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: PetscBool Clementcite = PETSC_FALSE;
10: const char ClementCitation[] = "@article{clement1975approximation,\n"
11: " title = {Approximation by finite element functions using local regularization},\n"
12: " author = {Philippe Cl{\\'e}ment},\n"
13: " journal = {Revue fran{\\c{c}}aise d'automatique, informatique, recherche op{\\'e}rationnelle. Analyse num{\\'e}rique},\n"
14: " volume = {9},\n"
15: " number = {R2},\n"
16: " pages = {77--84},\n"
17: " year = {1975}\n}\n";
19: static PetscErrorCode DMPlexConvertPlex(DM dm, DM *plex, PetscBool copy)
20: {
21: PetscBool isPlex;
23: PetscFunctionBegin;
24: PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
25: if (isPlex) {
26: *plex = dm;
27: PetscCall(PetscObjectReference((PetscObject)dm));
28: } else {
29: PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
30: if (!*plex) {
31: PetscCall(DMConvert(dm, DMPLEX, plex));
32: PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
33: } else {
34: PetscCall(PetscObjectReference((PetscObject)*plex));
35: }
36: if (copy) {
37: DMSubDomainHookLink link;
39: PetscCall(DMCopyDS(dm, PETSC_DETERMINE, PETSC_DETERMINE, *plex));
40: PetscCall(DMCopyAuxiliaryVec(dm, *plex));
41: /* Run the subdomain hook (this will copy the DMSNES/DMTS) */
42: for (link = dm->subdomainhook; link; link = link->next) {
43: if (link->ddhook) PetscCall((*link->ddhook)(dm, *plex, link->ctx));
44: }
45: }
46: }
47: PetscFunctionReturn(PETSC_SUCCESS);
48: }
50: static PetscErrorCode PetscContainerCtxDestroy_PetscFEGeom(PetscCtxRt ctx)
51: {
52: PetscFEGeom *geom = *(PetscFEGeom **)ctx;
54: PetscFunctionBegin;
55: PetscCall(PetscFEGeomDestroy(&geom));
56: PetscFunctionReturn(PETSC_SUCCESS);
57: }
59: static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
60: {
61: char composeStr[33] = {0};
62: PetscObjectId id;
63: PetscContainer container;
65: PetscFunctionBegin;
66: PetscCall(PetscObjectGetId((PetscObject)quad, &id));
67: PetscCall(PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%" PetscInt64_FMT "\n", id));
68: PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
69: if (container) {
70: PetscCall(PetscContainerGetPointer(container, geom));
71: } else {
72: PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, mode, geom));
73: PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
74: PetscCall(PetscContainerSetPointer(container, (void *)*geom));
75: PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
76: PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
77: PetscCall(PetscContainerDestroy(&container));
78: }
79: PetscFunctionReturn(PETSC_SUCCESS);
80: }
82: static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
83: {
84: PetscFunctionBegin;
85: *geom = NULL;
86: PetscFunctionReturn(PETSC_SUCCESS);
87: }
89: /*@
90: DMPlexGetScale - Get the scale for the specified fundamental unit
92: Not Collective
94: Input Parameters:
95: + dm - the `DM`
96: - unit - The SI unit
98: Output Parameter:
99: . scale - The value used to scale all quantities with this unit
101: Level: advanced
103: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetScale()`, `PetscUnit`
104: @*/
105: PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
106: {
107: DM_Plex *mesh = (DM_Plex *)dm->data;
109: PetscFunctionBegin;
111: PetscAssertPointer(scale, 3);
112: *scale = mesh->scale[unit];
113: PetscFunctionReturn(PETSC_SUCCESS);
114: }
116: /*@
117: DMPlexSetScale - Set the scale for the specified fundamental unit
119: Not Collective
121: Input Parameters:
122: + dm - the `DM`
123: . unit - The SI unit
124: - scale - The value used to scale all quantities with this unit
126: Level: advanced
128: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetScale()`, `PetscUnit`
129: @*/
130: PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
131: {
132: DM_Plex *mesh = (DM_Plex *)dm->data;
134: PetscFunctionBegin;
136: mesh->scale[unit] = scale;
137: PetscFunctionReturn(PETSC_SUCCESS);
138: }
140: PetscErrorCode DMPlexGetUseCeed_Plex(DM dm, PetscBool *useCeed)
141: {
142: DM_Plex *mesh = (DM_Plex *)dm->data;
144: PetscFunctionBegin;
145: *useCeed = mesh->useCeed;
146: PetscFunctionReturn(PETSC_SUCCESS);
147: }
148: PetscErrorCode DMPlexSetUseCeed_Plex(DM dm, PetscBool useCeed)
149: {
150: DM_Plex *mesh = (DM_Plex *)dm->data;
152: PetscFunctionBegin;
153: mesh->useCeed = useCeed;
154: PetscFunctionReturn(PETSC_SUCCESS);
155: }
157: /*@
158: DMPlexGetUseCeed - Get flag for using the LibCEED backend
160: Not collective
162: Input Parameter:
163: . dm - The `DM`
165: Output Parameter:
166: . useCeed - The flag
168: Level: intermediate
170: .seealso: `DMPlexSetUseCeed()`
171: @*/
172: PetscErrorCode DMPlexGetUseCeed(DM dm, PetscBool *useCeed)
173: {
174: PetscFunctionBegin;
176: PetscAssertPointer(useCeed, 2);
177: *useCeed = PETSC_FALSE;
178: PetscTryMethod(dm, "DMPlexGetUseCeed_C", (DM, PetscBool *), (dm, useCeed));
179: PetscFunctionReturn(PETSC_SUCCESS);
180: }
182: /*@
183: DMPlexSetUseCeed - Set flag for using the LibCEED backend
185: Not collective
187: Input Parameters:
188: + dm - The `DM`
189: - useCeed - The flag
191: Level: intermediate
193: .seealso: `DMPlexGetUseCeed()`
194: @*/
195: PetscErrorCode DMPlexSetUseCeed(DM dm, PetscBool useCeed)
196: {
197: PetscFunctionBegin;
200: PetscUseMethod(dm, "DMPlexSetUseCeed_C", (DM, PetscBool), (dm, useCeed));
201: PetscFunctionReturn(PETSC_SUCCESS);
202: }
204: /*@
205: DMPlexGetUseMatClosurePermutation - Get flag for using a closure permutation for matrix insertion
207: Not collective
209: Input Parameter:
210: . dm - The `DM`
212: Output Parameter:
213: . useClPerm - The flag
215: Level: intermediate
217: .seealso: `DMPlexSetUseMatClosurePermutation()`
218: @*/
219: PetscErrorCode DMPlexGetUseMatClosurePermutation(DM dm, PetscBool *useClPerm)
220: {
221: DM_Plex *mesh = (DM_Plex *)dm->data;
223: PetscFunctionBegin;
225: PetscAssertPointer(useClPerm, 2);
226: *useClPerm = mesh->useMatClPerm;
227: PetscFunctionReturn(PETSC_SUCCESS);
228: }
230: /*@
231: DMPlexSetUseMatClosurePermutation - Set flag for using a closure permutation for matrix insertion
233: Not collective
235: Input Parameters:
236: + dm - The `DM`
237: - useClPerm - The flag
239: Level: intermediate
241: .seealso: `DMPlexGetUseMatClosurePermutation()`
242: @*/
243: PetscErrorCode DMPlexSetUseMatClosurePermutation(DM dm, PetscBool useClPerm)
244: {
245: DM_Plex *mesh = (DM_Plex *)dm->data;
247: PetscFunctionBegin;
250: mesh->useMatClPerm = useClPerm;
251: PetscFunctionReturn(PETSC_SUCCESS);
252: }
254: static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, PetscCtx ctx)
255: {
256: const PetscInt eps[3][3][3] = {
257: {{0, 0, 0}, {0, 0, 1}, {0, -1, 0}},
258: {{0, 0, -1}, {0, 0, 0}, {1, 0, 0} },
259: {{0, 1, 0}, {-1, 0, 0}, {0, 0, 0} }
260: };
261: PetscInt *ctxInt = (PetscInt *)ctx;
262: PetscInt dim2 = ctxInt[0];
263: PetscInt d = ctxInt[1];
264: PetscInt i, j, k = dim > 2 ? d - dim : d;
266: PetscFunctionBegin;
267: PetscCheck(dim == dim2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %" PetscInt_FMT " does not match context dimension %" PetscInt_FMT, dim, dim2);
268: for (i = 0; i < dim; i++) mode[i] = 0.;
269: if (d < dim) {
270: mode[d] = 1.; /* Translation along axis d */
271: } else {
272: for (i = 0; i < dim; i++) {
273: for (j = 0; j < dim; j++) mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */
274: }
275: }
276: PetscFunctionReturn(PETSC_SUCCESS);
277: }
279: /*@
280: DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation
282: Collective
284: Input Parameters:
285: + dm - the `DM`
286: - field - The field number for the rigid body space, or 0 for the default
288: Output Parameter:
289: . sp - the null space
291: Level: advanced
293: Note:
294: This is necessary to provide a suitable coarse space for algebraic multigrid
296: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`, `PCGAMG`
297: @*/
298: PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
299: {
300: PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
301: MPI_Comm comm;
302: Vec mode[6];
303: PetscSection section, globalSection;
304: PetscInt dim, dimEmbed, Nf, n, m, mmin, d, i, j;
305: void **ctxs;
307: PetscFunctionBegin;
308: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
309: PetscCall(DMGetDimension(dm, &dim));
310: PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
311: PetscCall(DMGetNumFields(dm, &Nf));
312: PetscCheck(!Nf || !(field < 0 || field >= Nf), comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", field, Nf);
313: if (dim == 1 && Nf < 2) {
314: PetscCall(MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp));
315: PetscFunctionReturn(PETSC_SUCCESS);
316: }
317: PetscCall(DMGetLocalSection(dm, §ion));
318: PetscCall(DMGetGlobalSection(dm, &globalSection));
319: PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
320: PetscCall(PetscCalloc2(Nf, &func, Nf, &ctxs));
321: m = (dim * (dim + 1)) / 2;
322: PetscCall(VecCreate(comm, &mode[0]));
323: PetscCall(VecSetType(mode[0], dm->vectype));
324: PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
325: PetscCall(VecSetUp(mode[0]));
326: PetscCall(VecGetSize(mode[0], &n));
327: mmin = PetscMin(m, n);
328: func[field] = DMPlexProjectRigidBody_Private;
329: for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
330: for (d = 0; d < m; d++) {
331: PetscInt ctx[2];
333: ctxs[field] = (void *)(&ctx[0]);
334: ctx[0] = dimEmbed;
335: ctx[1] = d;
336: PetscCall(DMProjectFunction(dm, 0.0, func, ctxs, INSERT_VALUES, mode[d]));
337: }
338: /* Orthonormalize system */
339: for (i = 0; i < mmin; ++i) {
340: PetscScalar dots[6];
341: PetscReal norm;
343: PetscCall(VecNormalize(mode[i], &norm));
344: if (PetscAbsReal(norm) <= PETSC_SQRT_MACHINE_EPSILON) {
345: PetscCall(VecDestroy(&mode[i]));
346: if (i < mmin - 1) {
347: for (j = i; j < mmin - 1; j++) mode[j] = mode[j + 1];
348: mode[mmin - 1] = NULL;
349: }
350: m--;
351: mmin--;
352: i--;
353: continue;
354: }
355: PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1));
356: for (j = i + 1; j < mmin; ++j) {
357: dots[j] *= -1.0;
358: PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
359: }
360: }
361: PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp));
362: for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
363: PetscCall(PetscFree2(func, ctxs));
364: PetscFunctionReturn(PETSC_SUCCESS);
365: }
367: /*@
368: DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation
370: Collective
372: Input Parameters:
373: + dm - the `DM`
374: . nb - The number of bodies
375: . label - The `DMLabel` marking each domain
376: . nids - The number of ids per body
377: - ids - An array of the label ids in sequence for each domain
379: Output Parameter:
380: . sp - the null space
382: Level: advanced
384: Note:
385: This is necessary to provide a suitable coarse space for algebraic multigrid
387: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`
388: @*/
389: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
390: {
391: MPI_Comm comm;
392: PetscSection section, globalSection;
393: Vec *mode;
394: PetscScalar *dots;
395: PetscInt dim, dimEmbed, n, m, b, d, i, j, off;
397: PetscFunctionBegin;
398: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
399: PetscCall(DMGetDimension(dm, &dim));
400: PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
401: PetscCall(DMGetLocalSection(dm, §ion));
402: PetscCall(DMGetGlobalSection(dm, &globalSection));
403: PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
404: m = nb * (dim * (dim + 1)) / 2;
405: PetscCall(PetscMalloc2(m, &mode, m, &dots));
406: PetscCall(VecCreate(comm, &mode[0]));
407: PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
408: PetscCall(VecSetUp(mode[0]));
409: for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
410: for (b = 0, off = 0; b < nb; ++b) {
411: for (d = 0; d < m / nb; ++d) {
412: PetscInt ctx[2];
413: PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
414: void *voidctx = (void *)(&ctx[0]);
416: ctx[0] = dimEmbed;
417: ctx[1] = d;
418: PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]));
419: off += nids[b];
420: }
421: }
422: /* Orthonormalize system */
423: for (i = 0; i < m; ++i) {
424: PetscScalar dots[6];
426: PetscCall(VecNormalize(mode[i], NULL));
427: PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1));
428: for (j = i + 1; j < m; ++j) {
429: dots[j] *= -1.0;
430: PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
431: }
432: }
433: PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp));
434: for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
435: PetscCall(PetscFree2(mode, dots));
436: PetscFunctionReturn(PETSC_SUCCESS);
437: }
439: /*@
440: DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
441: are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
442: evaluating the dual space basis of that point.
444: Input Parameters:
445: + dm - the `DMPLEX` object
446: - height - the maximum projection height >= 0
448: Level: advanced
450: Notes:
451: A basis function is associated with the point in its transitively-closed support whose mesh
452: height is highest (w.r.t. DAG height), but not greater than the maximum projection height,
453: which is set with this function. By default, the maximum projection height is zero, which
454: means that only mesh cells are used to project basis functions. A height of one, for
455: example, evaluates a cell-interior basis functions using its cells dual space basis, but all
456: other basis functions with the dual space basis of a face.
458: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
459: @*/
460: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
461: {
462: DM_Plex *plex = (DM_Plex *)dm->data;
464: PetscFunctionBegin;
466: plex->maxProjectionHeight = height;
467: PetscFunctionReturn(PETSC_SUCCESS);
468: }
470: /*@
471: DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in
472: DMPlexProjectXXXLocal() functions.
474: Input Parameter:
475: . dm - the `DMPLEX` object
477: Output Parameter:
478: . height - the maximum projection height
480: Level: intermediate
482: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
483: @*/
484: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
485: {
486: DM_Plex *plex = (DM_Plex *)dm->data;
488: PetscFunctionBegin;
490: *height = plex->maxProjectionHeight;
491: PetscFunctionReturn(PETSC_SUCCESS);
492: }
494: typedef struct {
495: PetscReal alpha; /* The first Euler angle, and in 2D the only one */
496: PetscReal beta; /* The second Euler angle */
497: PetscReal gamma; /* The third Euler angle */
498: PetscInt dim; /* The dimension of R */
499: PetscScalar *R; /* The rotation matrix, transforming a vector in the local basis to the global basis */
500: PetscScalar *RT; /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
501: } RotCtx;
503: /*
504: Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
505: 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:
506: $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
507: $ 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.
508: $ The XYZ system rotates a third time about the z axis by gamma.
509: */
510: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, PetscCtx ctx)
511: {
512: RotCtx *rc = (RotCtx *)ctx;
513: PetscInt dim = rc->dim;
514: PetscReal c1, s1, c2, s2, c3, s3;
516: PetscFunctionBegin;
517: PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT));
518: switch (dim) {
519: case 2:
520: c1 = PetscCosReal(rc->alpha);
521: s1 = PetscSinReal(rc->alpha);
522: rc->R[0] = c1;
523: rc->R[1] = s1;
524: rc->R[2] = -s1;
525: rc->R[3] = c1;
526: PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
527: DMPlex_Transpose2D_Internal(rc->RT);
528: break;
529: case 3:
530: c1 = PetscCosReal(rc->alpha);
531: s1 = PetscSinReal(rc->alpha);
532: c2 = PetscCosReal(rc->beta);
533: s2 = PetscSinReal(rc->beta);
534: c3 = PetscCosReal(rc->gamma);
535: s3 = PetscSinReal(rc->gamma);
536: rc->R[0] = c1 * c3 - c2 * s1 * s3;
537: rc->R[1] = c3 * s1 + c1 * c2 * s3;
538: rc->R[2] = s2 * s3;
539: rc->R[3] = -c1 * s3 - c2 * c3 * s1;
540: rc->R[4] = c1 * c2 * c3 - s1 * s3;
541: rc->R[5] = c3 * s2;
542: rc->R[6] = s1 * s2;
543: rc->R[7] = -c1 * s2;
544: rc->R[8] = c2;
545: PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
546: DMPlex_Transpose3D_Internal(rc->RT);
547: break;
548: default:
549: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
550: }
551: PetscFunctionReturn(PETSC_SUCCESS);
552: }
554: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, PetscCtx ctx)
555: {
556: RotCtx *rc = (RotCtx *)ctx;
558: PetscFunctionBegin;
559: PetscCall(PetscFree2(rc->R, rc->RT));
560: PetscCall(PetscFree(rc));
561: PetscFunctionReturn(PETSC_SUCCESS);
562: }
564: static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, PetscCtx ctx)
565: {
566: RotCtx *rc = (RotCtx *)ctx;
568: PetscFunctionBeginHot;
569: PetscAssertPointer(ctx, 5);
570: if (l2g) {
571: *A = rc->R;
572: } else {
573: *A = rc->RT;
574: }
575: PetscFunctionReturn(PETSC_SUCCESS);
576: }
578: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, PetscCtx ctx)
579: {
580: PetscFunctionBegin;
581: #if defined(PETSC_USE_COMPLEX)
582: switch (dim) {
583: case 2: {
584: PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};
586: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
587: z[0] = PetscRealPart(zt[0]);
588: z[1] = PetscRealPart(zt[1]);
589: } break;
590: case 3: {
591: PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};
593: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
594: z[0] = PetscRealPart(zt[0]);
595: z[1] = PetscRealPart(zt[1]);
596: z[2] = PetscRealPart(zt[2]);
597: } break;
598: }
599: #else
600: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx));
601: #endif
602: PetscFunctionReturn(PETSC_SUCCESS);
603: }
605: PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, PetscCtx ctx)
606: {
607: const PetscScalar *A;
609: PetscFunctionBeginHot;
610: PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx));
611: switch (dim) {
612: case 2:
613: DMPlex_Mult2D_Internal(A, 1, y, z);
614: break;
615: case 3:
616: DMPlex_Mult3D_Internal(A, 1, y, z);
617: break;
618: }
619: PetscFunctionReturn(PETSC_SUCCESS);
620: }
622: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
623: {
624: PetscSection ts;
625: const PetscScalar *ta, *tva;
626: PetscInt dof;
628: PetscFunctionBeginHot;
629: PetscCall(DMGetLocalSection(tdm, &ts));
630: PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
631: PetscCall(VecGetArrayRead(tv, &ta));
632: PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva));
633: if (l2g) {
634: switch (dof) {
635: case 4:
636: DMPlex_Mult2D_Internal(tva, 1, a, a);
637: break;
638: case 9:
639: DMPlex_Mult3D_Internal(tva, 1, a, a);
640: break;
641: }
642: } else {
643: switch (dof) {
644: case 4:
645: DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
646: break;
647: case 9:
648: DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
649: break;
650: }
651: }
652: PetscCall(VecRestoreArrayRead(tv, &ta));
653: PetscFunctionReturn(PETSC_SUCCESS);
654: }
656: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
657: {
658: PetscSection s, ts;
659: const PetscScalar *ta, *tvaf, *tvag;
660: PetscInt fdof, gdof, fpdof, gpdof;
662: PetscFunctionBeginHot;
663: PetscCall(DMGetLocalSection(dm, &s));
664: PetscCall(DMGetLocalSection(tdm, &ts));
665: PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof));
666: PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof));
667: PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof));
668: PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof));
669: PetscCall(VecGetArrayRead(tv, &ta));
670: PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf));
671: PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag));
672: if (l2g) {
673: switch (fdof) {
674: case 4:
675: DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
676: break;
677: case 9:
678: DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
679: break;
680: }
681: switch (gdof) {
682: case 4:
683: DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
684: break;
685: case 9:
686: DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
687: break;
688: }
689: } else {
690: switch (fdof) {
691: case 4:
692: DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
693: break;
694: case 9:
695: DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
696: break;
697: }
698: switch (gdof) {
699: case 4:
700: DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
701: break;
702: case 9:
703: DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
704: break;
705: }
706: }
707: PetscCall(VecRestoreArrayRead(tv, &ta));
708: PetscFunctionReturn(PETSC_SUCCESS);
709: }
711: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
712: {
713: PetscSection s;
714: PetscSection clSection;
715: IS clPoints;
716: const PetscInt *clp;
717: PetscInt *points = NULL;
718: PetscInt Nf, f, Np, cp, dof, d = 0;
720: PetscFunctionBegin;
721: PetscCall(DMGetLocalSection(dm, &s));
722: PetscCall(PetscSectionGetNumFields(s, &Nf));
723: PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
724: for (f = 0; f < Nf; ++f) {
725: for (cp = 0; cp < Np * 2; cp += 2) {
726: PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof));
727: if (!dof) continue;
728: if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]));
729: d += dof;
730: }
731: }
732: PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
733: PetscFunctionReturn(PETSC_SUCCESS);
734: }
736: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
737: {
738: PetscSection s;
739: PetscSection clSection;
740: IS clPoints;
741: const PetscInt *clp;
742: PetscInt *points = NULL;
743: PetscInt Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;
745: PetscFunctionBegin;
746: PetscCall(DMGetLocalSection(dm, &s));
747: PetscCall(PetscSectionGetNumFields(s, &Nf));
748: PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
749: for (f = 0, r = 0; f < Nf; ++f) {
750: for (cpf = 0; cpf < Np * 2; cpf += 2) {
751: PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof));
752: for (g = 0, c = 0; g < Nf; ++g) {
753: for (cpg = 0; cpg < Np * 2; cpg += 2) {
754: PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof));
755: PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]));
756: c += gdof;
757: }
758: }
759: PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
760: r += fdof;
761: }
762: }
763: PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
764: PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
765: PetscFunctionReturn(PETSC_SUCCESS);
766: }
768: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
769: {
770: DM tdm;
771: Vec tv;
772: PetscSection ts, s;
773: const PetscScalar *ta;
774: PetscScalar *a, *va;
775: PetscInt pStart, pEnd, p, Nf, f;
777: PetscFunctionBegin;
778: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
779: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
780: PetscCall(DMGetLocalSection(tdm, &ts));
781: PetscCall(DMGetLocalSection(dm, &s));
782: PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
783: PetscCall(PetscSectionGetNumFields(s, &Nf));
784: PetscCall(VecGetArray(lv, &a));
785: PetscCall(VecGetArrayRead(tv, &ta));
786: for (p = pStart; p < pEnd; ++p) {
787: for (f = 0; f < Nf; ++f) {
788: PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va));
789: PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va));
790: }
791: }
792: PetscCall(VecRestoreArray(lv, &a));
793: PetscCall(VecRestoreArrayRead(tv, &ta));
794: PetscFunctionReturn(PETSC_SUCCESS);
795: }
797: /*@
798: DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis
800: Input Parameters:
801: + dm - The `DM`
802: - lv - A local vector with values in the global basis
804: Output Parameter:
805: . lv - A local vector with values in the local basis
807: Level: developer
809: Note:
810: 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.
812: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
813: @*/
814: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
815: {
816: PetscFunctionBegin;
819: PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE));
820: PetscFunctionReturn(PETSC_SUCCESS);
821: }
823: /*@
824: DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis
826: Input Parameters:
827: + dm - The `DM`
828: - lv - A local vector with values in the local basis
830: Output Parameter:
831: . lv - A local vector with values in the global basis
833: Level: developer
835: Note:
836: 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.
838: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
839: @*/
840: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
841: {
842: PetscFunctionBegin;
845: PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE));
846: PetscFunctionReturn(PETSC_SUCCESS);
847: }
849: /*@
850: DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions
851: and global solutions, to a local basis, appropriate for discretization integrals and assembly.
853: Input Parameters:
854: + dm - The `DM`
855: . alpha - The first Euler angle, and in 2D the only one
856: . beta - The second Euler angle
857: - gamma - The third Euler angle
859: Level: developer
861: Note:
862: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
863: 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
864: .vb
865: The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
866: 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.
867: The XYZ system rotates a third time about the z axis by gamma.
868: .ve
870: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
871: @*/
872: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
873: {
874: RotCtx *rc;
875: PetscInt cdim;
877: PetscFunctionBegin;
878: PetscCall(DMGetCoordinateDim(dm, &cdim));
879: PetscCall(PetscMalloc1(1, &rc));
880: dm->transformCtx = rc;
881: dm->transformSetUp = DMPlexBasisTransformSetUp_Rotation_Internal;
882: dm->transformDestroy = DMPlexBasisTransformDestroy_Rotation_Internal;
883: dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
884: rc->dim = cdim;
885: rc->alpha = alpha;
886: rc->beta = beta;
887: rc->gamma = gamma;
888: PetscCall((*dm->transformSetUp)(dm, dm->transformCtx));
889: PetscCall(DMConstructBasisTransform_Internal(dm));
890: PetscFunctionReturn(PETSC_SUCCESS);
891: }
893: /*@C
894: DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector using a function of the coordinates
896: Input Parameters:
897: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
898: . time - The time
899: . field - The field to constrain
900: . Nc - The number of constrained field components, or 0 for all components
901: . comps - An array of constrained component numbers, or `NULL` for all components
902: . label - The `DMLabel` defining constrained points
903: . numids - The number of `DMLabel` ids for constrained points
904: . ids - An array of ids for constrained points
905: . func - A pointwise function giving boundary values
906: - ctx - An optional application context for `bcFunc`
908: Output Parameter:
909: . locX - A local vector to receives the boundary values
911: Level: developer
913: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
914: @*/
915: 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 *), PetscCtx ctx, Vec locX)
916: {
917: PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, PetscCtx ctx);
918: void **ctxs;
919: PetscInt numFields;
921: PetscFunctionBegin;
922: PetscCall(DMGetNumFields(dm, &numFields));
923: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
924: funcs[field] = func;
925: ctxs[field] = ctx;
926: PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX));
927: PetscCall(PetscFree2(funcs, ctxs));
928: PetscFunctionReturn(PETSC_SUCCESS);
929: }
931: /*@C
932: DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector using a function of the coordinates and field data
934: Input Parameters:
935: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
936: . time - The time
937: . locU - A local vector with the input solution values
938: . field - The field to constrain
939: . Nc - The number of constrained field components, or 0 for all components
940: . comps - An array of constrained component numbers, or `NULL` for all components
941: . label - The `DMLabel` defining constrained points
942: . numids - The number of `DMLabel` ids for constrained points
943: . ids - An array of ids for constrained points
944: . func - A pointwise function giving boundary values
945: - ctx - An optional application context for `bcFunc`
947: Output Parameter:
948: . locX - A local vector to receives the boundary values
950: Level: developer
952: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
953: @*/
954: PetscErrorCode DMPlexInsertBoundaryValuesEssentialField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscCtx ctx, Vec locX)
955: {
956: void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
957: void **ctxs;
958: PetscInt numFields;
960: PetscFunctionBegin;
961: PetscCall(DMGetNumFields(dm, &numFields));
962: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
963: funcs[field] = func;
964: ctxs[field] = ctx;
965: PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
966: PetscCall(PetscFree2(funcs, ctxs));
967: PetscFunctionReturn(PETSC_SUCCESS);
968: }
970: /*@C
971: DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coordinates and boundary field data
973: Collective
975: Input Parameters:
976: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
977: . time - The time
978: . locU - A local vector with the input solution values
979: . field - The field to constrain
980: . Nc - The number of constrained field components, or 0 for all components
981: . comps - An array of constrained component numbers, or `NULL` for all components
982: . label - The `DMLabel` defining constrained points
983: . numids - The number of `DMLabel` ids for constrained points
984: . ids - An array of ids for constrained points
985: . func - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()`
986: - ctx - An optional application context for `func`
988: Output Parameter:
989: . locX - A local vector to receive the boundary values
991: Level: developer
993: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
994: @*/
995: PetscErrorCode DMPlexInsertBoundaryValuesEssentialBdField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscCtx ctx, Vec locX)
996: {
997: void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
998: void **ctxs;
999: PetscInt numFields;
1001: PetscFunctionBegin;
1002: PetscCall(DMGetNumFields(dm, &numFields));
1003: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
1004: funcs[field] = func;
1005: ctxs[field] = ctx;
1006: PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
1007: PetscCall(PetscFree2(funcs, ctxs));
1008: PetscFunctionReturn(PETSC_SUCCESS);
1009: }
1011: /*@C
1012: DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector
1014: Input Parameters:
1015: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
1016: . time - The time
1017: . faceGeometry - A vector with the FVM face geometry information
1018: . cellGeometry - A vector with the FVM cell geometry information
1019: . Grad - A vector with the FVM cell gradient information
1020: . field - The field to constrain
1021: . Nc - The number of constrained field components, or 0 for all components
1022: . comps - An array of constrained component numbers, or `NULL` for all components
1023: . label - The `DMLabel` defining constrained points
1024: . numids - The number of `DMLabel` ids for constrained points
1025: . ids - An array of ids for constrained points
1026: . func - A pointwise function giving boundary values
1027: - ctx - An optional application context for bcFunc
1029: Output Parameter:
1030: . locX - A local vector to receives the boundary values
1032: Level: developer
1034: Note:
1035: This implementation currently ignores the numcomps/comps argument from `DMAddBoundary()`
1037: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
1038: @*/
1039: 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[], PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *), PetscCtx ctx, Vec locX)
1040: {
1041: PetscDS prob;
1042: PetscSF sf;
1043: DM dmFace, dmCell, dmGrad;
1044: const PetscScalar *facegeom, *cellgeom = NULL, *grad;
1045: const PetscInt *leaves;
1046: PetscScalar *x, *fx;
1047: PetscInt dim, nleaves, loc, fStart, fEnd, pdim, i;
1048: PetscErrorCode ierru = PETSC_SUCCESS;
1050: PetscFunctionBegin;
1051: PetscCall(DMGetPointSF(dm, &sf));
1052: PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
1053: nleaves = PetscMax(0, nleaves);
1054: PetscCall(DMGetDimension(dm, &dim));
1055: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1056: PetscCall(DMGetDS(dm, &prob));
1057: PetscCall(VecGetDM(faceGeometry, &dmFace));
1058: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
1059: if (cellGeometry) {
1060: PetscCall(VecGetDM(cellGeometry, &dmCell));
1061: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
1062: }
1063: if (Grad) {
1064: PetscFV fv;
1066: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv));
1067: PetscCall(VecGetDM(Grad, &dmGrad));
1068: PetscCall(VecGetArrayRead(Grad, &grad));
1069: PetscCall(PetscFVGetNumComponents(fv, &pdim));
1070: PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx));
1071: }
1072: PetscCall(VecGetArray(locX, &x));
1073: for (i = 0; i < numids; ++i) {
1074: IS faceIS;
1075: const PetscInt *faces;
1076: PetscInt numFaces, f;
1078: PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS));
1079: if (!faceIS) continue; /* No points with that id on this process */
1080: PetscCall(ISGetLocalSize(faceIS, &numFaces));
1081: PetscCall(ISGetIndices(faceIS, &faces));
1082: for (f = 0; f < numFaces; ++f) {
1083: const PetscInt face = faces[f], *cells;
1084: PetscFVFaceGeom *fg;
1086: if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
1087: PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc));
1088: if (loc >= 0) continue;
1089: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
1090: PetscCall(DMPlexGetSupport(dm, face, &cells));
1091: if (Grad) {
1092: PetscFVCellGeom *cg;
1093: PetscScalar *cx, *cgrad;
1094: PetscScalar *xG;
1095: PetscReal dx[3];
1096: PetscInt d;
1098: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
1099: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
1100: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
1101: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
1102: DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
1103: for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
1104: PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
1105: } else {
1106: PetscScalar *xI;
1107: PetscScalar *xG;
1109: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
1110: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
1111: ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
1112: if (ierru) {
1113: PetscCall(ISRestoreIndices(faceIS, &faces));
1114: PetscCall(ISDestroy(&faceIS));
1115: goto cleanup;
1116: }
1117: }
1118: }
1119: PetscCall(ISRestoreIndices(faceIS, &faces));
1120: PetscCall(ISDestroy(&faceIS));
1121: }
1122: cleanup:
1123: PetscCall(VecRestoreArray(locX, &x));
1124: if (Grad) {
1125: PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
1126: PetscCall(VecRestoreArrayRead(Grad, &grad));
1127: }
1128: if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
1129: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
1130: PetscCall(ierru);
1131: PetscFunctionReturn(PETSC_SUCCESS);
1132: }
1134: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx)
1135: {
1136: PetscInt c;
1137: for (c = 0; c < Nc; ++c) u[c] = 0.0;
1138: return PETSC_SUCCESS;
1139: }
1141: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1142: {
1143: PetscObject isZero;
1144: PetscDS prob;
1145: PetscInt numBd, b;
1147: PetscFunctionBegin;
1148: PetscCall(DMGetDS(dm, &prob));
1149: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1150: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1151: PetscCall(PetscDSUpdateBoundaryLabels(prob, dm));
1152: for (b = 0; b < numBd; ++b) {
1153: PetscWeakForm wf;
1154: DMBoundaryConditionType type;
1155: const char *name;
1156: DMLabel label;
1157: PetscInt field, Nc;
1158: const PetscInt *comps;
1159: PetscObject obj;
1160: PetscClassId id;
1161: PetscVoidFn *bvfunc;
1162: PetscInt numids;
1163: const PetscInt *ids;
1164: void *ctx;
1166: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1167: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1168: PetscCall(DMGetField(dm, field, NULL, &obj));
1169: PetscCall(PetscObjectGetClassId(obj, &id));
1170: if (id == PETSCFE_CLASSID) {
1171: switch (type) {
1172: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1173: case DM_BC_ESSENTIAL: {
1174: PetscSimplePointFn *func = (PetscSimplePointFn *)bvfunc;
1176: if (isZero) func = zero;
1177: PetscCall(DMPlexLabelAddCells(dm, label));
1178: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
1179: PetscCall(DMPlexLabelClearCells(dm, label));
1180: } break;
1181: case DM_BC_ESSENTIAL_FIELD: {
1182: PetscPointFn *func = (PetscPointFn *)bvfunc;
1184: PetscCall(DMPlexLabelAddCells(dm, label));
1185: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
1186: PetscCall(DMPlexLabelClearCells(dm, label));
1187: } break;
1188: default:
1189: break;
1190: }
1191: } else if (id == PETSCFV_CLASSID) {
1192: {
1193: PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode (*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;
1195: if (!faceGeomFVM) continue;
1196: PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
1197: }
1198: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1199: }
1200: PetscFunctionReturn(PETSC_SUCCESS);
1201: }
1203: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1204: {
1205: PetscObject isZero;
1206: PetscDS prob;
1207: PetscInt numBd, b;
1209: PetscFunctionBegin;
1210: if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
1211: PetscCall(DMGetDS(dm, &prob));
1212: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1213: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1214: for (b = 0; b < numBd; ++b) {
1215: PetscWeakForm wf;
1216: DMBoundaryConditionType type;
1217: const char *name;
1218: DMLabel label;
1219: PetscInt field, Nc;
1220: const PetscInt *comps;
1221: PetscObject obj;
1222: PetscClassId id;
1223: PetscInt numids;
1224: const PetscInt *ids;
1225: PetscVoidFn *bvfunc;
1226: void *ctx;
1228: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
1229: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1230: PetscCall(DMGetField(dm, field, NULL, &obj));
1231: PetscCall(PetscObjectGetClassId(obj, &id));
1232: if (id == PETSCFE_CLASSID) {
1233: switch (type) {
1234: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1235: case DM_BC_ESSENTIAL: {
1236: PetscSimplePointFn *func_t = (PetscSimplePointFn *)bvfunc;
1238: if (isZero) func_t = zero;
1239: PetscCall(DMPlexLabelAddCells(dm, label));
1240: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1241: PetscCall(DMPlexLabelClearCells(dm, label));
1242: } break;
1243: case DM_BC_ESSENTIAL_FIELD: {
1244: PetscPointFn *func_t = (PetscPointFn *)bvfunc;
1246: PetscCall(DMPlexLabelAddCells(dm, label));
1247: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1248: PetscCall(DMPlexLabelClearCells(dm, label));
1249: } break;
1250: default:
1251: break;
1252: }
1253: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1254: }
1255: PetscFunctionReturn(PETSC_SUCCESS);
1256: }
1258: PetscErrorCode DMPlexInsertBounds_Plex(DM dm, PetscBool lower, PetscReal time, Vec locB)
1259: {
1260: PetscDS ds;
1261: PetscInt numBd;
1263: PetscFunctionBegin;
1264: PetscCall(DMGetDS(dm, &ds));
1265: PetscCall(PetscDSGetNumBoundary(ds, &numBd));
1266: PetscCall(PetscDSUpdateBoundaryLabels(ds, dm));
1267: for (PetscInt b = 0; b < numBd; ++b) {
1268: PetscWeakForm wf;
1269: DMBoundaryConditionType type;
1270: const char *name;
1271: DMLabel label;
1272: PetscInt numids;
1273: const PetscInt *ids;
1274: PetscInt field, Nc;
1275: const PetscInt *comps;
1276: PetscVoidFn *bvfunc;
1277: void *ctx;
1279: PetscCall(PetscDSGetBoundary(ds, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1280: if (lower && type != DM_BC_LOWER_BOUND) continue;
1281: if (!lower && type != DM_BC_UPPER_BOUND) continue;
1282: PetscCall(DMPlexLabelAddCells(dm, label));
1283: {
1284: PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, PetscCtx ctx);
1285: void **ctxs;
1286: PetscInt Nf;
1288: PetscCall(DMGetNumFields(dm, &Nf));
1289: PetscCall(PetscCalloc2(Nf, &funcs, Nf, &ctxs));
1290: funcs[field] = (PetscSimplePointFn *)bvfunc;
1291: ctxs[field] = ctx;
1292: PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_ALL_VALUES, locB));
1293: PetscCall(PetscFree2(funcs, ctxs));
1294: }
1295: PetscCall(DMPlexLabelClearCells(dm, label));
1296: }
1297: PetscFunctionReturn(PETSC_SUCCESS);
1298: }
1300: /*@
1301: DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector
1303: Not Collective
1305: Input Parameters:
1306: + dm - The `DM`
1307: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1308: . time - The time
1309: . faceGeomFVM - Face geometry data for FV discretizations
1310: . cellGeomFVM - Cell geometry data for FV discretizations
1311: - gradFVM - Gradient reconstruction data for FV discretizations
1313: Output Parameter:
1314: . locX - Solution updated with boundary values
1316: Level: intermediate
1318: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1319: @*/
1320: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1321: {
1322: PetscFunctionBegin;
1328: PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1329: PetscFunctionReturn(PETSC_SUCCESS);
1330: }
1332: /*@
1333: DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector
1335: Input Parameters:
1336: + dm - The `DM`
1337: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1338: . time - The time
1339: . faceGeomFVM - Face geometry data for FV discretizations
1340: . cellGeomFVM - Cell geometry data for FV discretizations
1341: - gradFVM - Gradient reconstruction data for FV discretizations
1343: Output Parameter:
1344: . locX_t - Solution updated with boundary values
1346: Level: developer
1348: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1349: @*/
1350: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1351: {
1352: PetscFunctionBegin;
1358: PetscTryMethod(dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1359: PetscFunctionReturn(PETSC_SUCCESS);
1360: }
1362: /*@
1363: DMPlexInsertBounds - Puts coefficients which represent solution bounds into the local bounds vector
1365: Not Collective
1367: Input Parameters:
1368: + dm - The `DM`
1369: . lower - If `PETSC_TRUE` use `DM_BC_LOWER_BOUND` conditions, otherwise use `DM_BC_UPPER_BOUND`
1370: - time - The time
1372: Output Parameter:
1373: . locB - Bounds vector updated with new bounds
1375: Level: intermediate
1377: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `PetscDSAddBoundary()`
1378: @*/
1379: PetscErrorCode DMPlexInsertBounds(DM dm, PetscBool lower, PetscReal time, Vec locB)
1380: {
1381: PetscFunctionBegin;
1384: PetscTryMethod(dm, "DMPlexInsertBounds_C", (DM, PetscBool, PetscReal, Vec), (dm, lower, time, locB));
1385: PetscFunctionReturn(PETSC_SUCCESS);
1386: }
1388: /*@
1389: DMPlexInsertBoundaryValuesFVM - Reconstruct cell gradients and insert non-essential (e.g. outflow) boundary values
1390: into a local finite-volume solution vector.
1392: Collective
1394: Input Parameters:
1395: + dm - the `DMPLEX`
1396: . fv - the `PetscFV` discretization
1397: . locX - the local solution vector; updated with non-essential boundary values
1398: - time - the current time
1400: Output Parameter:
1401: . locGradient - if non-`NULL`, the local vector holding the reconstructed cell gradients
1403: Level: developer
1405: Note:
1406: The caller receives ownership of `*locGradient` via `DMGetLocalVector()` and must return it with
1407: `DMRestoreLocalVector()`.
1409: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscFV`, `DMPlexInsertBoundaryValues()`, `DMPlexReconstructGradientsFVM()`
1410: @*/
1411: PetscErrorCode DMPlexInsertBoundaryValuesFVM(DM dm, PetscFV fv, Vec locX, PetscReal time, Vec *locGradient)
1412: {
1413: DM dmGrad;
1414: Vec cellGeometryFVM, faceGeometryFVM, locGrad = NULL;
1416: PetscFunctionBegin;
1420: if (locGradient) {
1421: PetscAssertPointer(locGradient, 5);
1422: *locGradient = NULL;
1423: }
1424: PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
1425: /* Reconstruct and limit cell gradients */
1426: PetscCall(DMPlexGetGradientDM(dm, fv, &dmGrad));
1427: if (dmGrad) {
1428: Vec grad;
1429: PetscInt fStart, fEnd;
1431: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1432: PetscCall(DMGetGlobalVector(dmGrad, &grad));
1433: PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
1434: /* Communicate gradient values */
1435: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
1436: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
1437: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
1438: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
1439: }
1440: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
1441: if (locGradient) *locGradient = locGrad;
1442: else if (locGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
1443: PetscFunctionReturn(PETSC_SUCCESS);
1444: }
1446: PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1447: {
1448: Vec localX;
1450: PetscFunctionBegin;
1451: PetscCall(DMGetLocalVector(dm, &localX));
1452: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1453: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1454: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1455: PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1456: PetscCall(DMRestoreLocalVector(dm, &localX));
1457: PetscFunctionReturn(PETSC_SUCCESS);
1458: }
1460: /*@C
1461: DMPlexComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
1463: Collective
1465: Input Parameters:
1466: + dm - The `DM`
1467: . time - The time
1468: . funcs - The functions to evaluate for each field component
1469: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1470: - localX - The coefficient vector u_h, a local vector
1472: Output Parameter:
1473: . diff - The diff ||u - u_h||_2
1475: Level: developer
1477: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1478: @*/
1479: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1480: {
1481: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1482: DM tdm;
1483: Vec tv;
1484: PetscSection section;
1485: PetscQuadrature quad;
1486: PetscFEGeom fegeom;
1487: PetscScalar *funcVal, *interpolant;
1488: PetscReal *coords, *gcoords;
1489: PetscReal localDiff = 0.0;
1490: const PetscReal *quadWeights;
1491: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1492: PetscBool transform;
1494: PetscFunctionBegin;
1495: PetscCall(DMGetDimension(dm, &dim));
1496: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1497: fegeom.dimEmbed = coordDim;
1498: PetscCall(DMGetLocalSection(dm, §ion));
1499: PetscCall(PetscSectionGetNumFields(section, &numFields));
1500: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1501: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1502: PetscCall(DMHasBasisTransform(dm, &transform));
1503: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1504: for (field = 0; field < numFields; ++field) {
1505: PetscObject obj;
1506: PetscClassId id;
1507: PetscInt Nc;
1509: PetscCall(DMGetField(dm, field, NULL, &obj));
1510: PetscCall(PetscObjectGetClassId(obj, &id));
1511: if (id == PETSCFE_CLASSID) {
1512: PetscFE fe = (PetscFE)obj;
1514: PetscCall(PetscFEGetQuadrature(fe, &quad));
1515: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1516: } else if (id == PETSCFV_CLASSID) {
1517: PetscFV fv = (PetscFV)obj;
1519: PetscCall(PetscFVGetQuadrature(fv, &quad));
1520: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1521: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1522: numComponents += Nc;
1523: }
1524: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1525: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1526: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1527: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1528: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1529: for (c = cStart; c < cEnd; ++c) {
1530: PetscScalar *x = NULL;
1531: PetscReal elemDiff = 0.0;
1532: PetscInt qc = 0;
1534: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1535: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1537: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1538: PetscObject obj;
1539: PetscClassId id;
1540: void *const ctx = ctxs ? ctxs[field] : NULL;
1541: PetscInt Nb, Nc, q, fc;
1543: PetscCall(DMGetField(dm, field, NULL, &obj));
1544: PetscCall(PetscObjectGetClassId(obj, &id));
1545: if (id == PETSCFE_CLASSID) {
1546: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1547: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1548: } else if (id == PETSCFV_CLASSID) {
1549: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1550: Nb = 1;
1551: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1552: if (debug) {
1553: char title[1024];
1554: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1555: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1556: }
1557: for (q = 0; q < Nq; ++q) {
1558: PetscFEGeom qgeom;
1559: PetscErrorCode ierr;
1561: qgeom.dimEmbed = fegeom.dimEmbed;
1562: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1563: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1564: qgeom.detJ = &fegeom.detJ[q];
1565: PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", point %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
1566: if (transform) {
1567: gcoords = &coords[coordDim * Nq];
1568: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1569: } else {
1570: gcoords = &coords[coordDim * q];
1571: }
1572: PetscCall(PetscArrayzero(funcVal, Nc));
1573: ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1574: if (ierr) {
1575: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1576: PetscCall(DMRestoreLocalVector(dm, &localX));
1577: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1578: }
1579: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1580: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1581: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1582: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1583: for (fc = 0; fc < Nc; ++fc) {
1584: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1585: if (debug)
1586: PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " field %" PetscInt_FMT ",%" PetscInt_FMT " point %g %g %g diff %g (%g, %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),
1587: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1588: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1589: }
1590: }
1591: fieldOffset += Nb;
1592: qc += Nc;
1593: }
1594: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1595: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1596: localDiff += elemDiff;
1597: }
1598: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1599: PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1600: *diff = PetscSqrtReal(*diff);
1601: PetscFunctionReturn(PETSC_SUCCESS);
1602: }
1604: 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)
1605: {
1606: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1607: DM tdm;
1608: PetscSection section;
1609: PetscQuadrature quad;
1610: Vec localX, tv;
1611: PetscScalar *funcVal, *interpolant;
1612: const PetscReal *quadWeights;
1613: PetscFEGeom fegeom;
1614: PetscReal *coords, *gcoords;
1615: PetscReal localDiff = 0.0;
1616: PetscInt dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1617: PetscBool transform;
1619: PetscFunctionBegin;
1620: PetscCall(DMGetDimension(dm, &dim));
1621: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1622: fegeom.dimEmbed = coordDim;
1623: PetscCall(DMGetLocalSection(dm, §ion));
1624: PetscCall(PetscSectionGetNumFields(section, &numFields));
1625: PetscCall(DMGetLocalVector(dm, &localX));
1626: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1627: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1628: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1629: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1630: PetscCall(DMHasBasisTransform(dm, &transform));
1631: for (field = 0; field < numFields; ++field) {
1632: PetscFE fe;
1633: PetscInt Nc;
1635: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1636: PetscCall(PetscFEGetQuadrature(fe, &quad));
1637: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1638: numComponents += Nc;
1639: }
1640: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1641: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1642: /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1643: PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1644: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1645: for (c = cStart; c < cEnd; ++c) {
1646: PetscScalar *x = NULL;
1647: PetscReal elemDiff = 0.0;
1648: PetscInt qc = 0;
1650: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1651: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1653: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1654: PetscFE fe;
1655: void *const ctx = ctxs ? ctxs[field] : NULL;
1656: PetscInt Nb, Nc, q, fc;
1658: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1659: PetscCall(PetscFEGetDimension(fe, &Nb));
1660: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1661: if (debug) {
1662: char title[1024];
1663: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1664: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1665: }
1666: for (q = 0; q < Nq; ++q) {
1667: PetscFEGeom qgeom;
1668: PetscErrorCode ierr;
1670: qgeom.dimEmbed = fegeom.dimEmbed;
1671: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1672: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1673: qgeom.detJ = &fegeom.detJ[q];
1674: PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
1675: if (transform) {
1676: gcoords = &coords[coordDim * Nq];
1677: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1678: } else {
1679: gcoords = &coords[coordDim * q];
1680: }
1681: PetscCall(PetscArrayzero(funcVal, Nc));
1682: ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1683: if (ierr) {
1684: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1685: PetscCall(DMRestoreLocalVector(dm, &localX));
1686: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1687: }
1688: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1689: PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1690: /* Overwrite with the dot product if the normal is given */
1691: if (n) {
1692: for (fc = 0; fc < Nc; ++fc) {
1693: PetscScalar sum = 0.0;
1694: PetscInt d;
1695: for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1696: interpolant[fc] = sum;
1697: }
1698: }
1699: for (fc = 0; fc < Nc; ++fc) {
1700: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1701: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " fieldDer %" PetscInt_FMT ",%" PetscInt_FMT " diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1702: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1703: }
1704: }
1705: fieldOffset += Nb;
1706: qc += Nc;
1707: }
1708: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1709: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1710: localDiff += elemDiff;
1711: }
1712: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1713: PetscCall(DMRestoreLocalVector(dm, &localX));
1714: PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1715: *diff = PetscSqrtReal(*diff);
1716: PetscFunctionReturn(PETSC_SUCCESS);
1717: }
1719: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1720: {
1721: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1722: DM tdm;
1723: DMLabel depthLabel;
1724: PetscSection section;
1725: Vec localX, tv;
1726: PetscReal *localDiff;
1727: PetscInt dim, depth, dE, Nf, f, Nds, s;
1728: PetscBool transform;
1730: PetscFunctionBegin;
1731: PetscCall(DMGetDimension(dm, &dim));
1732: PetscCall(DMGetCoordinateDim(dm, &dE));
1733: PetscCall(DMGetLocalSection(dm, §ion));
1734: PetscCall(DMGetLocalVector(dm, &localX));
1735: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1736: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1737: PetscCall(DMHasBasisTransform(dm, &transform));
1738: PetscCall(DMGetNumFields(dm, &Nf));
1739: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1740: PetscCall(DMLabelGetNumValues(depthLabel, &depth));
1742: PetscCall(VecSet(localX, 0.0));
1743: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1744: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1745: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1746: PetscCall(DMGetNumDS(dm, &Nds));
1747: PetscCall(PetscCalloc1(Nf, &localDiff));
1748: for (s = 0; s < Nds; ++s) {
1749: PetscDS ds;
1750: DMLabel label;
1751: IS fieldIS, pointIS;
1752: const PetscInt *fields, *points = NULL;
1753: PetscQuadrature quad;
1754: const PetscReal *quadPoints, *quadWeights;
1755: PetscFEGeom fegeom;
1756: PetscReal *coords, *gcoords;
1757: PetscScalar *funcVal, *interpolant;
1758: PetscBool isCohesive;
1759: PetscInt qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;
1761: PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1762: PetscCall(ISGetIndices(fieldIS, &fields));
1763: PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1764: PetscCall(PetscDSGetNumFields(ds, &dsNf));
1765: PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1766: PetscCall(PetscDSGetQuadrature(ds, &quad));
1767: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1768: PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1769: PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1770: if (!label) {
1771: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1772: } else {
1773: PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1774: PetscCall(ISGetLocalSize(pointIS, &cEnd));
1775: PetscCall(ISGetIndices(pointIS, &points));
1776: }
1777: for (c = cStart; c < cEnd; ++c) {
1778: const PetscInt cell = points ? points[c] : c;
1779: PetscScalar *x = NULL;
1780: const PetscInt *cone;
1781: PetscInt qc = 0, fOff = 0, dep;
1783: PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1784: if (dep != depth - 1) continue;
1785: if (isCohesive) {
1786: PetscCall(DMPlexGetCone(dm, cell, &cone));
1787: PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1788: } else {
1789: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1790: }
1791: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x));
1792: for (f = 0; f < dsNf; ++f) {
1793: PetscObject obj;
1794: PetscClassId id;
1795: void *const ctx = ctxs ? ctxs[fields[f]] : NULL;
1796: PetscInt Nb, Nc, q, fc;
1797: PetscReal elemDiff = 0.0;
1798: PetscBool cohesive;
1800: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1801: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1802: PetscCall(PetscObjectGetClassId(obj, &id));
1803: if (id == PETSCFE_CLASSID) {
1804: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1805: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1806: } else if (id == PETSCFV_CLASSID) {
1807: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1808: Nb = 1;
1809: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1810: if (isCohesive && !cohesive) {
1811: fOff += Nb * 2;
1812: qc += Nc;
1813: continue;
1814: }
1815: if (debug) {
1816: char title[1024];
1817: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1818: PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1819: }
1820: for (q = 0; q < Nq; ++q) {
1821: PetscFEGeom qgeom;
1822: PetscErrorCode ierr;
1824: qgeom.dimEmbed = fegeom.dimEmbed;
1825: qgeom.J = &fegeom.J[q * dE * dE];
1826: qgeom.invJ = &fegeom.invJ[q * dE * dE];
1827: qgeom.detJ = &fegeom.detJ[q];
1828: PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for cell %" PetscInt_FMT ", quadrature point %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
1829: if (transform) {
1830: gcoords = &coords[dE * Nq];
1831: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1832: } else {
1833: gcoords = &coords[dE * q];
1834: }
1835: for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1836: ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1837: if (ierr) {
1838: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1839: PetscCall(DMRestoreLocalVector(dm, &localX));
1840: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1841: }
1842: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1843: /* Call once for each face, except for lagrange field */
1844: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1845: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1846: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1847: for (fc = 0; fc < Nc; ++fc) {
1848: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1849: if (debug)
1850: PetscCall(PetscPrintf(PETSC_COMM_SELF, " cell %" PetscInt_FMT " field %" PetscInt_FMT ",%" PetscInt_FMT " 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),
1851: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1852: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1853: }
1854: }
1855: fOff += Nb;
1856: qc += Nc;
1857: localDiff[fields[f]] += elemDiff;
1858: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1859: }
1860: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1861: }
1862: if (label) {
1863: PetscCall(ISRestoreIndices(pointIS, &points));
1864: PetscCall(ISDestroy(&pointIS));
1865: }
1866: PetscCall(ISRestoreIndices(fieldIS, &fields));
1867: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1868: }
1869: PetscCall(DMRestoreLocalVector(dm, &localX));
1870: PetscCallMPI(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1871: PetscCall(PetscFree(localDiff));
1872: for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1873: PetscFunctionReturn(PETSC_SUCCESS);
1874: }
1876: /*@C
1877: 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.
1879: Collective
1881: Input Parameters:
1882: + dm - The `DM`
1883: . time - The time
1884: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1885: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1886: - X - The coefficient vector u_h
1888: Output Parameter:
1889: . D - A `Vec` which holds the difference ||u - u_h||_2 for each cell
1891: Level: developer
1893: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1894: @*/
1895: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1896: {
1897: PetscSection section;
1898: PetscQuadrature quad;
1899: Vec localX;
1900: PetscFEGeom fegeom;
1901: PetscScalar *funcVal, *interpolant;
1902: PetscReal *coords;
1903: const PetscReal *quadPoints, *quadWeights;
1904: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1906: PetscFunctionBegin;
1907: PetscCall(VecSet(D, 0.0));
1908: PetscCall(DMGetDimension(dm, &dim));
1909: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1910: PetscCall(DMGetLocalSection(dm, §ion));
1911: PetscCall(PetscSectionGetNumFields(section, &numFields));
1912: PetscCall(DMGetLocalVector(dm, &localX));
1913: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1914: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1915: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1916: for (field = 0; field < numFields; ++field) {
1917: PetscObject obj;
1918: PetscClassId id;
1919: PetscInt Nc;
1921: PetscCall(DMGetField(dm, field, NULL, &obj));
1922: PetscCall(PetscObjectGetClassId(obj, &id));
1923: if (id == PETSCFE_CLASSID) {
1924: PetscFE fe = (PetscFE)obj;
1926: PetscCall(PetscFEGetQuadrature(fe, &quad));
1927: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1928: } else if (id == PETSCFV_CLASSID) {
1929: PetscFV fv = (PetscFV)obj;
1931: PetscCall(PetscFVGetQuadrature(fv, &quad));
1932: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1933: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1934: numComponents += Nc;
1935: }
1936: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1937: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1938: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1939: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1940: for (c = cStart; c < cEnd; ++c) {
1941: PetscScalar *x = NULL;
1942: PetscScalar elemDiff = 0.0;
1943: PetscInt qc = 0;
1945: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1946: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1948: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1949: PetscObject obj;
1950: PetscClassId id;
1951: void *const ctx = ctxs ? ctxs[field] : NULL;
1952: PetscInt Nb, Nc, q, fc;
1954: PetscCall(DMGetField(dm, field, NULL, &obj));
1955: PetscCall(PetscObjectGetClassId(obj, &id));
1956: if (id == PETSCFE_CLASSID) {
1957: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1958: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1959: } else if (id == PETSCFV_CLASSID) {
1960: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1961: Nb = 1;
1962: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1963: if (funcs[field]) {
1964: for (q = 0; q < Nq; ++q) {
1965: PetscFEGeom qgeom;
1967: qgeom.dimEmbed = fegeom.dimEmbed;
1968: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1969: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1970: qgeom.detJ = &fegeom.detJ[q];
1971: PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
1972: PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1973: #if defined(needs_fix_with_return_code_argument)
1974: if (ierr) {
1975: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1976: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1977: PetscCall(DMRestoreLocalVector(dm, &localX));
1978: }
1979: #endif
1980: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1981: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1982: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1983: for (fc = 0; fc < Nc; ++fc) {
1984: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1985: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1986: }
1987: }
1988: }
1989: fieldOffset += Nb;
1990: qc += Nc;
1991: }
1992: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1993: PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1994: }
1995: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1996: PetscCall(DMRestoreLocalVector(dm, &localX));
1997: PetscCall(VecSqrtAbs(D));
1998: PetscFunctionReturn(PETSC_SUCCESS);
1999: }
2001: /*@
2002: DMPlexComputeL2FluxDiffVecLocal - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`
2004: Collective
2006: Input Parameters:
2007: + lu - The local `Vec` containing the primal solution
2008: . f - The field number for the potential
2009: . lmu - The local `Vec` containing the mixed solution
2010: - mf - The field number for the flux
2012: Output Parameter:
2013: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$
2015: Level: advanced
2017: Notes:
2018: We assume that the `DM` for each solution has the same topology, geometry, and quadrature.
2020: This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.
2022: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVec()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2023: @*/
2024: PetscErrorCode DMPlexComputeL2FluxDiffVecLocal(Vec lu, PetscInt f, Vec lmu, PetscInt mf, Vec eFlux)
2025: {
2026: DM dm, mdm, edm;
2027: PetscFE fe, mfe;
2028: PetscFEGeom fegeom;
2029: PetscQuadrature quad;
2030: const PetscReal *quadWeights;
2031: PetscReal *coords;
2032: PetscScalar *interpolant, *minterpolant, *earray;
2033: PetscInt cdim, mcdim, cStart, cEnd, Nc, mNc, qNc, Nq;
2034: MPI_Comm comm;
2036: PetscFunctionBegin;
2037: PetscCall(VecGetDM(lu, &dm));
2038: PetscCall(VecGetDM(lmu, &mdm));
2039: PetscCall(VecGetDM(eFlux, &edm));
2040: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2041: PetscCall(VecSet(eFlux, 0.0));
2043: // Check if the both problems are on the same mesh
2044: PetscCall(DMGetCoordinateDim(dm, &cdim));
2045: PetscCall(DMGetCoordinateDim(mdm, &mcdim));
2046: PetscCheck(cdim == mcdim, comm, PETSC_ERR_ARG_SIZ, "primal coordinate Dim %" PetscInt_FMT " != %" PetscInt_FMT " mixed coordinate Dim", cdim, mcdim);
2047: fegeom.dimEmbed = cdim;
2049: PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
2050: PetscCall(DMGetField(mdm, mf, NULL, (PetscObject *)&mfe));
2051: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2052: PetscCall(PetscFEGetNumComponents(mfe, &mNc));
2053: PetscCall(PetscFEGetQuadrature(fe, &quad));
2054: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
2055: PetscCheck(qNc == 1 || qNc == mNc, comm, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, mNc);
2057: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2058: PetscCall(VecGetArrayWrite(eFlux, &earray));
2059: PetscCall(PetscMalloc6(Nc * cdim, &interpolant, mNc * cdim, &minterpolant, cdim * (Nq + 1), &coords, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ, Nq, &fegeom.detJ));
2060: for (PetscInt c = cStart; c < cEnd; ++c) {
2061: PetscScalar *x = NULL;
2062: PetscScalar *mx = NULL;
2063: PetscScalar *eval = NULL;
2064: PetscReal fluxElemDiff = 0.0;
2066: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2067: PetscCall(DMPlexVecGetClosure(dm, NULL, lu, c, NULL, &x));
2068: PetscCall(DMPlexVecGetClosure(mdm, NULL, lmu, c, NULL, &mx));
2070: for (PetscInt q = 0; q < Nq; ++q) {
2071: PetscFEGeom qgeom;
2073: qgeom.dimEmbed = fegeom.dimEmbed;
2074: qgeom.J = &fegeom.J[q * cdim * cdim];
2075: qgeom.invJ = &fegeom.invJ[q * cdim * cdim];
2076: qgeom.detJ = &fegeom.detJ[q];
2078: PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
2080: PetscCall(PetscFEInterpolate_Static(mfe, &mx[0], &qgeom, q, minterpolant));
2081: PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[0], &qgeom, q, interpolant));
2083: /* Now take the elementwise difference and store that in a vector. */
2084: for (PetscInt fc = 0; fc < mNc; ++fc) {
2085: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : fc)];
2086: fluxElemDiff += PetscSqr(PetscRealPart(interpolant[fc] - minterpolant[fc])) * wt * fegeom.detJ[q];
2087: }
2088: }
2089: PetscCall(DMPlexVecRestoreClosure(dm, NULL, lu, c, NULL, &x));
2090: PetscCall(DMPlexVecRestoreClosure(mdm, NULL, lmu, c, NULL, &mx));
2091: PetscCall(DMPlexPointGlobalRef(edm, c, earray, (void *)&eval));
2092: if (eval) eval[0] = fluxElemDiff;
2093: }
2094: PetscCall(PetscFree6(interpolant, minterpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2095: PetscCall(VecRestoreArrayWrite(eFlux, &earray));
2097: PetscCall(VecAssemblyBegin(eFlux));
2098: PetscCall(VecAssemblyEnd(eFlux));
2099: PetscCall(VecSqrtAbs(eFlux));
2100: PetscFunctionReturn(PETSC_SUCCESS);
2101: }
2103: /*@
2104: DMPlexComputeL2FluxDiffVec - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`
2106: Collective
2108: Input Parameters:
2109: + u - The global `Vec` containing the primal solution
2110: . f - The field number for the potential
2111: . mu - The global `Vec` containing the mixed solution
2112: - mf - The field number for the flux
2114: Output Parameter:
2115: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$
2117: Level: advanced
2119: Notes:
2120: We assume that the `DM` for each solution has the same topology, geometry, and quadrature.
2122: This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.
2124: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2125: @*/
2126: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
2127: {
2128: DM dm, mdm;
2129: Vec lu, lmu;
2131: PetscFunctionBegin;
2132: PetscCall(VecGetDM(u, &dm));
2133: PetscCall(DMGetLocalVector(dm, &lu));
2134: PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
2135: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));
2137: PetscCall(VecGetDM(mu, &mdm));
2138: PetscCall(DMGetLocalVector(mdm, &lmu));
2139: PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
2140: PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));
2142: PetscCall(DMPlexComputeL2FluxDiffVecLocal(lu, f, lmu, mf, eFlux));
2144: PetscCall(DMRestoreLocalVector(dm, &lu));
2145: PetscCall(DMRestoreLocalVector(mdm, &lmu));
2146: PetscFunctionReturn(PETSC_SUCCESS);
2147: }
2149: /*@
2150: DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1
2152: Collective
2154: Input Parameters:
2155: + dm - The `DM`
2156: - locX - The coefficient vector u_h
2158: Output Parameter:
2159: . locC - A `Vec` which holds the Clement interpolant of the function
2161: Level: developer
2163: Note:
2164: $ u_h(v_i) = \sum_{T_i \in support(v_i)} |T_i| u_h(T_i) / \sum_{T_i \in support(v_i)} |T_i| $ where $ |T_i| $ is the cell volume
2166: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2167: @*/
2168: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
2169: {
2170: PetscInt debug = ((DM_Plex *)dm->data)->printFEM;
2171: DM dmc;
2172: PetscQuadrature quad;
2173: PetscScalar *interpolant, *valsum;
2174: PetscFEGeom fegeom;
2175: PetscReal *coords;
2176: const PetscReal *quadPoints, *quadWeights;
2177: PetscInt dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;
2179: PetscFunctionBegin;
2180: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2181: PetscCall(VecGetDM(locC, &dmc));
2182: PetscCall(VecSet(locC, 0.0));
2183: PetscCall(DMGetDimension(dm, &dim));
2184: PetscCall(DMGetCoordinateDim(dm, &cdim));
2185: fegeom.dimEmbed = cdim;
2186: PetscCall(DMGetNumFields(dm, &Nf));
2187: PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2188: for (f = 0; f < Nf; ++f) {
2189: PetscObject obj;
2190: PetscClassId id;
2191: PetscInt fNc;
2193: PetscCall(DMGetField(dm, f, NULL, &obj));
2194: PetscCall(PetscObjectGetClassId(obj, &id));
2195: if (id == PETSCFE_CLASSID) {
2196: PetscFE fe = (PetscFE)obj;
2198: PetscCall(PetscFEGetQuadrature(fe, &quad));
2199: PetscCall(PetscFEGetNumComponents(fe, &fNc));
2200: } else if (id == PETSCFV_CLASSID) {
2201: PetscFV fv = (PetscFV)obj;
2203: PetscCall(PetscFVGetQuadrature(fv, &quad));
2204: PetscCall(PetscFVGetNumComponents(fv, &fNc));
2205: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2206: Nc += fNc;
2207: }
2208: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2209: PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
2210: PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
2211: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2212: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2213: for (v = vStart; v < vEnd; ++v) {
2214: PetscScalar volsum = 0.0;
2215: PetscInt *star = NULL;
2216: PetscInt starSize, st, fc;
2218: PetscCall(PetscArrayzero(valsum, Nc));
2219: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2220: for (st = 0; st < starSize * 2; st += 2) {
2221: const PetscInt cell = star[st];
2222: PetscScalar *val = &valsum[Nc];
2223: PetscScalar *x = NULL;
2224: PetscReal vol = 0.0;
2225: PetscInt foff = 0;
2227: if ((cell < cStart) || (cell >= cEnd)) continue;
2228: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2229: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2230: for (f = 0; f < Nf; ++f) {
2231: PetscObject obj;
2232: PetscClassId id;
2233: PetscInt Nb, fNc, q;
2235: PetscCall(PetscArrayzero(val, Nc));
2236: PetscCall(DMGetField(dm, f, NULL, &obj));
2237: PetscCall(PetscObjectGetClassId(obj, &id));
2238: if (id == PETSCFE_CLASSID) {
2239: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
2240: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2241: } else if (id == PETSCFV_CLASSID) {
2242: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
2243: Nb = 1;
2244: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2245: for (q = 0; q < Nq; ++q) {
2246: const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
2247: PetscFEGeom qgeom;
2249: qgeom.dimEmbed = fegeom.dimEmbed;
2250: qgeom.J = &fegeom.J[q * cdim * cdim];
2251: qgeom.invJ = &fegeom.invJ[q * cdim * cdim];
2252: qgeom.detJ = &fegeom.detJ[q];
2253: PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
2254: PetscCheck(id == PETSCFE_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2255: PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
2256: for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
2257: vol += wt;
2258: }
2259: foff += Nb;
2260: }
2261: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2262: for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
2263: volsum += vol;
2264: if (debug) {
2265: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
2266: for (fc = 0; fc < Nc; ++fc) {
2267: if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2268: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
2269: }
2270: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2271: }
2272: }
2273: for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
2274: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2275: PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
2276: }
2277: PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2278: PetscFunctionReturn(PETSC_SUCCESS);
2279: }
2281: /*@
2282: DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1
2284: Collective
2286: Input Parameters:
2287: + dm - The `DM`
2288: - locX - The coefficient vector u_h
2290: Output Parameter:
2291: . locC - A `Vec` which holds the Clement interpolant of the gradient
2293: Level: developer
2295: Note:
2296: $\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
2298: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2299: @*/
2300: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
2301: {
2302: DM_Plex *mesh = (DM_Plex *)dm->data;
2303: PetscInt debug = mesh->printFEM;
2304: DM dmC;
2305: PetscQuadrature quad;
2306: PetscScalar *interpolant, *gradsum;
2307: PetscFEGeom fegeom;
2308: PetscReal *coords;
2309: const PetscReal *quadPoints, *quadWeights;
2310: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
2312: PetscFunctionBegin;
2313: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2314: PetscCall(VecGetDM(locC, &dmC));
2315: PetscCall(VecSet(locC, 0.0));
2316: PetscCall(DMGetDimension(dm, &dim));
2317: PetscCall(DMGetCoordinateDim(dm, &coordDim));
2318: fegeom.dimEmbed = coordDim;
2319: PetscCall(DMGetNumFields(dm, &numFields));
2320: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2321: for (field = 0; field < numFields; ++field) {
2322: PetscObject obj;
2323: PetscClassId id;
2324: PetscInt Nc;
2326: PetscCall(DMGetField(dm, field, NULL, &obj));
2327: PetscCall(PetscObjectGetClassId(obj, &id));
2328: if (id == PETSCFE_CLASSID) {
2329: PetscFE fe = (PetscFE)obj;
2331: PetscCall(PetscFEGetQuadrature(fe, &quad));
2332: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2333: } else if (id == PETSCFV_CLASSID) {
2334: PetscFV fv = (PetscFV)obj;
2336: PetscCall(PetscFVGetQuadrature(fv, &quad));
2337: PetscCall(PetscFVGetNumComponents(fv, &Nc));
2338: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2339: numComponents += Nc;
2340: }
2341: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2342: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
2343: PetscCall(PetscMalloc6(coordDim * numComponents * 2, &gradsum, coordDim * numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
2344: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2345: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2346: for (v = vStart; v < vEnd; ++v) {
2347: PetscScalar volsum = 0.0;
2348: PetscInt *star = NULL;
2349: PetscInt starSize, st, d, fc;
2351: PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
2352: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2353: for (st = 0; st < starSize * 2; st += 2) {
2354: const PetscInt cell = star[st];
2355: PetscScalar *grad = &gradsum[coordDim * numComponents];
2356: PetscScalar *x = NULL;
2357: PetscReal vol = 0.0;
2359: if ((cell < cStart) || (cell >= cEnd)) continue;
2360: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2361: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2362: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
2363: PetscObject obj;
2364: PetscClassId id;
2365: PetscInt Nb, Nc, q, qc = 0;
2367: PetscCall(PetscArrayzero(grad, coordDim * numComponents));
2368: PetscCall(DMGetField(dm, field, NULL, &obj));
2369: PetscCall(PetscObjectGetClassId(obj, &id));
2370: if (id == PETSCFE_CLASSID) {
2371: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
2372: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2373: } else if (id == PETSCFV_CLASSID) {
2374: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
2375: Nb = 1;
2376: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2377: for (q = 0; q < Nq; ++q) {
2378: PetscFEGeom qgeom;
2380: qgeom.dimEmbed = fegeom.dimEmbed;
2381: qgeom.J = &fegeom.J[q * coordDim * coordDim];
2382: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
2383: qgeom.detJ = &fegeom.detJ[q];
2384: PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
2385: PetscCheck(id == PETSCFE_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2386: PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
2387: for (fc = 0; fc < Nc; ++fc) {
2388: const PetscReal wt = quadWeights[q * qNc + qc];
2390: for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
2391: }
2392: vol += quadWeights[q * qNc] * fegeom.detJ[q];
2393: }
2394: fieldOffset += Nb;
2395: qc += Nc;
2396: }
2397: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2398: for (fc = 0; fc < numComponents; ++fc) {
2399: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
2400: }
2401: volsum += vol;
2402: if (debug) {
2403: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
2404: for (fc = 0; fc < numComponents; ++fc) {
2405: for (d = 0; d < coordDim; ++d) {
2406: if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2407: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
2408: }
2409: }
2410: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2411: }
2412: }
2413: for (fc = 0; fc < numComponents; ++fc) {
2414: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2415: }
2416: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2417: PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2418: }
2419: PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2420: PetscFunctionReturn(PETSC_SUCCESS);
2421: }
2423: PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec locX, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, PetscCtx ctx)
2424: {
2425: DM dmAux = NULL, plexA = NULL;
2426: PetscDS prob, probAux = NULL;
2427: PetscSection section, sectionAux;
2428: Vec locA;
2429: PetscInt dim, numCells = cEnd - cStart, c, f;
2430: PetscBool useFVM = PETSC_FALSE;
2431: /* DS */
2432: PetscInt Nf, totDim, *uOff, *uOff_x, numConstants;
2433: PetscInt NfAux, totDimAux, *aOff;
2434: PetscScalar *u, *a = NULL;
2435: const PetscScalar *constants;
2436: /* Geometry */
2437: PetscFEGeom *cgeomFEM;
2438: DM dmGrad;
2439: PetscQuadrature affineQuad = NULL;
2440: Vec cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2441: PetscFVCellGeom *cgeomFVM;
2442: const PetscScalar *lgrad;
2443: PetscInt maxDegree;
2444: DMField coordField;
2445: IS cellIS;
2447: PetscFunctionBegin;
2448: PetscCall(DMGetDS(dm, &prob));
2449: PetscCall(DMGetDimension(dm, &dim));
2450: PetscCall(DMGetLocalSection(dm, §ion));
2451: PetscCall(DMGetNumFields(dm, &Nf));
2452: /* Determine which discretizations we have */
2453: for (f = 0; f < Nf; ++f) {
2454: PetscObject obj;
2455: PetscClassId id;
2457: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2458: PetscCall(PetscObjectGetClassId(obj, &id));
2459: if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2460: }
2461: /* Read DS information */
2462: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2463: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2464: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2465: PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2466: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2467: /* Read Auxiliary DS information */
2468: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2469: if (locA) {
2470: PetscCall(VecGetDM(locA, &dmAux));
2471: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2472: PetscCall(DMGetDS(dmAux, &probAux));
2473: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2474: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2475: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2476: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2477: }
2478: /* Allocate data arrays */
2479: PetscCall(PetscCalloc1(numCells * totDim, &u));
2480: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2481: /* Read out geometry */
2482: PetscCall(DMGetCoordinateField(dm, &coordField));
2483: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2484: if (maxDegree <= 1) {
2485: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2486: if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &cgeomFEM));
2487: }
2488: if (useFVM) {
2489: PetscFV fv = NULL;
2490: Vec grad;
2491: PetscInt fStart, fEnd;
2492: PetscBool compGrad;
2494: for (f = 0; f < Nf; ++f) {
2495: PetscObject obj;
2496: PetscClassId id;
2498: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2499: PetscCall(PetscObjectGetClassId(obj, &id));
2500: if (id == PETSCFV_CLASSID) {
2501: fv = (PetscFV)obj;
2502: break;
2503: }
2504: }
2505: PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2506: PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2507: PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2508: PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2509: PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2510: PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2511: /* Reconstruct and limit cell gradients */
2512: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2513: PetscCall(DMGetGlobalVector(dmGrad, &grad));
2514: PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2515: /* Communicate gradient values */
2516: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2517: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2518: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2519: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2520: /* Handle non-essential (e.g. outflow) boundary values */
2521: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2522: PetscCall(VecGetArrayRead(locGrad, &lgrad));
2523: }
2524: /* Read out data from inputs */
2525: for (c = cStart; c < cEnd; ++c) {
2526: PetscScalar *x = NULL;
2527: PetscInt i;
2529: PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2530: for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2531: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2532: if (dmAux) {
2533: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, c, NULL, &x));
2534: for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2535: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, c, NULL, &x));
2536: }
2537: }
2538: /* Do integration for each field */
2539: for (f = 0; f < Nf; ++f) {
2540: PetscObject obj;
2541: PetscClassId id;
2542: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
2544: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2545: PetscCall(PetscObjectGetClassId(obj, &id));
2546: if (id == PETSCFE_CLASSID) {
2547: PetscFE fe = (PetscFE)obj;
2548: PetscQuadrature q;
2549: PetscFEGeom *chunkGeom = NULL;
2550: PetscInt Nq, Nb;
2552: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2553: PetscCall(PetscFEGetQuadrature(fe, &q));
2554: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2555: PetscCall(PetscFEGetDimension(fe, &Nb));
2556: blockSize = Nb * Nq;
2557: batchSize = numBlocks * blockSize;
2558: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2559: numChunks = numCells / (numBatches * batchSize);
2560: Ne = numChunks * numBatches * batchSize;
2561: Nr = numCells % (numBatches * batchSize);
2562: offset = numCells - Nr;
2563: if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FEGEOM_BASIC, &cgeomFEM));
2564: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2565: PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2566: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2567: PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &cintegral[offset * Nf]));
2568: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2569: if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2570: } else if (id == PETSCFV_CLASSID) {
2571: PetscInt foff;
2572: PetscPointFn *obj_func;
2574: PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2575: PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2576: if (obj_func) {
2577: for (c = 0; c < numCells; ++c) {
2578: PetscScalar *u_x;
2579: PetscScalar lint = 0.;
2581: PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2582: obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim * c + foff], NULL, u_x, aOff, NULL, PetscSafePointerPlusOffset(a, totDimAux * c), NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
2583: cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2584: }
2585: }
2586: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2587: }
2588: /* Cleanup data arrays */
2589: if (useFVM) {
2590: PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2591: PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2592: PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2593: PetscCall(VecDestroy(&faceGeometryFVM));
2594: PetscCall(VecDestroy(&cellGeometryFVM));
2595: PetscCall(DMDestroy(&dmGrad));
2596: }
2597: if (dmAux) PetscCall(PetscFree(a));
2598: PetscCall(DMDestroy(&plexA));
2599: PetscCall(PetscFree(u));
2600: /* Cleanup */
2601: if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2602: PetscCall(PetscQuadratureDestroy(&affineQuad));
2603: PetscCall(ISDestroy(&cellIS));
2604: PetscFunctionReturn(PETSC_SUCCESS);
2605: }
2607: /*@
2608: DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user
2610: Input Parameters:
2611: + dm - The mesh
2612: . X - Global input vector
2613: - ctx - The application context
2615: Output Parameter:
2616: . integral - Integral for each field
2618: Level: developer
2620: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2621: @*/
2622: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, PetscCtx ctx)
2623: {
2624: PetscInt printFEM;
2625: PetscScalar *cintegral, *lintegral;
2626: PetscInt Nf, f, cellHeight, cStart, cEnd, cell;
2627: Vec locX;
2629: PetscFunctionBegin;
2632: PetscAssertPointer(integral, 3);
2633: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2634: PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2635: PetscCall(DMGetNumFields(dm, &Nf));
2636: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2637: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2638: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2639: PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2640: /* Get local solution with boundary values */
2641: PetscCall(DMGetLocalVector(dm, &locX));
2642: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2643: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2644: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2645: PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, ctx));
2646: PetscCall(DMRestoreLocalVector(dm, &locX));
2647: printFEM = ((DM_Plex *)dm->data)->printFEM;
2648: /* Sum up values */
2649: for (cell = cStart; cell < cEnd; ++cell) {
2650: const PetscInt c = cell - cStart;
2652: if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2653: for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2654: }
2655: PetscCallMPI(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2656: if (printFEM) {
2657: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2658: for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2659: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2660: }
2661: PetscCall(PetscFree2(lintegral, cintegral));
2662: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2663: PetscCall(DMDestroy(&dm));
2664: PetscFunctionReturn(PETSC_SUCCESS);
2665: }
2667: /*@
2668: DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user
2670: Input Parameters:
2671: + dm - The mesh
2672: . X - Global input vector
2673: - ctx - The application context
2675: Output Parameter:
2676: . F - Cellwise integrals for each field
2678: Level: developer
2680: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2681: @*/
2682: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, PetscCtx ctx)
2683: {
2684: PetscInt printFEM;
2685: DM dmF;
2686: PetscSection sectionF = NULL;
2687: PetscScalar *cintegral, *af;
2688: PetscInt Nf, f, cellHeight, cStart, cEnd, cell, n;
2689: Vec locX;
2691: PetscFunctionBegin;
2695: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2696: PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2697: PetscCall(DMGetNumFields(dm, &Nf));
2698: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2699: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2700: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2701: PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2702: /* Get local solution with boundary values */
2703: PetscCall(DMGetLocalVector(dm, &locX));
2704: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2705: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2706: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2707: PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, ctx));
2708: PetscCall(DMRestoreLocalVector(dm, &locX));
2709: /* Put values in F */
2710: PetscCall(VecGetArray(F, &af));
2711: PetscCall(VecGetDM(F, &dmF));
2712: if (dmF) PetscCall(DMGetLocalSection(dmF, §ionF));
2713: PetscCall(VecGetLocalSize(F, &n));
2714: PetscCheck(n >= (cEnd - cStart) * Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Vector size %" PetscInt_FMT " < %" PetscInt_FMT, n, (cEnd - cStart) * Nf);
2715: printFEM = ((DM_Plex *)dm->data)->printFEM;
2716: for (cell = cStart; cell < cEnd; ++cell) {
2717: const PetscInt c = cell - cStart;
2718: PetscInt dof = Nf, off = c * Nf;
2720: if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2721: if (sectionF) {
2722: PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2723: PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2724: }
2725: PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2726: for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2727: }
2728: PetscCall(VecRestoreArray(F, &af));
2729: PetscCall(PetscFree(cintegral));
2730: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2731: PetscCall(DMDestroy(&dm));
2732: PetscFunctionReturn(PETSC_SUCCESS);
2733: }
2735: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *fintegral, PetscCtx ctx)
2736: {
2737: DM plex = NULL, plexA = NULL;
2738: DMEnclosureType encAux;
2739: PetscDS prob, probAux = NULL;
2740: PetscSection section, sectionAux = NULL;
2741: Vec locA = NULL;
2742: DMField coordField;
2743: PetscInt Nf, totDim, *uOff, *uOff_x;
2744: PetscInt NfAux = 0, totDimAux = 0, *aOff = NULL;
2745: PetscScalar *u, *a = NULL;
2746: const PetscScalar *constants;
2747: PetscInt numConstants, f;
2749: PetscFunctionBegin;
2750: PetscCall(DMGetCoordinateField(dm, &coordField));
2751: PetscCall(DMConvert(dm, DMPLEX, &plex));
2752: PetscCall(DMGetDS(dm, &prob));
2753: PetscCall(DMGetLocalSection(dm, §ion));
2754: PetscCall(PetscSectionGetNumFields(section, &Nf));
2755: /* Determine which discretizations we have */
2756: for (f = 0; f < Nf; ++f) {
2757: PetscObject obj;
2758: PetscClassId id;
2760: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2761: PetscCall(PetscObjectGetClassId(obj, &id));
2762: PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2763: }
2764: /* Read DS information */
2765: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2766: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2767: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2768: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2769: /* Read Auxiliary DS information */
2770: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2771: if (locA) {
2772: DM dmAux;
2774: PetscCall(VecGetDM(locA, &dmAux));
2775: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2776: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2777: PetscCall(DMGetDS(dmAux, &probAux));
2778: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2779: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2780: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2781: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2782: }
2783: /* Integrate over points */
2784: {
2785: PetscFEGeom *fgeom, *chunkGeom = NULL;
2786: PetscInt maxDegree;
2787: PetscQuadrature qGeom = NULL;
2788: const PetscInt *points;
2789: PetscInt numFaces, face, Nq, field;
2790: PetscInt numChunks, chunkSize, chunk, Nr, offset;
2792: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2793: PetscCall(ISGetIndices(pointIS, &points));
2794: PetscCall(PetscCalloc2(numFaces * totDim, &u, (locA ? (size_t)numFaces * totDimAux : 0), &a));
2795: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2796: for (face = 0; face < numFaces; ++face) {
2797: const PetscInt point = points[face], *support;
2798: PetscScalar *x = NULL;
2800: PetscCall(DMPlexGetSupport(dm, point, &support));
2801: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2802: for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2803: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2804: if (locA) {
2805: PetscInt subp;
2806: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2807: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2808: for (PetscInt i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2809: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2810: }
2811: }
2812: for (field = 0; field < Nf; ++field) {
2813: PetscFE fe;
2815: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2816: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2817: if (!qGeom) {
2818: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2819: PetscCall(PetscObjectReference((PetscObject)qGeom));
2820: }
2821: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2822: PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
2823: /* Get blocking */
2824: {
2825: PetscQuadrature q;
2826: PetscInt numBatches, batchSize, numBlocks, blockSize;
2827: PetscInt Nq, Nb;
2829: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2830: PetscCall(PetscFEGetQuadrature(fe, &q));
2831: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2832: PetscCall(PetscFEGetDimension(fe, &Nb));
2833: blockSize = Nb * Nq;
2834: batchSize = numBlocks * blockSize;
2835: chunkSize = numBatches * batchSize;
2836: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2837: numChunks = numFaces / chunkSize;
2838: Nr = numFaces % chunkSize;
2839: offset = numFaces - Nr;
2840: }
2841: /* Do integration for each field */
2842: for (chunk = 0; chunk < numChunks; ++chunk) {
2843: PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2844: PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], chunkSize, chunkGeom, &u[chunk * chunkSize * totDim], probAux, PetscSafePointerPlusOffset(a, chunk * chunkSize * totDimAux), &fintegral[chunk * chunkSize * Nf]));
2845: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2846: }
2847: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2848: PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf]));
2849: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2850: /* Cleanup data arrays */
2851: PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
2852: PetscCall(PetscQuadratureDestroy(&qGeom));
2853: }
2854: PetscCall(PetscFree2(u, a));
2855: PetscCall(ISRestoreIndices(pointIS, &points));
2856: }
2857: PetscCall(DMDestroy(&plex));
2858: PetscCall(DMDestroy(&plexA));
2859: PetscFunctionReturn(PETSC_SUCCESS);
2860: }
2862: /*@C
2863: DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2865: Input Parameters:
2866: + dm - The mesh
2867: . X - Global input vector
2868: . label - The boundary `DMLabel`
2869: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2870: . vals - The label values to use, or NULL for all values
2871: . funcs - The functions to integrate along the boundary for each field
2872: - ctx - The application context
2874: Output Parameter:
2875: . integral - Integral for each field
2877: Level: developer
2879: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2880: @*/
2881: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *integral, PetscCtx ctx)
2882: {
2883: Vec locX;
2884: PetscSection section;
2885: DMLabel depthLabel;
2886: IS facetIS;
2887: PetscInt dim, Nf, f, v;
2889: PetscFunctionBegin;
2893: if (vals) PetscAssertPointer(vals, 5);
2894: PetscAssertPointer(integral, 7);
2895: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2896: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2897: PetscCall(DMGetDimension(dm, &dim));
2898: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2899: /* Filter out ghost facets (SF leaves) so that each boundary facet is only
2900: counted on one rank. Without this, shared facets at partition boundaries
2901: are integrated on multiple ranks, causing double-counting after MPI sum. */
2902: if (facetIS) {
2903: PetscSF sf;
2904: PetscInt nleaves;
2905: const PetscInt *leaves;
2907: PetscCall(DMGetPointSF(dm, &sf));
2908: PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
2909: if (nleaves > 0 && leaves) {
2910: IS leafIS, ownedFacetIS;
2912: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nleaves, leaves, PETSC_USE_POINTER, &leafIS));
2913: PetscCall(ISDifference(facetIS, leafIS, &ownedFacetIS));
2914: PetscCall(ISDestroy(&leafIS));
2915: PetscCall(ISDestroy(&facetIS));
2916: facetIS = ownedFacetIS;
2917: }
2918: }
2919: PetscCall(DMGetLocalSection(dm, §ion));
2920: PetscCall(PetscSectionGetNumFields(section, &Nf));
2921: /* Get local solution with boundary values */
2922: PetscCall(DMGetLocalVector(dm, &locX));
2923: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2924: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2925: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2926: /* Loop over label values */
2927: PetscCall(PetscArrayzero(integral, Nf));
2928: for (v = 0; v < numVals; ++v) {
2929: IS pointIS;
2930: PetscInt numFaces, face;
2931: PetscScalar *fintegral;
2933: PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2934: if (!pointIS) continue; /* No points with that id on this process */
2935: {
2936: IS isectIS;
2938: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2939: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2940: PetscCall(ISDestroy(&pointIS));
2941: pointIS = isectIS;
2942: }
2943: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2944: PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2945: PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, funcs, fintegral, ctx));
2946: /* Sum point contributions into integral */
2947: for (f = 0; f < Nf; ++f)
2948: for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2949: PetscCall(PetscFree(fintegral));
2950: PetscCall(ISDestroy(&pointIS));
2951: }
2952: PetscCall(DMRestoreLocalVector(dm, &locX));
2953: PetscCall(ISDestroy(&facetIS));
2954: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2955: PetscFunctionReturn(PETSC_SUCCESS);
2956: }
2958: /*@
2959: DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix from the coarse `DM` to a uniformly refined `DM`.
2961: Input Parameters:
2962: + dmc - The coarse mesh
2963: . dmf - The fine mesh
2964: . isRefined - Flag indicating regular refinement, rather than the same topology
2965: - ctx - The application context
2967: Output Parameter:
2968: . In - The interpolation matrix
2970: Level: developer
2972: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`
2973: @*/
2974: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, PetscCtx ctx)
2975: {
2976: DM_Plex *mesh = (DM_Plex *)dmc->data;
2977: const char *name = "Interpolator";
2978: PetscFE *feRef;
2979: PetscFV *fvRef;
2980: PetscSection fsection, fglobalSection;
2981: PetscSection csection, cglobalSection;
2982: PetscScalar *elemMat;
2983: PetscInt dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2984: PetscInt cTotDim = 0, rTotDim = 0;
2986: PetscFunctionBegin;
2987: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2988: PetscCall(DMGetDimension(dmf, &dim));
2989: PetscCall(DMGetLocalSection(dmf, &fsection));
2990: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2991: PetscCall(DMGetLocalSection(dmc, &csection));
2992: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2993: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2994: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2995: PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2996: for (f = 0; f < Nf; ++f) {
2997: PetscObject obj, objc;
2998: PetscClassId id, idc;
2999: PetscInt rNb = 0, Nc = 0, cNb = 0;
3001: PetscCall(DMGetField(dmf, f, NULL, &obj));
3002: PetscCall(PetscObjectGetClassId(obj, &id));
3003: if (id == PETSCFE_CLASSID) {
3004: PetscFE fe = (PetscFE)obj;
3006: if (isRefined) PetscCall(PetscFERefine(fe, &feRef[f]));
3007: else {
3008: PetscCall(PetscObjectReference((PetscObject)fe));
3009: feRef[f] = fe;
3010: }
3011: PetscCall(PetscFEGetDimension(feRef[f], &rNb));
3012: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3013: } else if (id == PETSCFV_CLASSID) {
3014: PetscFV fv = (PetscFV)obj;
3015: PetscDualSpace Q;
3017: if (isRefined) PetscCall(PetscFVRefine(fv, &fvRef[f]));
3018: else {
3019: PetscCall(PetscObjectReference((PetscObject)fv));
3020: fvRef[f] = fv;
3021: }
3022: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3023: PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
3024: PetscCall(PetscFVGetDualSpace(fv, &Q));
3025: PetscCall(PetscFVGetNumComponents(fv, &Nc));
3026: }
3027: PetscCall(DMGetField(dmc, f, NULL, &objc));
3028: PetscCall(PetscObjectGetClassId(objc, &idc));
3029: if (idc == PETSCFE_CLASSID) {
3030: PetscFE fe = (PetscFE)objc;
3032: PetscCall(PetscFEGetDimension(fe, &cNb));
3033: } else if (id == PETSCFV_CLASSID) {
3034: PetscFV fv = (PetscFV)obj;
3035: PetscDualSpace Q;
3037: PetscCall(PetscFVGetDualSpace(fv, &Q));
3038: PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
3039: }
3040: rTotDim += rNb;
3041: cTotDim += cNb;
3042: }
3043: PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
3044: PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
3045: for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
3046: PetscDualSpace Qref;
3047: PetscQuadrature f;
3048: const PetscReal *qpoints, *qweights;
3049: PetscReal *points;
3050: PetscInt npoints = 0, Nc, Np, fpdim, i, k, p, d;
3052: /* Compose points from all dual basis functionals */
3053: if (feRef[fieldI]) {
3054: PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
3055: PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
3056: } else {
3057: PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
3058: PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
3059: }
3060: PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
3061: for (i = 0; i < fpdim; ++i) {
3062: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3063: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
3064: npoints += Np;
3065: }
3066: PetscCall(PetscMalloc1(npoints * dim, &points));
3067: for (i = 0, k = 0; i < fpdim; ++i) {
3068: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3069: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
3070: for (p = 0; p < Np; ++p, ++k)
3071: for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
3072: }
3074: for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
3075: PetscObject obj;
3076: PetscClassId id;
3077: PetscInt NcJ = 0, cpdim = 0, j, qNc;
3079: PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
3080: PetscCall(PetscObjectGetClassId(obj, &id));
3081: if (id == PETSCFE_CLASSID) {
3082: PetscFE fe = (PetscFE)obj;
3083: PetscTabulation T = NULL;
3085: /* Evaluate basis at points */
3086: PetscCall(PetscFEGetNumComponents(fe, &NcJ));
3087: PetscCall(PetscFEGetDimension(fe, &cpdim));
3088: /* For now, fields only interpolate themselves */
3089: if (fieldI == fieldJ) {
3090: PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ);
3091: PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
3092: for (i = 0, k = 0; i < fpdim; ++i) {
3093: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3094: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3095: PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ);
3096: for (p = 0; p < Np; ++p, ++k) {
3097: for (j = 0; j < cpdim; ++j) {
3098: /*
3099: cTotDim: Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
3100: offsetI, offsetJ: Offsets into the larger element interpolation matrix for different fields
3101: fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
3102: qNC, Nc, Ncj, c: Number of components in this field
3103: Np, p: Number of quad points in the fine grid functional i
3104: k: i*Np + p, overall point number for the interpolation
3105: */
3106: 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];
3107: }
3108: }
3109: }
3110: PetscCall(PetscTabulationDestroy(&T));
3111: }
3112: } else if (id == PETSCFV_CLASSID) {
3113: PetscFV fv = (PetscFV)obj;
3115: /* Evaluate constant function at points */
3116: PetscCall(PetscFVGetNumComponents(fv, &NcJ));
3117: cpdim = 1;
3118: /* For now, fields only interpolate themselves */
3119: if (fieldI == fieldJ) {
3120: PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ);
3121: for (i = 0, k = 0; i < fpdim; ++i) {
3122: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3123: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3124: PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ);
3125: for (p = 0; p < Np; ++p, ++k) {
3126: for (j = 0; j < cpdim; ++j) {
3127: for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
3128: }
3129: }
3130: }
3131: }
3132: }
3133: offsetJ += cpdim;
3134: }
3135: offsetI += fpdim;
3136: PetscCall(PetscFree(points));
3137: }
3138: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
3139: /* Preallocate matrix */
3140: {
3141: Mat preallocator;
3142: PetscScalar *vals;
3143: PetscInt *cellCIndices, *cellFIndices;
3144: PetscInt locRows, locCols, cell;
3146: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3147: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
3148: PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
3149: PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3150: PetscCall(MatSetUp(preallocator));
3151: PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
3152: if (locRows || locCols) {
3153: for (cell = cStart; cell < cEnd; ++cell) {
3154: if (isRefined) {
3155: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
3156: PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
3157: } else {
3158: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
3159: }
3160: }
3161: }
3162: PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
3163: PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
3164: PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
3165: PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
3166: PetscCall(MatDestroy(&preallocator));
3167: }
3168: /* Fill matrix */
3169: PetscCall(MatZeroEntries(In));
3170: for (c = cStart; c < cEnd; ++c) {
3171: if (isRefined) {
3172: PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
3173: } else {
3174: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
3175: }
3176: }
3177: for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
3178: PetscCall(PetscFree2(feRef, fvRef));
3179: PetscCall(PetscFree(elemMat));
3180: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3181: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3182: if (mesh->printFEM > 1) {
3183: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
3184: PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
3185: PetscCall(MatView(In, NULL));
3186: }
3187: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3188: PetscFunctionReturn(PETSC_SUCCESS);
3189: }
3191: /*@
3192: DMPlexComputeMassMatrixNested - Form the local portion of the mass matrix from a coarse `DM` to a nested fine `DM`.
3194: Collective
3196: Input Parameters:
3197: + dmc - the coarse mesh
3198: . dmf - the fine mesh
3199: - ctx - the application context
3201: Output Parameter:
3202: . mass - the mass matrix
3204: Level: developer
3206: Note:
3207: This routine is not implemented and currently raises `PETSC_ERR_SUP`.
3209: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixGeneral()`, `DMPlexComputeInterpolatorNested()`
3210: @*/
3211: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, PetscCtx ctx)
3212: {
3213: SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
3214: }
3216: /*@
3217: DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix from the coarse `DM` to a non-nested fine `DM`.
3219: Input Parameters:
3220: + dmf - The fine mesh
3221: . dmc - The coarse mesh
3222: - ctx - The application context
3224: Output Parameter:
3225: . In - The interpolation matrix
3227: Level: developer
3229: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3230: @*/
3231: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, PetscCtx ctx)
3232: {
3233: DM_Plex *mesh = (DM_Plex *)dmf->data;
3234: const char *name = "Interpolator";
3235: PetscDS prob;
3236: Mat interp;
3237: PetscSection fsection, globalFSection;
3238: PetscSection csection, globalCSection;
3239: PetscInt locRows, locCols;
3240: PetscReal *x, *v0, *J, *invJ, detJ;
3241: PetscReal *v0c, *Jc, *invJc, detJc;
3242: PetscScalar *elemMat;
3243: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;
3245: PetscFunctionBegin;
3246: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3247: PetscCall(DMGetCoordinateDim(dmc, &dim));
3248: PetscCall(DMGetDS(dmc, &prob));
3249: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3250: PetscCall(PetscDSGetNumFields(prob, &Nf));
3251: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3252: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3253: PetscCall(DMGetLocalSection(dmf, &fsection));
3254: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3255: PetscCall(DMGetLocalSection(dmc, &csection));
3256: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3257: PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
3258: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3259: PetscCall(PetscMalloc1(totDim, &elemMat));
3261: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3262: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
3263: PetscCall(MatSetType(interp, MATPREALLOCATOR));
3264: PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3265: PetscCall(MatSetUp(interp));
3266: for (s = 0; s < 2; ++s) {
3267: for (field = 0; field < Nf; ++field) {
3268: PetscObject obj;
3269: PetscClassId id;
3270: PetscDualSpace Q = NULL;
3271: PetscTabulation T = NULL;
3272: PetscQuadrature f;
3273: const PetscReal *qpoints, *qweights;
3274: PetscInt Nc, qNc, Np, fpdim, off, i, d;
3276: PetscCall(PetscDSGetFieldOffset(prob, field, &off));
3277: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3278: PetscCall(PetscObjectGetClassId(obj, &id));
3279: if (id == PETSCFE_CLASSID) {
3280: PetscFE fe = (PetscFE)obj;
3282: PetscCall(PetscFEGetDualSpace(fe, &Q));
3283: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3284: if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
3285: } else if (id == PETSCFV_CLASSID) {
3286: PetscFV fv = (PetscFV)obj;
3288: PetscCall(PetscFVGetDualSpace(fv, &Q));
3289: Nc = 1;
3290: } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
3291: PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
3292: /* For each fine grid cell */
3293: for (cell = cStart; cell < cEnd; ++cell) {
3294: PetscInt *findices, *cindices;
3295: PetscInt numFIndices, numCIndices;
3297: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3298: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3299: PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
3300: for (i = 0; i < fpdim; ++i) {
3301: Vec pointVec;
3302: PetscScalar *pV;
3303: PetscSF coarseCellSF = NULL;
3304: const PetscSFNode *coarseCells;
3305: PetscInt numCoarseCells, cpdim, row = findices[i + off], q, c, j;
3307: /* Get points from the dual basis functional quadrature */
3308: PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
3309: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
3310: PetscCheck(qNc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, Nc);
3311: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
3312: PetscCall(VecSetBlockSize(pointVec, dim));
3313: PetscCall(VecGetArray(pointVec, &pV));
3314: for (q = 0; q < Np; ++q) {
3315: const PetscReal xi0[3] = {-1., -1., -1.};
3317: /* Transform point to real space */
3318: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3319: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3320: }
3321: PetscCall(VecRestoreArray(pointVec, &pV));
3322: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3323: /* OPT: Read this out from preallocation information */
3324: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3325: /* Update preallocation info */
3326: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3327: PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3328: PetscCall(VecGetArray(pointVec, &pV));
3329: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3330: PetscReal pVReal[3];
3331: const PetscReal xi0[3] = {-1., -1., -1.};
3333: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3334: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim));
3335: else cpdim = 1;
3337: if (s) {
3338: /* Transform points from real space to coarse reference space */
3339: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3340: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3341: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3343: if (id == PETSCFE_CLASSID) {
3344: /* Evaluate coarse basis on contained point */
3345: PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
3346: PetscCall(PetscArrayzero(elemMat, cpdim));
3347: /* Get elemMat entries by multiplying by weight */
3348: for (j = 0; j < cpdim; ++j) {
3349: for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
3350: }
3351: } else {
3352: for (j = 0; j < cpdim; ++j) {
3353: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
3354: }
3355: }
3356: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3357: }
3358: /* Update interpolator */
3359: PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
3360: PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
3361: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3362: }
3363: PetscCall(VecRestoreArray(pointVec, &pV));
3364: PetscCall(PetscSFDestroy(&coarseCellSF));
3365: PetscCall(VecDestroy(&pointVec));
3366: }
3367: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3368: }
3369: if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3370: }
3371: if (!s) {
3372: PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
3373: PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
3374: PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
3375: PetscCall(MatDestroy(&interp));
3376: interp = In;
3377: }
3378: }
3379: PetscCall(PetscFree3(v0, J, invJ));
3380: PetscCall(PetscFree3(v0c, Jc, invJc));
3381: PetscCall(PetscFree(elemMat));
3382: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3383: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3384: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3385: PetscFunctionReturn(PETSC_SUCCESS);
3386: }
3388: /*@
3389: DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix from the coarse `DM` to a non-nested fine `DM`.
3391: Input Parameters:
3392: + dmf - The fine mesh
3393: . dmc - The coarse mesh
3394: - ctx - The application context
3396: Output Parameter:
3397: . mass - The mass matrix
3399: Level: developer
3401: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`
3402: @*/
3403: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, PetscCtx ctx)
3404: {
3405: DM_Plex *mesh = (DM_Plex *)dmf->data;
3406: const char *name = "Mass Matrix";
3407: PetscDS prob;
3408: PetscSection fsection, csection, globalFSection, globalCSection;
3409: PetscHSetIJ ht;
3410: PetscLayout rLayout;
3411: PetscInt *dnz, *onz;
3412: PetscInt locRows, rStart, rEnd;
3413: PetscReal *x, *v0, *J, *invJ, detJ;
3414: PetscReal *v0c, *Jc, *invJc, detJc;
3415: PetscScalar *elemMat;
3416: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
3418: PetscFunctionBegin;
3419: PetscCall(DMGetCoordinateDim(dmc, &dim));
3420: PetscCall(DMGetDS(dmc, &prob));
3421: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3422: PetscCall(PetscDSGetNumFields(prob, &Nf));
3423: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3424: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3425: PetscCall(DMGetLocalSection(dmf, &fsection));
3426: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3427: PetscCall(DMGetLocalSection(dmc, &csection));
3428: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3429: PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
3430: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3431: PetscCall(PetscMalloc1(totDim, &elemMat));
3433: PetscCall(MatGetLocalSize(mass, &locRows, NULL));
3434: PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
3435: PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
3436: PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
3437: PetscCall(PetscLayoutSetUp(rLayout));
3438: PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
3439: PetscCall(PetscLayoutDestroy(&rLayout));
3440: PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
3441: PetscCall(PetscHSetIJCreate(&ht));
3442: for (field = 0; field < Nf; ++field) {
3443: PetscObject obj;
3444: PetscClassId id;
3445: PetscQuadrature quad;
3446: const PetscReal *qpoints;
3447: PetscInt Nq, Nc, i, d;
3449: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3450: PetscCall(PetscObjectGetClassId(obj, &id));
3451: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3452: else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3453: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
3454: /* For each fine grid cell */
3455: for (cell = cStart; cell < cEnd; ++cell) {
3456: Vec pointVec;
3457: PetscScalar *pV;
3458: PetscSF coarseCellSF = NULL;
3459: const PetscSFNode *coarseCells;
3460: PetscInt numCoarseCells, q, c;
3461: PetscInt *findices, *cindices;
3462: PetscInt numFIndices, numCIndices;
3464: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3465: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3466: /* Get points from the quadrature */
3467: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3468: PetscCall(VecSetBlockSize(pointVec, dim));
3469: PetscCall(VecGetArray(pointVec, &pV));
3470: for (q = 0; q < Nq; ++q) {
3471: const PetscReal xi0[3] = {-1., -1., -1.};
3473: /* Transform point to real space */
3474: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3475: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3476: }
3477: PetscCall(VecRestoreArray(pointVec, &pV));
3478: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3479: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3480: PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3481: /* Update preallocation info */
3482: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3483: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3484: {
3485: PetscHashIJKey key;
3486: PetscBool missing;
3488: for (i = 0; i < numFIndices; ++i) {
3489: key.i = findices[i];
3490: if (key.i >= 0) {
3491: /* Get indices for coarse elements */
3492: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3493: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3494: for (c = 0; c < numCIndices; ++c) {
3495: key.j = cindices[c];
3496: if (key.j < 0) continue;
3497: PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3498: if (missing) {
3499: if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3500: else ++onz[key.i - rStart];
3501: }
3502: }
3503: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3504: }
3505: }
3506: }
3507: }
3508: PetscCall(PetscSFDestroy(&coarseCellSF));
3509: PetscCall(VecDestroy(&pointVec));
3510: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3511: }
3512: }
3513: PetscCall(PetscHSetIJDestroy(&ht));
3514: PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3515: PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3516: PetscCall(PetscFree2(dnz, onz));
3517: for (field = 0; field < Nf; ++field) {
3518: PetscObject obj;
3519: PetscClassId id;
3520: PetscTabulation T, Tfine;
3521: PetscQuadrature quad;
3522: const PetscReal *qpoints, *qweights;
3523: PetscInt Nq, Nc, i, d;
3525: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3526: PetscCall(PetscObjectGetClassId(obj, &id));
3527: if (id == PETSCFE_CLASSID) {
3528: PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3529: PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3530: PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3531: } else {
3532: PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3533: }
3534: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3535: /* For each fine grid cell */
3536: for (cell = cStart; cell < cEnd; ++cell) {
3537: Vec pointVec;
3538: PetscScalar *pV;
3539: PetscSF coarseCellSF = NULL;
3540: const PetscSFNode *coarseCells;
3541: PetscInt numCoarseCells, cpdim, q, c, j;
3542: PetscInt *findices, *cindices;
3543: PetscInt numFIndices, numCIndices;
3545: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3546: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3547: /* Get points from the quadrature */
3548: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3549: PetscCall(VecSetBlockSize(pointVec, dim));
3550: PetscCall(VecGetArray(pointVec, &pV));
3551: for (q = 0; q < Nq; ++q) {
3552: const PetscReal xi0[3] = {-1., -1., -1.};
3554: /* Transform point to real space */
3555: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3556: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3557: }
3558: PetscCall(VecRestoreArray(pointVec, &pV));
3559: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3560: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3561: /* Update matrix */
3562: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3563: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3564: PetscCall(VecGetArray(pointVec, &pV));
3565: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3566: PetscReal pVReal[3];
3567: const PetscReal xi0[3] = {-1., -1., -1.};
3569: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3570: /* Transform points from real space to coarse reference space */
3571: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3572: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3573: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3575: if (id == PETSCFE_CLASSID) {
3576: PetscFE fe = (PetscFE)obj;
3578: /* Evaluate coarse basis on contained point */
3579: PetscCall(PetscFEGetDimension(fe, &cpdim));
3580: PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3581: /* Get elemMat entries by multiplying by weight */
3582: for (i = 0; i < numFIndices; ++i) {
3583: PetscCall(PetscArrayzero(elemMat, cpdim));
3584: for (j = 0; j < cpdim; ++j) {
3585: 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;
3586: }
3587: /* Update interpolator */
3588: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3589: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3590: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3591: }
3592: } else {
3593: cpdim = 1;
3594: for (i = 0; i < numFIndices; ++i) {
3595: PetscCall(PetscArrayzero(elemMat, cpdim));
3596: for (j = 0; j < cpdim; ++j) {
3597: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3598: }
3599: /* Update interpolator */
3600: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3601: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Nq: %" PetscInt_FMT " %" PetscInt_FMT " Nf: %" PetscInt_FMT " %" PetscInt_FMT " Nc: %" PetscInt_FMT " %" PetscInt_FMT "\n", ccell, Nq, i, numFIndices, j, numCIndices));
3602: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3603: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3604: }
3605: }
3606: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3607: }
3608: PetscCall(VecRestoreArray(pointVec, &pV));
3609: PetscCall(PetscSFDestroy(&coarseCellSF));
3610: PetscCall(VecDestroy(&pointVec));
3611: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3612: }
3613: if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3614: }
3615: PetscCall(PetscFree3(v0, J, invJ));
3616: PetscCall(PetscFree3(v0c, Jc, invJc));
3617: PetscCall(PetscFree(elemMat));
3618: PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3619: PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3620: PetscFunctionReturn(PETSC_SUCCESS);
3621: }
3623: /*@
3624: DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
3626: Input Parameters:
3627: + dmc - The coarse mesh
3628: . dmf - The fine mesh
3629: - ctx - The application context
3631: Output Parameter:
3632: . sc - The mapping
3634: Level: developer
3636: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3637: @*/
3638: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, PetscCtx ctx)
3639: {
3640: PetscDS prob;
3641: PetscFE *feRef;
3642: PetscFV *fvRef;
3643: Vec fv, cv;
3644: IS fis, cis;
3645: PetscSection fsection, fglobalSection, csection, cglobalSection;
3646: PetscInt *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3647: PetscInt cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3648: PetscBool *needAvg;
3650: PetscFunctionBegin;
3651: PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3652: PetscCall(DMGetDimension(dmf, &dim));
3653: PetscCall(DMGetLocalSection(dmf, &fsection));
3654: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3655: PetscCall(DMGetLocalSection(dmc, &csection));
3656: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3657: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3658: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3659: PetscCall(DMGetDS(dmc, &prob));
3660: PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3661: for (f = 0; f < Nf; ++f) {
3662: PetscObject obj;
3663: PetscClassId id;
3664: PetscInt fNb = 0, Nc = 0;
3666: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3667: PetscCall(PetscObjectGetClassId(obj, &id));
3668: if (id == PETSCFE_CLASSID) {
3669: PetscFE fe = (PetscFE)obj;
3670: PetscSpace sp;
3671: PetscInt maxDegree;
3673: PetscCall(PetscFERefine(fe, &feRef[f]));
3674: PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3675: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3676: PetscCall(PetscFEGetBasisSpace(fe, &sp));
3677: PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3678: if (!maxDegree) needAvg[f] = PETSC_TRUE;
3679: } else if (id == PETSCFV_CLASSID) {
3680: PetscFV fv = (PetscFV)obj;
3681: PetscDualSpace Q;
3683: PetscCall(PetscFVRefine(fv, &fvRef[f]));
3684: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3685: PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3686: PetscCall(PetscFVGetNumComponents(fv, &Nc));
3687: needAvg[f] = PETSC_TRUE;
3688: }
3689: fTotDim += fNb;
3690: }
3691: PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3692: PetscCall(PetscMalloc1(cTotDim, &cmap));
3693: for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3694: PetscFE feC;
3695: PetscFV fvC;
3696: PetscDualSpace QF, QC;
3697: PetscInt order = -1, NcF, NcC, fpdim, cpdim;
3699: if (feRef[field]) {
3700: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3701: PetscCall(PetscFEGetNumComponents(feC, &NcC));
3702: PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3703: PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3704: PetscCall(PetscDualSpaceGetOrder(QF, &order));
3705: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3706: PetscCall(PetscFEGetDualSpace(feC, &QC));
3707: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3708: } else {
3709: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3710: PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3711: PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3712: PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3713: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3714: PetscCall(PetscFVGetDualSpace(fvC, &QC));
3715: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3716: }
3717: PetscCheck(NcF == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, NcF, NcC);
3718: for (c = 0; c < cpdim; ++c) {
3719: PetscQuadrature cfunc;
3720: const PetscReal *cqpoints, *cqweights;
3721: PetscInt NqcC, NpC;
3722: PetscBool found = PETSC_FALSE;
3724: PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3725: PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3726: PetscCheck(NqcC == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcC, NcC);
3727: PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3728: for (f = 0; f < fpdim; ++f) {
3729: PetscQuadrature ffunc;
3730: const PetscReal *fqpoints, *fqweights;
3731: PetscReal sum = 0.0;
3732: PetscInt NqcF, NpF;
3734: PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3735: PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3736: PetscCheck(NqcF == NcF, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcF, NcF);
3737: if (NpC != NpF) continue;
3738: for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3739: if (sum > 1.0e-9) continue;
3740: for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3741: if (sum < 1.0e-9) continue;
3742: cmap[offsetC + c] = offsetF + f;
3743: found = PETSC_TRUE;
3744: break;
3745: }
3746: if (!found) {
3747: /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3748: PetscCheck(fvRef[field] || (feRef[field] && order == 0), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3749: cmap[offsetC + c] = offsetF + 0;
3750: }
3751: }
3752: offsetC += cpdim;
3753: offsetF += fpdim;
3754: }
3755: for (f = 0; f < Nf; ++f) {
3756: PetscCall(PetscFEDestroy(&feRef[f]));
3757: PetscCall(PetscFVDestroy(&fvRef[f]));
3758: }
3759: PetscCall(PetscFree3(feRef, fvRef, needAvg));
3761: PetscCall(DMGetGlobalVector(dmf, &fv));
3762: PetscCall(DMGetGlobalVector(dmc, &cv));
3763: PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3764: PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3765: PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3766: PetscCall(PetscMalloc1(m, &cindices));
3767: PetscCall(PetscMalloc1(m, &findices));
3768: for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3769: for (c = cStart; c < cEnd; ++c) {
3770: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3771: for (d = 0; d < cTotDim; ++d) {
3772: if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3773: PetscCheck(!(findices[cellCIndices[d] - startC] >= 0) || !(findices[cellCIndices[d] - startC] != cellFIndices[cmap[d]]), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Cell %" PetscInt_FMT " Coarse dof %" PetscInt_FMT " maps to both %" PetscInt_FMT " and %" PetscInt_FMT, c, cindices[cellCIndices[d] - startC], findices[cellCIndices[d] - startC], cellFIndices[cmap[d]]);
3774: cindices[cellCIndices[d] - startC] = cellCIndices[d];
3775: findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3776: }
3777: }
3778: PetscCall(PetscFree(cmap));
3779: PetscCall(PetscFree2(cellCIndices, cellFIndices));
3781: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3782: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3783: PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3784: PetscCall(ISDestroy(&cis));
3785: PetscCall(ISDestroy(&fis));
3786: PetscCall(DMRestoreGlobalVector(dmf, &fv));
3787: PetscCall(DMRestoreGlobalVector(dmc, &cv));
3788: PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3789: PetscFunctionReturn(PETSC_SUCCESS);
3790: }
3792: /*@C
3793: DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3795: Input Parameters:
3796: + dm - The `DM`
3797: . cellIS - The cells to include
3798: . locX - A local vector with the solution fields
3799: . locX_t - A local vector with solution field time derivatives, or `NULL`
3800: - locA - A local vector with auxiliary fields, or `NULL`
3802: Output Parameters:
3803: + u - The field coefficients
3804: . u_t - The fields derivative coefficients
3805: - a - The auxiliary field coefficients
3807: Level: developer
3809: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3810: @*/
3811: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
3812: {
3813: DM plex, plexA = NULL;
3814: DMEnclosureType encAux;
3815: PetscSection section, sectionAux;
3816: PetscDS prob;
3817: const PetscInt *cells;
3818: PetscInt cStart, cEnd, numCells, totDim, totDimAux, c;
3820: PetscFunctionBegin;
3825: PetscAssertPointer(u, 6);
3826: PetscAssertPointer(u_t, 7);
3827: PetscAssertPointer(a, 8);
3828: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3829: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3830: PetscCall(DMGetLocalSection(dm, §ion));
3831: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3832: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3833: if (locA) {
3834: DM dmAux;
3835: PetscDS probAux;
3837: PetscCall(VecGetDM(locA, &dmAux));
3838: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3839: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3840: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3841: PetscCall(DMGetDS(dmAux, &probAux));
3842: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3843: }
3844: numCells = cEnd - cStart;
3845: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3846: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3847: else *u_t = NULL;
3848: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3849: else *a = NULL;
3850: for (c = cStart; c < cEnd; ++c) {
3851: const PetscInt cell = cells ? cells[c] : c;
3852: const PetscInt cind = c - cStart;
3853: PetscScalar *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3854: PetscInt i;
3856: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3857: for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3858: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3859: if (locX_t) {
3860: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3861: for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3862: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3863: }
3864: if (locA) {
3865: PetscInt subcell;
3866: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3867: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3868: for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3869: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3870: }
3871: }
3872: PetscCall(DMDestroy(&plex));
3873: if (locA) PetscCall(DMDestroy(&plexA));
3874: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3875: PetscFunctionReturn(PETSC_SUCCESS);
3876: }
3878: /*@C
3879: DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3881: Input Parameters:
3882: + dm - The `DM`
3883: . cellIS - The cells to include
3884: . locX - A local vector with the solution fields
3885: . locX_t - A local vector with solution field time derivatives, or `NULL`
3886: - locA - A local vector with auxiliary fields, or `NULL`
3888: Output Parameters:
3889: + u - The field coefficients
3890: . u_t - The fields derivative coefficients
3891: - a - The auxiliary field coefficients
3893: Level: developer
3895: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3896: @*/
3897: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
3898: {
3899: PetscFunctionBegin;
3900: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3901: if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3902: if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3903: PetscFunctionReturn(PETSC_SUCCESS);
3904: }
3906: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3907: {
3908: DM plex, plexA = NULL;
3909: DMEnclosureType encAux;
3910: PetscSection section, sectionAux;
3911: PetscDS ds, dsIn;
3912: const PetscInt *cells;
3913: PetscInt cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;
3915: PetscFunctionBegin;
3921: PetscAssertPointer(u, 6);
3922: PetscAssertPointer(u_t, 7);
3923: PetscAssertPointer(a, 8);
3924: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3925: numCells = cEnd - cStart;
3926: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3927: PetscCall(DMGetLocalSection(dm, §ion));
3928: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3929: PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3930: PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3931: if (locA) {
3932: DM dmAux;
3933: PetscDS probAux;
3935: PetscCall(VecGetDM(locA, &dmAux));
3936: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3937: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3938: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3939: PetscCall(DMGetDS(dmAux, &probAux));
3940: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3941: }
3942: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3943: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3944: else {
3945: *u_t = NULL;
3946: }
3947: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3948: else {
3949: *a = NULL;
3950: }
3951: // Loop over cohesive cells
3952: for (c = cStart; c < cEnd; ++c) {
3953: const PetscInt cell = cells ? cells[c] : c;
3954: const PetscInt cind = c - cStart;
3955: PetscScalar *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
3956: PetscScalar *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim);
3957: const PetscInt *cone, *ornt;
3958: PetscInt Nx = 0, Nxf, s;
3960: PetscCall(DMPlexGetCone(dm, cell, &cone));
3961: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3962: // Put in cohesive unknowns
3963: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3964: if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3965: for (f = 0; f < Nf; ++f) {
3966: PetscInt fdofIn, foff, foffIn;
3967: PetscBool cohesive;
3969: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3970: if (!cohesive) continue;
3971: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3972: PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3973: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3974: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3975: if (locX_t)
3976: for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3977: Nx += fdofIn;
3978: }
3979: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3980: if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3981: // Loop over sides of surface
3982: for (s = 0; s < 2; ++s) {
3983: const PetscInt *support;
3984: const PetscInt face = cone[s];
3985: PetscDS dsC;
3986: PetscInt ssize, ncell, Nxc;
3988: // I don't think I need the face to have 0 orientation in the hybrid cell
3989: //PetscCheck(!ornt[s], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", face, cell, ornt[s]);
3990: PetscCall(DMPlexGetSupport(dm, face, &support));
3991: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3992: if (support[0] == cell) ncell = support[1];
3993: else if (support[1] == cell) ncell = support[0];
3994: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3995: // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3996: PetscCall(DMGetCellDS(dm, ncell, &dsC, NULL));
3997: PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3998: if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3999: for (f = 0; f < Nf; ++f) {
4000: PetscInt fdofIn, foffIn, foff;
4001: PetscBool cohesive;
4003: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
4004: if (cohesive) continue;
4005: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
4006: PetscCall(PetscDSGetFieldOffset(dsC, f, &foff));
4007: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
4008: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foff + i];
4009: if (locX_t)
4010: for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foff + i];
4011: Nx += fdofIn;
4012: }
4013: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
4014: if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
4015: }
4016: PetscCheck(Nx == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for cell %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, cell, totDim);
4018: if (locA) {
4019: PetscScalar *al = &(*a)[cind * totDimAux];
4020: PetscInt subcell;
4022: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
4023: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
4024: PetscCheck(Nx == totDimAux, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subcell %" PetscInt_FMT "does not match DS size %" PetscInt_FMT, Nx, subcell, totDimAux);
4025: for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
4026: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
4027: }
4028: }
4029: PetscCall(DMDestroy(&plex));
4030: PetscCall(DMDestroy(&plexA));
4031: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4032: PetscFunctionReturn(PETSC_SUCCESS);
4033: }
4035: /*
4036: DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface
4038: Input Parameters:
4039: + dm - The full domain DM
4040: . dmX - An array of DM for the field, say an auxiliary DM, indexed by s
4041: . dsX - An array of PetscDS for the field, indexed by s
4042: . cellIS - The interface cells for which we want values
4043: . locX - An array of local vectors with the field values, indexed by s
4044: - useCell - Flag to have values come from neighboring cell rather than endcap face
4046: Output Parameter:
4047: . x - An array of field values, indexed by s
4049: Note:
4050: The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`.
4052: Level: advanced
4054: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
4055: */
4056: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
4057: {
4058: DM plexX[2];
4059: DMEnclosureType encX[2];
4060: PetscSection sectionX[2];
4061: const PetscInt *cells;
4062: PetscInt cStart, cEnd, numCells, c, s, totDimX[2];
4064: PetscFunctionBegin;
4065: PetscAssertPointer(locX, 5);
4066: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
4067: PetscAssertPointer(dmX, 2);
4068: PetscAssertPointer(dsX, 3);
4070: PetscAssertPointer(x, 7);
4071: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4072: numCells = cEnd - cStart;
4073: for (s = 0; s < 2; ++s) {
4077: PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
4078: PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
4079: PetscCall(DMGetLocalSection(dmX[s], §ionX[s]));
4080: PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
4081: PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
4082: }
4083: for (c = cStart; c < cEnd; ++c) {
4084: const PetscInt cell = cells ? cells[c] : c;
4085: const PetscInt cind = c - cStart;
4086: const PetscInt *cone, *ornt;
4088: PetscCall(DMPlexGetCone(dm, cell, &cone));
4089: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
4090: //PetscCheck(!ornt[0], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", cone[0], cell, ornt[0]);
4091: for (s = 0; s < 2; ++s) {
4092: const PetscInt tdX = totDimX[s];
4093: PetscScalar *closure = NULL, *xl = &x[s][cind * tdX];
4094: PetscInt face = cone[s], point = face, subpoint, Nx, i;
4096: if (useCell) {
4097: const PetscInt *support;
4098: PetscInt ssize;
4100: PetscCall(DMPlexGetSupport(dm, face, &support));
4101: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
4102: PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", face, cell, ssize);
4103: if (support[0] == cell) point = support[1];
4104: else if (support[1] == cell) point = support[0];
4105: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
4106: }
4107: PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
4108: PetscCall(DMPlexVecGetOrientedClosure(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
4109: PetscCheck(Nx == tdX, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subpoint %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, subpoint, tdX);
4110: for (i = 0; i < Nx; ++i) xl[i] = closure[i];
4111: PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
4112: }
4113: }
4114: for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
4115: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4116: PetscFunctionReturn(PETSC_SUCCESS);
4117: }
4119: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
4120: {
4121: PetscFunctionBegin;
4122: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
4123: PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
4124: PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
4125: PetscFunctionReturn(PETSC_SUCCESS);
4126: }
4128: /*@C
4129: DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
4131: Input Parameters:
4132: + dm - The `DM`
4133: . fStart - The first face to include
4134: . fEnd - The first face to exclude
4135: . locX - A local vector with the solution fields
4136: . locX_t - A local vector with solution field time derivatives, or `NULL`
4137: . faceGeometry - A local vector with face geometry
4138: . cellGeometry - A local vector with cell geometry
4139: - locGrad - A local vector with field gradients, or `NULL`
4141: Output Parameters:
4142: + Nface - The number of faces with field values
4143: . uL - The field values at the left side of the face
4144: - uR - The field values at the right side of the face
4146: Level: developer
4148: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4149: @*/
4150: PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, PeOp Vec locX_t, Vec faceGeometry, Vec cellGeometry, PeOp Vec locGrad, PetscInt *Nface, PetscScalar *uL[], PetscScalar *uR[])
4151: {
4152: DM dmFace, dmCell, dmGrad = NULL;
4153: PetscSection section;
4154: PetscDS prob;
4155: DMLabel ghostLabel;
4156: const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
4157: PetscBool *isFE;
4158: PetscInt dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
4160: PetscFunctionBegin;
4167: PetscAssertPointer(uL, 10);
4168: PetscAssertPointer(uR, 11);
4169: PetscCall(DMGetDimension(dm, &dim));
4170: PetscCall(DMGetDS(dm, &prob));
4171: PetscCall(DMGetLocalSection(dm, §ion));
4172: PetscCall(PetscDSGetNumFields(prob, &Nf));
4173: PetscCall(PetscDSGetTotalComponents(prob, &Nc));
4174: PetscCall(PetscMalloc1(Nf, &isFE));
4175: for (f = 0; f < Nf; ++f) {
4176: PetscObject obj;
4177: PetscClassId id;
4179: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4180: PetscCall(PetscObjectGetClassId(obj, &id));
4181: if (id == PETSCFE_CLASSID) {
4182: isFE[f] = PETSC_TRUE;
4183: } else if (id == PETSCFV_CLASSID) {
4184: isFE[f] = PETSC_FALSE;
4185: } else {
4186: isFE[f] = PETSC_FALSE;
4187: }
4188: }
4189: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4190: PetscCall(VecGetArrayRead(locX, &x));
4191: PetscCall(VecGetDM(faceGeometry, &dmFace));
4192: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4193: PetscCall(VecGetDM(cellGeometry, &dmCell));
4194: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4195: if (locGrad) {
4196: PetscCall(VecGetDM(locGrad, &dmGrad));
4197: PetscCall(VecGetArrayRead(locGrad, &lgrad));
4198: }
4199: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
4200: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
4201: /* Right now just eat the extra work for FE (could make a cell loop) */
4202: for (face = fStart, iface = 0; face < fEnd; ++face) {
4203: const PetscInt *cells;
4204: PetscFVFaceGeom *fg;
4205: PetscFVCellGeom *cgL, *cgR;
4206: PetscScalar *xL, *xR, *gL, *gR;
4207: PetscScalar *uLl = *uL, *uRl = *uR;
4208: PetscInt ghost, nsupp, nchild;
4210: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4211: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4212: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4213: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4214: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4215: PetscCall(DMPlexGetSupport(dm, face, &cells));
4216: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4217: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4218: for (f = 0; f < Nf; ++f) {
4219: PetscInt off;
4221: PetscCall(PetscDSGetComponentOffset(prob, f, &off));
4222: if (isFE[f]) {
4223: const PetscInt *cone;
4224: PetscInt comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
4226: xL = xR = NULL;
4227: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4228: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, &xL));
4229: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, &xR));
4230: PetscCall(DMPlexGetCone(dm, cells[0], &cone));
4231: PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
4232: for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
4233: if (cone[faceLocL] == face) break;
4234: PetscCall(DMPlexGetCone(dm, cells[1], &cone));
4235: PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
4236: for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
4237: if (cone[faceLocR] == face) break;
4238: PetscCheck(faceLocL != coneSizeL || faceLocR != coneSizeR, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %" PetscInt_FMT " in cone of cell %" PetscInt_FMT " or cell %" PetscInt_FMT, face, cells[0], cells[1]);
4239: /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
4240: /* TODO: this is a hack that might not be right for nonconforming */
4241: if (faceLocL < coneSizeL) {
4242: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
4243: if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4244: else {
4245: for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
4246: }
4247: } else {
4248: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4249: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4250: for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
4251: }
4252: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, &xL));
4253: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, &xR));
4254: } else {
4255: PetscFV fv;
4256: PetscInt numComp, c;
4258: PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
4259: PetscCall(PetscFVGetNumComponents(fv, &numComp));
4260: PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
4261: PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
4262: if (dmGrad) {
4263: PetscReal dxL[3], dxR[3];
4265: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
4266: PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
4267: DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
4268: DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
4269: for (c = 0; c < numComp; ++c) {
4270: uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
4271: uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
4272: }
4273: } else {
4274: for (c = 0; c < numComp; ++c) {
4275: uLl[iface * Nc + off + c] = xL[c];
4276: uRl[iface * Nc + off + c] = xR[c];
4277: }
4278: }
4279: }
4280: }
4281: ++iface;
4282: }
4283: *Nface = iface;
4284: PetscCall(VecRestoreArrayRead(locX, &x));
4285: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4286: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4287: if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
4288: PetscCall(PetscFree(isFE));
4289: PetscFunctionReturn(PETSC_SUCCESS);
4290: }
4292: /*@C
4293: DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
4295: Input Parameters:
4296: + dm - The `DM`
4297: . fStart - The first face to include
4298: . fEnd - The first face to exclude
4299: . locX - A local vector with the solution fields
4300: . locX_t - A local vector with solution field time derivatives, or `NULL`
4301: . faceGeometry - A local vector with face geometry
4302: . cellGeometry - A local vector with cell geometry
4303: - locGrad - A local vector with field gradients, or `NULL`
4305: Output Parameters:
4306: + Nface - The number of faces with field values
4307: . uL - The field values at the left side of the face
4308: - uR - The field values at the right side of the face
4310: Level: developer
4312: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4313: @*/
4314: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, PeOp Vec locX_t, Vec faceGeometry, Vec cellGeometry, PeOp Vec locGrad, PetscInt *Nface, PetscScalar *uL[], PetscScalar *uR[])
4315: {
4316: PetscFunctionBegin;
4317: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
4318: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
4319: PetscFunctionReturn(PETSC_SUCCESS);
4320: }
4322: /*@C
4323: DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
4325: Input Parameters:
4326: + dm - The `DM`
4327: . fStart - The first face to include
4328: . fEnd - The first face to exclude
4329: . faceGeometry - A local vector with face geometry
4330: - cellGeometry - A local vector with cell geometry
4332: Output Parameters:
4333: + Nface - The number of faces with field values
4334: . fgeom - The face centroid and normals
4335: - vol - The cell volumes
4337: Level: developer
4339: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4340: @*/
4341: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
4342: {
4343: DM dmFace, dmCell;
4344: DMLabel ghostLabel;
4345: const PetscScalar *facegeom, *cellgeom;
4346: PetscInt dim, numFaces = fEnd - fStart, iface, face;
4348: PetscFunctionBegin;
4352: PetscAssertPointer(fgeom, 7);
4353: PetscAssertPointer(vol, 8);
4354: PetscCall(DMGetDimension(dm, &dim));
4355: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4356: PetscCall(VecGetDM(faceGeometry, &dmFace));
4357: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4358: PetscCall(VecGetDM(cellGeometry, &dmCell));
4359: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4360: PetscCall(PetscMalloc1(numFaces, fgeom));
4361: PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
4362: for (face = fStart, iface = 0; face < fEnd; ++face) {
4363: const PetscInt *cells;
4364: PetscFVFaceGeom *fg;
4365: PetscFVCellGeom *cgL, *cgR;
4366: PetscFVFaceGeom *fgeoml = *fgeom;
4367: PetscReal *voll = *vol;
4368: PetscInt ghost, d, nchild, nsupp;
4370: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4371: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4372: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4373: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4374: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4375: PetscCall(DMPlexGetSupport(dm, face, &cells));
4376: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4377: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4378: for (d = 0; d < dim; ++d) {
4379: fgeoml[iface].centroid[d] = fg->centroid[d];
4380: fgeoml[iface].normal[d] = fg->normal[d];
4381: }
4382: voll[iface * 2 + 0] = cgL->volume;
4383: voll[iface * 2 + 1] = cgR->volume;
4384: ++iface;
4385: }
4386: *Nface = iface;
4387: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4388: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4389: PetscFunctionReturn(PETSC_SUCCESS);
4390: }
4392: /*@C
4393: DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
4395: Input Parameters:
4396: + dm - The `DM`
4397: . fStart - The first face to include
4398: . fEnd - The first face to exclude
4399: . faceGeometry - A local vector with face geometry
4400: - cellGeometry - A local vector with cell geometry
4402: Output Parameters:
4403: + Nface - The number of faces with field values
4404: . fgeom - The face centroid and normals
4405: - vol - The cell volumes
4407: Level: developer
4409: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4410: @*/
4411: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
4412: {
4413: PetscFunctionBegin;
4414: PetscCall(PetscFree(*fgeom));
4415: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
4416: PetscFunctionReturn(PETSC_SUCCESS);
4417: }
4419: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
4420: {
4421: char composeStr[33] = {0};
4422: PetscObjectId id;
4423: PetscContainer container;
4425: PetscFunctionBegin;
4426: PetscCall(PetscObjectGetId((PetscObject)quad, &id));
4427: PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
4428: PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
4429: if (container) {
4430: PetscCall(PetscContainerGetPointer(container, geom));
4431: } else {
4432: PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, mode, geom));
4433: PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
4434: PetscCall(PetscContainerSetPointer(container, (void *)*geom));
4435: PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
4436: PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
4437: PetscCall(PetscContainerDestroy(&container));
4438: }
4439: PetscFunctionReturn(PETSC_SUCCESS);
4440: }
4442: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4443: {
4444: PetscFunctionBegin;
4445: *geom = NULL;
4446: PetscFunctionReturn(PETSC_SUCCESS);
4447: }
4449: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, PetscCtx ctx)
4450: {
4451: DM_Plex *mesh = (DM_Plex *)dm->data;
4452: const char *name = "Residual";
4453: DM dmAux = NULL;
4454: DMLabel ghostLabel = NULL;
4455: PetscDS prob = NULL;
4456: PetscDS probAux = NULL;
4457: PetscBool useFEM = PETSC_FALSE;
4458: PetscBool isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4459: DMField coordField = NULL;
4460: Vec locA;
4461: PetscScalar *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
4462: IS chunkIS;
4463: const PetscInt *cells;
4464: PetscInt cStart, cEnd, numCells;
4465: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
4466: PetscInt maxDegree = PETSC_INT_MAX;
4467: PetscFormKey key;
4468: PetscQuadrature affineQuad = NULL, *quads = NULL;
4469: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
4471: PetscFunctionBegin;
4472: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4473: /* FEM+FVM */
4474: /* 1: Get sizes from dm and dmAux */
4475: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4476: PetscCall(DMGetDS(dm, &prob));
4477: PetscCall(PetscDSGetNumFields(prob, &Nf));
4478: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4479: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4480: if (locA) {
4481: PetscCall(VecGetDM(locA, &dmAux));
4482: PetscCall(DMGetDS(dmAux, &probAux));
4483: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4484: }
4485: /* 2: Get geometric data */
4486: for (f = 0; f < Nf; ++f) {
4487: PetscObject obj;
4488: PetscClassId id;
4489: PetscBool fimp;
4491: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4492: if (isImplicit != fimp) continue;
4493: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4494: PetscCall(PetscObjectGetClassId(obj, &id));
4495: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4496: PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4497: }
4498: if (useFEM) {
4499: PetscCall(DMGetCoordinateField(dm, &coordField));
4500: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4501: if (maxDegree <= 1) {
4502: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4503: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
4504: } else {
4505: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4506: for (f = 0; f < Nf; ++f) {
4507: PetscObject obj;
4508: PetscClassId id;
4509: PetscBool fimp;
4511: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4512: if (isImplicit != fimp) continue;
4513: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4514: PetscCall(PetscObjectGetClassId(obj, &id));
4515: if (id == PETSCFE_CLASSID) {
4516: PetscFE fe = (PetscFE)obj;
4518: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4519: PetscCall(PetscObjectReference((PetscObject)quads[f]));
4520: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
4521: }
4522: }
4523: }
4524: }
4525: /* Loop over chunks */
4526: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4527: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4528: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4529: numCells = cEnd - cStart;
4530: numChunks = 1;
4531: cellChunkSize = numCells / numChunks;
4532: numChunks = PetscMin(1, numCells);
4533: key.label = NULL;
4534: key.value = 0;
4535: key.part = 0;
4536: for (chunk = 0; chunk < numChunks; ++chunk) {
4537: PetscScalar *elemVec, *fluxL = NULL, *fluxR = NULL;
4538: PetscReal *vol = NULL;
4539: PetscFVFaceGeom *fgeom = NULL;
4540: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4541: PetscInt numFaces = 0;
4543: /* Extract field coefficients */
4544: if (useFEM) {
4545: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4546: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4547: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4548: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4549: }
4550: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4551: /* Loop over fields */
4552: for (f = 0; f < Nf; ++f) {
4553: PetscObject obj;
4554: PetscClassId id;
4555: PetscBool fimp;
4556: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4558: key.field = f;
4559: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4560: if (isImplicit != fimp) continue;
4561: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4562: PetscCall(PetscObjectGetClassId(obj, &id));
4563: if (id == PETSCFE_CLASSID) {
4564: PetscFE fe = (PetscFE)obj;
4565: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
4566: PetscFEGeom *chunkGeom = NULL;
4567: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4568: PetscInt Nq, Nb;
4570: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4571: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4572: PetscCall(PetscFEGetDimension(fe, &Nb));
4573: blockSize = Nb;
4574: batchSize = numBlocks * blockSize;
4575: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4576: numChunks = numCells / (numBatches * batchSize);
4577: Ne = numChunks * numBatches * batchSize;
4578: Nr = numCells % (numBatches * batchSize);
4579: offset = numCells - Nr;
4580: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4581: /* 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) */
4582: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4583: PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4584: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4585: PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4586: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4587: } else if (id == PETSCFV_CLASSID) {
4588: PetscFV fv = (PetscFV)obj;
4590: Ne = numFaces;
4591: /* Riemann solve over faces (need fields at face centroids) */
4592: /* We need to evaluate FE fields at those coordinates */
4593: PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4594: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4595: }
4596: /* Loop over domain */
4597: if (useFEM) {
4598: /* Add elemVec to locX */
4599: for (c = cS; c < cE; ++c) {
4600: const PetscInt cell = cells ? cells[c] : c;
4601: const PetscInt cind = c - cStart;
4603: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4604: if (ghostLabel) {
4605: PetscInt ghostVal;
4607: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4608: if (ghostVal > 0) continue;
4609: }
4610: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4611: }
4612: }
4613: /* Handle time derivative */
4614: if (locX_t) {
4615: PetscScalar *x_t, *fa;
4617: PetscCall(VecGetArray(locF, &fa));
4618: PetscCall(VecGetArray(locX_t, &x_t));
4619: for (f = 0; f < Nf; ++f) {
4620: PetscFV fv;
4621: PetscObject obj;
4622: PetscClassId id;
4623: PetscInt pdim, d;
4625: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4626: PetscCall(PetscObjectGetClassId(obj, &id));
4627: if (id != PETSCFV_CLASSID) continue;
4628: fv = (PetscFV)obj;
4629: PetscCall(PetscFVGetNumComponents(fv, &pdim));
4630: for (c = cS; c < cE; ++c) {
4631: const PetscInt cell = cells ? cells[c] : c;
4632: PetscScalar *u_t, *r;
4634: if (ghostLabel) {
4635: PetscInt ghostVal;
4637: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4638: if (ghostVal > 0) continue;
4639: }
4640: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4641: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4642: for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4643: }
4644: }
4645: PetscCall(VecRestoreArray(locX_t, &x_t));
4646: PetscCall(VecRestoreArray(locF, &fa));
4647: }
4648: if (useFEM) {
4649: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4650: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4651: }
4652: }
4653: if (useFEM) PetscCall(ISDestroy(&chunkIS));
4654: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4655: /* TODO Could include boundary residual here (see DMPlexComputeResidualByKey) */
4656: if (useFEM) {
4657: if (maxDegree <= 1) {
4658: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4659: PetscCall(PetscQuadratureDestroy(&affineQuad));
4660: } else {
4661: for (f = 0; f < Nf; ++f) {
4662: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4663: PetscCall(PetscQuadratureDestroy(&quads[f]));
4664: }
4665: PetscCall(PetscFree2(quads, geoms));
4666: }
4667: }
4668: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4669: PetscFunctionReturn(PETSC_SUCCESS);
4670: }
4672: /*
4673: 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
4675: X - The local solution vector
4676: X_t - The local solution time derivative vector, or NULL
4677: */
4678: PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, PetscCtx ctx)
4679: {
4680: DM_Plex *mesh = (DM_Plex *)dm->data;
4681: const char *name = "Jacobian", *nameP = "JacobianPre";
4682: DM dmAux = NULL;
4683: PetscDS prob, probAux = NULL;
4684: PetscSection sectionAux = NULL;
4685: Vec A;
4686: DMField coordField;
4687: PetscFEGeom *cgeomFEM;
4688: PetscQuadrature qGeom = NULL;
4689: Mat J = Jac, JP = JacP;
4690: PetscScalar *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4691: PetscBool hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4692: const PetscInt *cells;
4693: PetscFormKey key;
4694: PetscInt Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
4696: PetscFunctionBegin;
4697: PetscCall(ISGetLocalSize(cellIS, &numCells));
4698: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4699: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4700: PetscCall(DMGetDS(dm, &prob));
4701: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4702: if (A) {
4703: PetscCall(VecGetDM(A, &dmAux));
4704: PetscCall(DMGetLocalSection(dmAux, §ionAux));
4705: PetscCall(DMGetDS(dmAux, &probAux));
4706: }
4707: /* Get flags */
4708: PetscCall(PetscDSGetNumFields(prob, &Nf));
4709: PetscCall(DMGetWorkArray(dm, Nf, MPI_C_BOOL, &isFE));
4710: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4711: PetscObject disc;
4712: PetscClassId id;
4713: PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4714: PetscCall(PetscObjectGetClassId(disc, &id));
4715: if (id == PETSCFE_CLASSID) {
4716: isFE[fieldI] = PETSC_TRUE;
4717: } else if (id == PETSCFV_CLASSID) {
4718: hasFV = PETSC_TRUE;
4719: isFE[fieldI] = PETSC_FALSE;
4720: }
4721: }
4722: PetscCall(PetscDSHasJacobian(prob, &hasJac));
4723: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4724: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4725: assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4726: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4727: if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4728: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4729: if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4730: /* Compute batch sizes */
4731: if (isFE[0]) {
4732: PetscFE fe;
4733: PetscQuadrature q;
4734: PetscInt numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
4736: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4737: PetscCall(PetscFEGetQuadrature(fe, &q));
4738: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4739: PetscCall(PetscFEGetDimension(fe, &Nb));
4740: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4741: blockSize = Nb * numQuadPoints;
4742: batchSize = numBlocks * blockSize;
4743: chunkSize = numBatches * batchSize;
4744: numChunks = numCells / chunkSize + numCells % chunkSize;
4745: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4746: } else {
4747: chunkSize = numCells;
4748: numChunks = 1;
4749: }
4750: /* Get work space */
4751: wsz = (((X ? 1 : 0) + (X_t ? 1 : 0)) * totDim + (dmAux ? 1 : 0) * totDimAux + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4752: PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4753: PetscCall(PetscArrayzero(work, wsz));
4754: off = 0;
4755: u = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4756: u_t = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4757: a = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4758: elemMat = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4759: elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4760: elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4761: PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4762: /* Setup geometry */
4763: PetscCall(DMGetCoordinateField(dm, &coordField));
4764: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4765: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4766: if (!qGeom) {
4767: PetscFE fe;
4769: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4770: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4771: PetscCall(PetscObjectReference((PetscObject)qGeom));
4772: }
4773: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
4774: /* Compute volume integrals */
4775: if (assembleJac) PetscCall(MatZeroEntries(J));
4776: PetscCall(MatZeroEntries(JP));
4777: key.label = NULL;
4778: key.value = 0;
4779: key.part = 0;
4780: for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4781: const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4782: PetscInt c;
4784: /* Extract values */
4785: for (c = 0; c < Ncell; ++c) {
4786: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4787: PetscScalar *x = NULL, *x_t = NULL;
4788: PetscInt i;
4790: if (X) {
4791: PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4792: for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4793: PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4794: }
4795: if (X_t) {
4796: PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4797: for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4798: PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4799: }
4800: if (dmAux) {
4801: PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4802: for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4803: PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4804: }
4805: }
4806: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4807: PetscFE fe;
4808: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4809: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4810: key.field = fieldI * Nf + fieldJ;
4811: if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4812: if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4813: if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4814: }
4815: /* For finite volume, add the identity */
4816: if (!isFE[fieldI]) {
4817: PetscFV fv;
4818: PetscInt eOffset = 0, Nc, fc, foff;
4820: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4821: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4822: PetscCall(PetscFVGetNumComponents(fv, &Nc));
4823: for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4824: for (fc = 0; fc < Nc; ++fc) {
4825: const PetscInt i = foff + fc;
4826: if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4827: if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4828: }
4829: }
4830: }
4831: }
4832: /* Add contribution from X_t */
4833: if (hasDyn) {
4834: for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4835: }
4836: /* Insert values into matrix */
4837: for (c = 0; c < Ncell; ++c) {
4838: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4839: if (mesh->printFEM > 1) {
4840: if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4841: if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4842: }
4843: if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4844: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4845: }
4846: }
4847: /* Cleanup */
4848: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4849: PetscCall(PetscQuadratureDestroy(&qGeom));
4850: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4851: PetscCall(DMRestoreWorkArray(dm, Nf, MPI_C_BOOL, &isFE));
4852: PetscCall(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));
4853: /* Compute boundary integrals */
4854: /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4855: /* Assemble matrix */
4856: if (assembleJac) {
4857: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4858: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4859: }
4860: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4861: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4862: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4863: PetscFunctionReturn(PETSC_SUCCESS);
4864: }
4866: /* FEM Assembly Function */
4868: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4869: {
4870: PetscBool isPlex;
4872: PetscFunctionBegin;
4873: PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4874: if (isPlex) {
4875: *plex = dm;
4876: PetscCall(PetscObjectReference((PetscObject)dm));
4877: } else {
4878: PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4879: if (!*plex) {
4880: PetscCall(DMConvert(dm, DMPLEX, plex));
4881: PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4882: } else {
4883: PetscCall(PetscObjectReference((PetscObject)*plex));
4884: }
4885: if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4886: }
4887: PetscFunctionReturn(PETSC_SUCCESS);
4888: }
4890: /*@
4891: DMPlexGetGeometryFVM - Return precomputed geometric data
4893: Collective
4895: Input Parameter:
4896: . dm - The `DM`
4898: Output Parameters:
4899: + facegeom - The values precomputed from face geometry
4900: . cellgeom - The values precomputed from cell geometry
4901: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell, or `NULL` if not needed
4903: Level: developer
4905: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4906: @*/
4907: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PeOp PetscReal *minRadius)
4908: {
4909: DM plex;
4911: PetscFunctionBegin;
4913: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4914: PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4915: if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4916: PetscCall(DMDestroy(&plex));
4917: PetscFunctionReturn(PETSC_SUCCESS);
4918: }
4920: /*@
4921: DMPlexGetGradientDM - Return gradient data layout
4923: Collective
4925: Input Parameters:
4926: + dm - The `DM`
4927: - fv - The `PetscFV`
4929: Output Parameter:
4930: . dmGrad - The layout for gradient values
4932: Level: developer
4934: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4935: @*/
4936: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4937: {
4938: DM plex;
4939: PetscBool computeGradients;
4941: PetscFunctionBegin;
4944: PetscAssertPointer(dmGrad, 3);
4945: PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4946: if (!computeGradients) {
4947: *dmGrad = NULL;
4948: PetscFunctionReturn(PETSC_SUCCESS);
4949: }
4950: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4951: PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4952: PetscCall(DMDestroy(&plex));
4953: PetscFunctionReturn(PETSC_SUCCESS);
4954: }
4956: /*@
4957: DMPlexComputeBdResidualSingleByKey - Compute the local boundary residual for terms matching the input key
4959: Not collective
4961: Input Parameters:
4962: + dm - The output `DM`
4963: . wf - The `PetscWeakForm` holding forms on this boundary
4964: . key - The `PetscFormKey` indicating what should be integrated
4965: . facetIS - The `IS` giving a set of faces to integrate over
4966: . locX - The local solution
4967: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
4968: . t - The time
4969: - coordField - The `DMField` object with coordinates for these faces
4971: Output Parameter:
4972: . locF - The local residual
4974: Level: developer
4976: .seealso: `DMPlexComputeBdResidualSingle()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
4977: @*/
4978: PetscErrorCode DMPlexComputeBdResidualSingleByKey(DM dm, PetscWeakForm wf, PetscFormKey key, IS facetIS, Vec locX, Vec locX_t, PetscReal t, DMField coordField, Vec locF)
4979: {
4980: DM_Plex *mesh = (DM_Plex *)dm->data;
4981: DM plex = NULL, plexA = NULL;
4982: const char *name = "BdResidual";
4983: DMEnclosureType encAux;
4984: PetscDS prob, probAux = NULL;
4985: PetscSection section, sectionAux = NULL;
4986: Vec locA = NULL;
4987: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4988: PetscInt totDim, totDimAux = 0;
4990: PetscFunctionBegin;
4991: PetscCall(DMConvert(dm, DMPLEX, &plex));
4992: PetscCall(DMGetLocalSection(dm, §ion));
4993: PetscCall(DMGetDS(dm, &prob));
4994: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4995: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4996: if (locA) {
4997: DM dmAux;
4999: PetscCall(VecGetDM(locA, &dmAux));
5000: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5001: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5002: PetscCall(DMGetDS(plexA, &probAux));
5003: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5004: PetscCall(DMGetLocalSection(plexA, §ionAux));
5005: }
5006: {
5007: PetscFEGeom *fgeom;
5008: PetscInt maxDegree;
5009: PetscQuadrature qGeom = NULL;
5010: IS pointIS;
5011: const PetscInt *points;
5012: PetscInt numFaces, face, Nq;
5014: PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
5015: if (!pointIS) goto end; /* No points with that id on this process */
5016: {
5017: IS isectIS;
5019: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
5020: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5021: PetscCall(ISDestroy(&pointIS));
5022: pointIS = isectIS;
5023: }
5024: PetscCall(ISGetLocalSize(pointIS, &numFaces));
5025: PetscCall(ISGetIndices(pointIS, &points));
5026: PetscCall(PetscMalloc4(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, numFaces * totDim, &elemVec, (locA ? (size_t)numFaces * totDimAux : 0), &a));
5027: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5028: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5029: if (!qGeom) {
5030: PetscFE fe;
5032: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
5033: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5034: PetscCall(PetscObjectReference((PetscObject)qGeom));
5035: }
5036: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5037: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
5038: for (face = 0; face < numFaces; ++face) {
5039: const PetscInt point = points[face], *support;
5040: PetscScalar *x = NULL;
5041: PetscInt i;
5043: PetscCall(DMPlexGetSupport(dm, point, &support));
5044: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5045: for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5046: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5047: if (locX_t) {
5048: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5049: for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5050: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5051: }
5052: if (locA) {
5053: PetscInt subp;
5055: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5056: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5057: for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5058: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5059: }
5060: }
5061: PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
5062: {
5063: PetscFE fe;
5064: PetscInt Nb;
5065: PetscFEGeom *chunkGeom = NULL;
5066: /* Conforming batches */
5067: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5068: /* Remainder */
5069: PetscInt Nr, offset;
5071: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
5072: PetscCall(PetscFEGetDimension(fe, &Nb));
5073: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5074: /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
5075: blockSize = Nb;
5076: batchSize = numBlocks * blockSize;
5077: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5078: numChunks = numFaces / (numBatches * batchSize);
5079: Ne = numChunks * numBatches * batchSize;
5080: Nr = numFaces % (numBatches * batchSize);
5081: offset = numFaces - Nr;
5082: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5083: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
5084: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
5085: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5086: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5087: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5088: }
5089: for (face = 0; face < numFaces; ++face) {
5090: const PetscInt point = points[face], *support;
5092: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, name, totDim, &elemVec[face * totDim]));
5093: PetscCall(DMPlexGetSupport(plex, point, &support));
5094: PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
5095: }
5096: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5097: PetscCall(PetscQuadratureDestroy(&qGeom));
5098: PetscCall(ISRestoreIndices(pointIS, &points));
5099: PetscCall(ISDestroy(&pointIS));
5100: PetscCall(PetscFree4(u, u_t, elemVec, a));
5101: }
5102: end:
5103: if (mesh->printFEM) {
5104: PetscSection s;
5105: Vec locFbc;
5106: PetscInt pStart, pEnd, maxDof;
5107: PetscScalar *zeroes;
5109: PetscCall(DMGetLocalSection(dm, &s));
5110: PetscCall(VecDuplicate(locF, &locFbc));
5111: PetscCall(VecCopy(locF, locFbc));
5112: PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
5113: PetscCall(PetscSectionGetMaxDof(s, &maxDof));
5114: PetscCall(PetscCalloc1(maxDof, &zeroes));
5115: for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES));
5116: PetscCall(PetscFree(zeroes));
5117: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5118: PetscCall(VecDestroy(&locFbc));
5119: }
5120: PetscCall(DMDestroy(&plex));
5121: PetscCall(DMDestroy(&plexA));
5122: PetscFunctionReturn(PETSC_SUCCESS);
5123: }
5125: /*@
5126: DMPlexComputeBdResidualSingle - Compute the local boundary residual
5128: Not collective
5130: Input Parameters:
5131: + dm - The output `DM`
5132: . wf - The `PetscWeakForm` holding forms on this boundary
5133: . key - The `PetscFormKey` indicating what should be integrated
5134: . locX - The local solution
5135: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5136: - t - The time
5138: Output Parameter:
5139: . locF - The local residual
5141: Level: developer
5143: .seealso: `DMPlexComputeBdResidualSingleByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5144: @*/
5145: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, PetscReal t, Vec locF)
5146: {
5147: DMField coordField;
5148: DMLabel depthLabel;
5149: IS facetIS;
5150: PetscInt dim;
5152: PetscFunctionBegin;
5153: PetscCall(DMGetDimension(dm, &dim));
5154: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5155: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5156: PetscCall(DMGetCoordinateField(dm, &coordField));
5157: PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
5158: PetscCall(ISDestroy(&facetIS));
5159: PetscFunctionReturn(PETSC_SUCCESS);
5160: }
5162: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
5163: {
5164: PetscDS prob;
5165: PetscInt numBd, bd;
5166: DMField coordField = NULL;
5167: IS facetIS = NULL;
5168: DMLabel depthLabel;
5169: PetscInt dim;
5171: PetscFunctionBegin;
5172: PetscCall(DMGetDS(dm, &prob));
5173: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5174: PetscCall(DMGetDimension(dm, &dim));
5175: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5176: /* Filter out ghost facets (SF leaves) so that boundary residual contributions
5177: from shared facets are only assembled on the owning rank. Without this,
5178: internal boundary natural BCs at partition junctions get double-counted
5179: because LocalToGlobal with ADD_VALUES sums contributions from all ranks. */
5180: if (facetIS) {
5181: PetscSF sf;
5182: PetscInt nleaves;
5183: const PetscInt *leaves;
5185: PetscCall(DMGetPointSF(dm, &sf));
5186: PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
5187: if (nleaves > 0 && leaves) {
5188: IS leafIS, ownedFacetIS;
5190: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nleaves, leaves, PETSC_USE_POINTER, &leafIS));
5191: PetscCall(ISDifference(facetIS, leafIS, &ownedFacetIS));
5192: PetscCall(ISDestroy(&leafIS));
5193: PetscCall(ISDestroy(&facetIS));
5194: facetIS = ownedFacetIS;
5195: }
5196: }
5197: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5198: for (bd = 0; bd < numBd; ++bd) {
5199: PetscWeakForm wf;
5200: DMBoundaryConditionType type;
5201: DMLabel label;
5202: const PetscInt *values;
5203: PetscInt field, numValues, v;
5204: PetscObject obj;
5205: PetscClassId id;
5206: PetscFormKey key;
5208: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
5209: if (type & DM_BC_ESSENTIAL) continue;
5210: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
5211: PetscCall(PetscObjectGetClassId(obj, &id));
5212: if (id != PETSCFE_CLASSID) continue;
5213: if (!facetIS) {
5214: DMLabel depthLabel;
5215: PetscInt dim;
5217: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5218: PetscCall(DMGetDimension(dm, &dim));
5219: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5220: }
5221: PetscCall(DMGetCoordinateField(dm, &coordField));
5222: for (v = 0; v < numValues; ++v) {
5223: key.label = label;
5224: key.value = values[v];
5225: key.field = field;
5226: key.part = 0;
5227: PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
5228: }
5229: }
5230: PetscCall(ISDestroy(&facetIS));
5231: PetscFunctionReturn(PETSC_SUCCESS);
5232: }
5234: /*@
5235: DMPlexComputeResidualByKey - Compute the local residual for terms matching the input key
5237: Collective
5239: Input Parameters:
5240: + dm - The output `DM`
5241: . key - The `PetscFormKey` indicating what should be integrated
5242: . cellIS - The `IS` giving a set of cells to integrate over
5243: . time - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5244: . locX - The local solution
5245: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5246: . t - The time
5247: - ctx - An optional application context, passed to the pointwise functions
5249: Output Parameter:
5250: . locF - The local residual
5252: Level: developer
5254: .seealso: `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5255: @*/
5256: PetscErrorCode DMPlexComputeResidualByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
5257: {
5258: DM_Plex *mesh = (DM_Plex *)dm->data;
5259: const char *name = "Residual";
5260: DM dmAux = NULL;
5261: DM dmGrad = NULL;
5262: DMLabel ghostLabel = NULL;
5263: PetscDS ds = NULL;
5264: PetscDS dsAux = NULL;
5265: PetscSection section = NULL;
5266: PetscBool useFEM = PETSC_FALSE;
5267: PetscBool useFVM = PETSC_FALSE;
5268: PetscBool isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
5269: PetscFV fvm = NULL;
5270: DMField coordField = NULL;
5271: Vec locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
5272: PetscScalar *u = NULL, *u_t, *a, *uL, *uR;
5273: IS chunkIS;
5274: const PetscInt *cells;
5275: PetscInt cStart, cEnd, numCells;
5276: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
5277: PetscInt maxDegree = PETSC_INT_MAX;
5278: PetscQuadrature affineQuad = NULL, *quads = NULL;
5279: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
5281: PetscFunctionBegin;
5282: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5283: if (!cellIS) goto end;
5284: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5285: if (cStart >= cEnd) goto end;
5286: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5287: /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
5288: /* FEM+FVM */
5289: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
5290: /* 1: Get sizes from dm and dmAux */
5291: PetscCall(DMGetLocalSection(dm, §ion));
5292: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5293: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
5294: PetscCall(PetscDSGetNumFields(ds, &Nf));
5295: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5296: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
5297: if (locA) {
5298: PetscInt subcell;
5299: PetscCall(VecGetDM(locA, &dmAux));
5300: PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
5301: PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
5302: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5303: }
5304: /* 2: Get geometric data */
5305: for (f = 0; f < Nf; ++f) {
5306: PetscObject obj;
5307: PetscClassId id;
5308: PetscBool fimp;
5310: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5311: if (isImplicit != fimp) continue;
5312: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5313: PetscCall(PetscObjectGetClassId(obj, &id));
5314: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
5315: if (id == PETSCFV_CLASSID) {
5316: useFVM = PETSC_TRUE;
5317: fvm = (PetscFV)obj;
5318: }
5319: }
5320: if (useFEM) {
5321: PetscCall(DMGetCoordinateField(dm, &coordField));
5322: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5323: if (maxDegree <= 1) {
5324: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
5325: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
5326: } else {
5327: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5328: for (f = 0; f < Nf; ++f) {
5329: PetscObject obj;
5330: PetscClassId id;
5331: PetscBool fimp;
5333: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5334: if (isImplicit != fimp) continue;
5335: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5336: PetscCall(PetscObjectGetClassId(obj, &id));
5337: if (id == PETSCFE_CLASSID) {
5338: PetscFE fe = (PetscFE)obj;
5340: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5341: PetscCall(PetscObjectReference((PetscObject)quads[f]));
5342: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
5343: }
5344: }
5345: }
5346: }
5347: // Handle non-essential (e.g. outflow) boundary values
5348: if (useFVM) {
5349: PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad));
5350: PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
5351: PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
5352: }
5353: /* Loop over chunks */
5354: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
5355: numCells = cEnd - cStart;
5356: numChunks = 1;
5357: cellChunkSize = numCells / numChunks;
5358: faceChunkSize = (fEnd - fStart) / numChunks;
5359: numChunks = PetscMin(1, numCells);
5360: for (chunk = 0; chunk < numChunks; ++chunk) {
5361: PetscScalar *elemVec, *fluxL, *fluxR;
5362: PetscReal *vol;
5363: PetscFVFaceGeom *fgeom;
5364: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5365: PetscInt fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;
5367: /* Extract field coefficients */
5368: if (useFEM) {
5369: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
5370: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5371: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5372: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
5373: }
5374: if (useFVM) {
5375: PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5376: PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5377: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5378: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5379: PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
5380: PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
5381: }
5382: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
5383: /* Loop over fields */
5384: for (f = 0; f < Nf; ++f) {
5385: PetscObject obj;
5386: PetscClassId id;
5387: PetscBool fimp;
5388: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
5390: key.field = f;
5391: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5392: if (isImplicit != fimp) continue;
5393: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5394: PetscCall(PetscObjectGetClassId(obj, &id));
5395: if (id == PETSCFE_CLASSID) {
5396: PetscFE fe = (PetscFE)obj;
5397: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
5398: PetscFEGeom *chunkGeom = NULL;
5399: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5400: PetscInt Nq, Nb;
5402: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5403: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5404: PetscCall(PetscFEGetDimension(fe, &Nb));
5405: blockSize = Nb;
5406: batchSize = numBlocks * blockSize;
5407: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5408: numChunks = numCells / (numBatches * batchSize);
5409: Ne = numChunks * numBatches * batchSize;
5410: Nr = numCells % (numBatches * batchSize);
5411: offset = numCells - Nr;
5412: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
5413: /* 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) */
5414: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5415: PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
5416: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
5417: PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5418: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
5419: } else if (id == PETSCFV_CLASSID) {
5420: PetscFV fv = (PetscFV)obj;
5422: Ne = numFaces;
5423: /* Riemann solve over faces (need fields at face centroids) */
5424: /* We need to evaluate FE fields at those coordinates */
5425: PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
5426: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
5427: }
5428: /* Loop over domain */
5429: if (useFEM) {
5430: /* Add elemVec to locX */
5431: for (c = cS; c < cE; ++c) {
5432: const PetscInt cell = cells ? cells[c] : c;
5433: const PetscInt cind = c - cStart;
5435: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
5436: if (ghostLabel) {
5437: PetscInt ghostVal;
5439: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5440: if (ghostVal > 0) continue;
5441: }
5442: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5443: }
5444: }
5445: if (useFVM) {
5446: PetscScalar *fa;
5447: PetscInt iface;
5449: PetscCall(VecGetArray(locF, &fa));
5450: for (f = 0; f < Nf; ++f) {
5451: PetscFV fv;
5452: PetscObject obj;
5453: PetscClassId id;
5454: PetscInt cdim, foff, pdim;
5456: PetscCall(DMGetCoordinateDim(dm, &cdim));
5457: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5458: PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
5459: PetscCall(PetscObjectGetClassId(obj, &id));
5460: if (id != PETSCFV_CLASSID) continue;
5461: fv = (PetscFV)obj;
5462: PetscCall(PetscFVGetNumComponents(fv, &pdim));
5463: /* Accumulate fluxes to cells */
5464: for (face = fS, iface = 0; face < fE; ++face) {
5465: const PetscInt *scells;
5466: PetscScalar *fL = NULL, *fR = NULL;
5467: PetscInt ghost, d, nsupp, nchild;
5469: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
5470: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
5471: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
5472: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
5473: PetscCall(DMPlexGetSupport(dm, face, &scells));
5474: PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
5475: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
5476: PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
5477: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
5478: if (mesh->printFVM > 1) {
5479: PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal));
5480: PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff]));
5481: PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff]));
5482: PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff]));
5483: PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff]));
5484: }
5485: for (d = 0; d < pdim; ++d) {
5486: if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
5487: if (fR) fR[d] += fluxR[iface * totDim + foff + d];
5488: }
5489: ++iface;
5490: }
5491: }
5492: PetscCall(VecRestoreArray(locF, &fa));
5493: }
5494: /* Handle time derivative */
5495: if (locX_t) {
5496: PetscScalar *x_t, *fa;
5498: PetscCall(VecGetArray(locF, &fa));
5499: PetscCall(VecGetArray(locX_t, &x_t));
5500: for (f = 0; f < Nf; ++f) {
5501: PetscFV fv;
5502: PetscObject obj;
5503: PetscClassId id;
5504: PetscInt pdim, d;
5506: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5507: PetscCall(PetscObjectGetClassId(obj, &id));
5508: if (id != PETSCFV_CLASSID) continue;
5509: fv = (PetscFV)obj;
5510: PetscCall(PetscFVGetNumComponents(fv, &pdim));
5511: for (c = cS; c < cE; ++c) {
5512: const PetscInt cell = cells ? cells[c] : c;
5513: PetscScalar *u_t, *r;
5515: if (ghostLabel) {
5516: PetscInt ghostVal;
5518: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5519: if (ghostVal > 0) continue;
5520: }
5521: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
5522: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
5523: for (d = 0; d < pdim; ++d) r[d] += u_t[d];
5524: }
5525: }
5526: PetscCall(VecRestoreArray(locX_t, &x_t));
5527: PetscCall(VecRestoreArray(locF, &fa));
5528: }
5529: if (useFEM) {
5530: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5531: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5532: }
5533: if (useFVM) {
5534: PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5535: PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5536: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5537: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5538: if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
5539: }
5540: }
5541: if (useFEM) PetscCall(ISDestroy(&chunkIS));
5542: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5544: if (useFEM) {
5545: PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, ctx));
5547: if (maxDegree <= 1) {
5548: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5549: PetscCall(PetscQuadratureDestroy(&affineQuad));
5550: } else {
5551: for (f = 0; f < Nf; ++f) {
5552: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5553: PetscCall(PetscQuadratureDestroy(&quads[f]));
5554: }
5555: PetscCall(PetscFree2(quads, geoms));
5556: }
5557: }
5559: /* FEM */
5560: /* 1: Get sizes from dm and dmAux */
5561: /* 2: Get geometric data */
5562: /* 3: Handle boundary values */
5563: /* 4: Loop over domain */
5564: /* Extract coefficients */
5565: /* Loop over fields */
5566: /* Set tiling for FE*/
5567: /* Integrate FE residual to get elemVec */
5568: /* Loop over subdomain */
5569: /* Loop over quad points */
5570: /* Transform coords to real space */
5571: /* Evaluate field and aux fields at point */
5572: /* Evaluate residual at point */
5573: /* Transform residual to real space */
5574: /* Add residual to elemVec */
5575: /* Loop over domain */
5576: /* Add elemVec to locX */
5578: /* FVM */
5579: /* Get geometric data */
5580: /* If using gradients */
5581: /* Compute gradient data */
5582: /* Loop over domain faces */
5583: /* Count computational faces */
5584: /* Reconstruct cell gradient */
5585: /* Loop over domain cells */
5586: /* Limit cell gradients */
5587: /* Handle boundary values */
5588: /* Loop over domain faces */
5589: /* Read out field, centroid, normal, volume for each side of face */
5590: /* Riemann solve over faces */
5591: /* Loop over domain faces */
5592: /* Accumulate fluxes to cells */
5593: /* TODO Change printFEM to printDisc here */
5594: if (mesh->printFEM) {
5595: Vec locFbc;
5596: PetscInt pStart, pEnd, p, maxDof;
5597: PetscScalar *zeroes;
5599: PetscCall(VecDuplicate(locF, &locFbc));
5600: PetscCall(VecCopy(locF, locFbc));
5601: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5602: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5603: PetscCall(PetscCalloc1(maxDof, &zeroes));
5604: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5605: PetscCall(PetscFree(zeroes));
5606: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5607: PetscCall(VecDestroy(&locFbc));
5608: }
5609: end:
5610: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5611: PetscFunctionReturn(PETSC_SUCCESS);
5612: }
5614: /*@
5615: DMPlexComputeResidualHybridByKey - Compute the local residual over hybrid cells for terms matching the input key
5617: Collective
5619: Input Parameters:
5620: + dm - The output `DM`
5621: . key - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
5622: . cellIS - The `IS` give a set of cells to integrate over
5623: . time - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5624: . locX - The local solution
5625: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5626: . t - The time
5627: - ctx - An optional application context, passed to the pointwise functions
5629: Output Parameter:
5630: . locF - The local residual
5632: Level: developer
5634: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5635: @*/
5636: PetscErrorCode DMPlexComputeResidualHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
5637: {
5638: DM_Plex *mesh = (DM_Plex *)dm->data;
5639: const char *name = "Hybrid Residual";
5640: DM dmAux[3] = {NULL, NULL, NULL};
5641: DMLabel ghostLabel = NULL;
5642: PetscDS ds = NULL;
5643: PetscDS dsIn = NULL;
5644: PetscDS dsAux[3] = {NULL, NULL, NULL};
5645: Vec locA[3] = {NULL, NULL, NULL};
5646: DM dmScale[3] = {NULL, NULL, NULL};
5647: PetscDS dsScale[3] = {NULL, NULL, NULL};
5648: Vec locS[3] = {NULL, NULL, NULL};
5649: PetscSection section = NULL;
5650: DMField coordField = NULL;
5651: PetscScalar *a[3] = {NULL, NULL, NULL};
5652: PetscScalar *s[3] = {NULL, NULL, NULL};
5653: PetscScalar *u = NULL, *u_t;
5654: PetscScalar *elemVecNeg, *elemVecPos, *elemVecCoh;
5655: IS chunkISF, chunkISN;
5656: const PetscInt *cells;
5657: PetscInt *faces, *neighbors;
5658: PetscInt cStart, cEnd, numCells;
5659: PetscInt Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5660: PetscInt maxDegree = PETSC_INT_MAX;
5661: PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
5662: PetscFEGeom *affineGeomF = NULL, **geomsF = NULL;
5663: PetscQuadrature affineQuadN = NULL, *quadsN = NULL;
5664: PetscFEGeom *affineGeomN = NULL, **geomsN = NULL;
5666: PetscFunctionBegin;
5667: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5668: if (!cellIS) goto end;
5669: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5670: PetscCall(ISGetLocalSize(cellIS, &numCells));
5671: if (cStart >= cEnd) goto end;
5672: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5673: const char *name;
5674: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5675: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Form keys for each side of a cohesive surface must be different (%s, %" PetscInt_FMT ", %" PetscInt_FMT ")", name, key[0].value, key[0].part);
5676: }
5677: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5678: /* FEM */
5679: /* 1: Get sizes from dm and dmAux */
5680: PetscCall(DMGetLocalSection(dm, §ion));
5681: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5682: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5683: PetscCall(PetscDSGetNumFields(ds, &Nf));
5684: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5685: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5686: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5687: if (locA[2]) {
5688: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5690: PetscCall(VecGetDM(locA[2], &dmAux[2]));
5691: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5692: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5693: {
5694: const PetscInt *cone;
5695: PetscInt c;
5697: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5698: for (c = 0; c < 2; ++c) {
5699: const PetscInt *support;
5700: PetscInt ssize, s;
5702: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5703: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5704: PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
5705: if (support[0] == cellStart) s = 1;
5706: else if (support[1] == cellStart) s = 0;
5707: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5708: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5709: PetscCheck(locA[c], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must have auxiliary vector for (%p, %" PetscInt_FMT ", %" PetscInt_FMT ")", (void *)key[c].label, key[c].value, key[c].part);
5710: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5711: else dmAux[c] = dmAux[2];
5712: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5713: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5714: }
5715: }
5716: }
5717: /* Handle mass matrix scaling
5718: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5719: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5720: if (locS[2]) {
5721: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5722: PetscInt Nb, Nbs;
5724: PetscCall(VecGetDM(locS[2], &dmScale[2]));
5725: PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5726: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5727: // BRAD: This is not set correctly
5728: key[2].field = 2;
5729: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5730: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5731: PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs);
5732: {
5733: const PetscInt *cone;
5734: PetscInt c;
5736: locS[1] = locS[0] = locS[2];
5737: dmScale[1] = dmScale[0] = dmScale[2];
5738: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5739: for (c = 0; c < 2; ++c) {
5740: const PetscInt *support;
5741: PetscInt ssize, s;
5743: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5744: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5745: PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
5746: if (support[0] == cellStart) s = 1;
5747: else if (support[1] == cellStart) s = 0;
5748: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5749: PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5750: PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5751: }
5752: }
5753: }
5754: /* 2: Setup geometric data */
5755: PetscCall(DMGetCoordinateField(dm, &coordField));
5756: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5757: if (maxDegree > 1) {
5758: PetscCall(PetscCalloc4(Nf, &quadsF, Nf, &geomsF, Nf, &quadsN, Nf, &geomsN));
5759: for (f = 0; f < Nf; ++f) {
5760: PetscFE fe;
5761: PetscBool isCohesiveField;
5763: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5764: if (fe) {
5765: PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
5766: PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
5767: }
5768: PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fe));
5769: PetscCall(PetscDSGetCohesive(dsIn, f, &isCohesiveField));
5770: if (fe) {
5771: if (isCohesiveField) {
5772: for (PetscInt g = 0; g < Nf; ++g) {
5773: PetscCall(PetscDSGetDiscretization(dsIn, g, (PetscObject *)&fe));
5774: PetscCall(PetscDSGetCohesive(dsIn, g, &isCohesiveField));
5775: if (!isCohesiveField) break;
5776: }
5777: }
5778: PetscCall(PetscFEGetQuadrature(fe, &quadsN[f]));
5779: PetscCall(PetscObjectReference((PetscObject)quadsN[f]));
5780: }
5781: }
5782: }
5783: /* Loop over chunks */
5784: cellChunkSize = numCells;
5785: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5786: PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
5787: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
5788: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
5789: /* Extract field coefficients */
5790: /* NOTE This needs the end cap faces to have identical orientations */
5791: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5792: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5793: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5794: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5795: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5796: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5797: for (chunk = 0; chunk < numChunks; ++chunk) {
5798: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5800: PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5801: PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5802: PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5803: /* Get faces and neighbors */
5804: for (c = cS; c < cE; ++c) {
5805: const PetscInt cell = cells ? cells[c] : c;
5806: const PetscInt *cone, *support;
5807: PetscCall(DMPlexGetCone(dm, cell, &cone));
5808: faces[(c - cS) * 2 + 0] = cone[0];
5809: faces[(c - cS) * 2 + 1] = cone[1];
5810: PetscCall(DMPlexGetSupport(dm, cone[0], &support));
5811: neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
5812: PetscCall(DMPlexGetSupport(dm, cone[1], &support));
5813: neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
5814: }
5815: PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5816: PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
5817: /* Get geometric data */
5818: if (maxDegree <= 1) {
5819: if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
5820: if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
5821: if (!affineQuadN) {
5822: PetscInt dim;
5823: PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
5824: PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
5825: PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
5826: }
5827: if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
5828: } else {
5829: for (f = 0; f < Nf; ++f) {
5830: if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
5831: if (quadsN[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, quadsN[f], PETSC_FEGEOM_BASIC, &geomsN[f]));
5832: }
5833: }
5834: /* Loop over fields */
5835: for (f = 0; f < Nf; ++f) {
5836: PetscFE fe;
5837: PetscFEGeom *geomF = affineGeomF ? affineGeomF : geomsF[f];
5838: PetscFEGeom *chunkGeomF = NULL, *remGeomF = NULL;
5839: PetscFEGeom *geomN = affineGeomN ? affineGeomN : geomsN[f];
5840: PetscFEGeom *chunkGeomN = NULL, *remGeomN = NULL;
5841: PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[f];
5842: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5843: PetscBool isCohesiveField;
5845: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5846: if (!fe) continue;
5847: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5848: PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
5849: PetscCall(PetscFEGetDimension(fe, &Nb));
5850: blockSize = Nb;
5851: batchSize = numBlocks * blockSize;
5852: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5853: numChunks = numCells / (numBatches * batchSize);
5854: Ne = numChunks * numBatches * batchSize;
5855: Nr = numCells % (numBatches * batchSize);
5856: offset = numCells - Nr;
5857: PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
5858: PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
5859: PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
5860: PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
5861: PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5862: // TODO Do I need to set isCohesive on the chunks?
5863: key[0].field = f;
5864: key[1].field = f;
5865: key[2].field = f;
5866: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5867: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, &elemVecNeg[offset * totDim]));
5868: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, elemVecPos));
5869: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, &elemVecPos[offset * totDim]));
5870: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5871: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, &elemVecCoh[offset * totDim]));
5872: PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
5873: PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
5874: PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
5875: PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
5876: }
5877: /* Add elemVec to locX */
5878: for (c = cS; c < cE; ++c) {
5879: const PetscInt cell = cells ? cells[c] : c;
5880: const PetscInt cind = c - cStart;
5881: PetscInt i;
5883: /* Scale element values */
5884: if (locS[0]) {
5885: PetscInt Nb, off = cind * totDim, soff = cind * totDimScale[0];
5886: PetscBool cohesive;
5888: for (f = 0; f < Nf; ++f) {
5889: PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5890: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5891: if (f == key[2].field) {
5892: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5893: // No cohesive scaling field is currently input
5894: for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5895: off += Nb;
5896: } else {
5897: const PetscInt N = cohesive ? Nb : Nb * 2;
5899: for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5900: off += N;
5901: }
5902: }
5903: } else {
5904: for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5905: }
5906: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5907: if (ghostLabel) {
5908: PetscInt ghostVal;
5910: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5911: if (ghostVal > 0) continue;
5912: }
5913: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5914: }
5915: }
5916: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5917: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5918: PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5919: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5920: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5921: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5922: PetscCall(PetscFree2(faces, neighbors));
5923: PetscCall(ISDestroy(&chunkISF));
5924: PetscCall(ISDestroy(&chunkISN));
5925: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5926: if (maxDegree <= 1) {
5927: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
5928: PetscCall(PetscQuadratureDestroy(&affineQuadF));
5929: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
5930: PetscCall(PetscQuadratureDestroy(&affineQuadN));
5931: } else {
5932: for (f = 0; f < Nf; ++f) {
5933: if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
5934: if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
5935: if (geomsN) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsN[f], PETSC_FALSE, &geomsN[f]));
5936: if (quadsN) PetscCall(PetscQuadratureDestroy(&quadsN[f]));
5937: }
5938: PetscCall(PetscFree4(quadsF, geomsF, quadsN, geomsN));
5939: }
5940: if (mesh->printFEM) {
5941: Vec locFbc;
5942: PetscInt pStart, pEnd, p, maxDof;
5943: PetscScalar *zeroes;
5945: PetscCall(VecDuplicate(locF, &locFbc));
5946: PetscCall(VecCopy(locF, locFbc));
5947: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5948: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5949: PetscCall(PetscCalloc1(maxDof, &zeroes));
5950: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5951: PetscCall(PetscFree(zeroes));
5952: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5953: PetscCall(VecDestroy(&locFbc));
5954: }
5955: end:
5956: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5957: PetscFunctionReturn(PETSC_SUCCESS);
5958: }
5960: /*@
5961: DMPlexComputeBdJacobianSingleByLabel - Compute the local boundary Jacobian for terms matching the input label
5963: Not collective
5965: Input Parameters:
5966: + dm - The output `DM`
5967: . wf - The `PetscWeakForm` holding forms on this boundary
5968: . label - The `DMLabel` indicating what faces should be integrated over
5969: . numValues - The number of label values
5970: . values - The array of label values
5971: . fieldI - The test field for these integrals
5972: . facetIS - The `IS` giving the set of possible faces to integrate over (intersected with the label)
5973: . locX - The local solution
5974: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5975: . t - The time
5976: . coordField - The `DMField` object with coordinates for these faces
5977: - X_tShift - The multiplier for dF/dxdot
5979: Output Parameters:
5980: + Jac - The local Jacobian
5981: - JacP - The local Jacobian preconditioner
5983: Level: developer
5985: .seealso: `DMPlexComputeBdJacobianSingle()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5986: @*/
5987: PetscErrorCode DMPlexComputeBdJacobianSingleByLabel(DM dm, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, IS facetIS, Vec locX, Vec locX_t, PetscReal t, DMField coordField, PetscReal X_tShift, Mat Jac, Mat JacP)
5988: {
5989: DM_Plex *mesh = (DM_Plex *)dm->data;
5990: DM plex = NULL, plexA = NULL, tdm;
5991: DMEnclosureType encAux;
5992: PetscDS ds, dsAux = NULL;
5993: PetscSection section, sectionAux = NULL;
5994: PetscSection globalSection;
5995: Vec locA = NULL, tv;
5996: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
5997: PetscInt v;
5998: PetscInt Nf, totDim, totDimAux = 0;
5999: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;
6001: PetscFunctionBegin;
6002: PetscCall(DMHasBasisTransform(dm, &transform));
6003: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
6004: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
6005: PetscCall(DMGetLocalSection(dm, §ion));
6006: PetscCall(DMGetDS(dm, &ds));
6007: PetscCall(PetscDSGetNumFields(ds, &Nf));
6008: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
6009: PetscCall(PetscWeakFormHasBdJacobian(wf, &hasJac));
6010: PetscCall(PetscWeakFormHasBdJacobianPreconditioner(wf, &hasPrec));
6011: if (!hasJac && !hasPrec) PetscFunctionReturn(PETSC_SUCCESS);
6012: PetscCall(DMConvert(dm, DMPLEX, &plex));
6013: PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
6014: if (locA) {
6015: DM dmAux;
6017: PetscCall(VecGetDM(locA, &dmAux));
6018: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6019: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
6020: PetscCall(DMGetDS(plexA, &dsAux));
6021: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
6022: PetscCall(DMGetLocalSection(plexA, §ionAux));
6023: }
6025: PetscCall(DMGetGlobalSection(dm, &globalSection));
6026: for (v = 0; v < numValues; ++v) {
6027: PetscFEGeom *fgeom;
6028: PetscInt maxDegree;
6029: PetscQuadrature qGeom = NULL;
6030: IS pointIS;
6031: const PetscInt *points;
6032: PetscFormKey key;
6033: PetscInt numFaces, face, Nq;
6035: key.label = label;
6036: key.value = values[v];
6037: key.part = 0;
6038: PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
6039: if (!pointIS) continue; /* No points with that id on this process */
6040: {
6041: IS isectIS;
6043: /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
6044: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
6045: PetscCall(ISDestroy(&pointIS));
6046: pointIS = isectIS;
6047: }
6048: PetscCall(ISGetLocalSize(pointIS, &numFaces));
6049: PetscCall(ISGetIndices(pointIS, &points));
6050: PetscCall(PetscMalloc5(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, (hasJac ? (size_t)numFaces * totDim * totDim : 0), &elemMat, (hasPrec ? (size_t)numFaces * totDim * totDim : 0), &elemMatP, (locA ? (size_t)numFaces * totDimAux : 0), &a));
6051: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
6052: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
6053: if (!qGeom) {
6054: PetscFE fe;
6056: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
6057: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
6058: PetscCall(PetscObjectReference((PetscObject)qGeom));
6059: }
6060: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6061: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
6062: for (face = 0; face < numFaces; ++face) {
6063: const PetscInt point = points[face], *support;
6064: PetscScalar *x = NULL;
6065: PetscInt i;
6067: PetscCall(DMPlexGetSupport(dm, point, &support));
6068: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
6069: for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
6070: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
6071: if (locX_t) {
6072: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
6073: for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
6074: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
6075: }
6076: if (locA) {
6077: PetscInt subp;
6078: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
6079: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
6080: for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
6081: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
6082: }
6083: }
6084: if (elemMat) PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
6085: if (elemMatP) PetscCall(PetscArrayzero(elemMatP, numFaces * totDim * totDim));
6086: {
6087: PetscFE fe;
6088: PetscInt Nb;
6089: /* Conforming batches */
6090: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6091: /* Remainder */
6092: PetscFEGeom *chunkGeom = NULL;
6093: PetscInt fieldJ, Nr, offset;
6095: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
6096: PetscCall(PetscFEGetDimension(fe, &Nb));
6097: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6098: blockSize = Nb;
6099: batchSize = numBlocks * blockSize;
6100: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6101: numChunks = numFaces / (numBatches * batchSize);
6102: Ne = numChunks * numBatches * batchSize;
6103: Nr = numFaces % (numBatches * batchSize);
6104: offset = numFaces - Nr;
6105: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
6106: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6107: key.field = fieldI * Nf + fieldJ;
6108: if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
6109: if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
6110: }
6111: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
6112: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6113: key.field = fieldI * Nf + fieldJ;
6114: if (hasJac)
6115: PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
6116: if (hasPrec)
6117: PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
6118: }
6119: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
6120: }
6121: for (face = 0; face < numFaces; ++face) {
6122: const PetscInt point = points[face], *support;
6124: /* Transform to global basis before insertion in Jacobian */
6125: PetscCall(DMPlexGetSupport(plex, point, &support));
6126: if (hasJac && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
6127: if (hasPrec && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMatP[face * totDim * totDim]));
6128: if (hasPrec) {
6129: if (hasJac) {
6130: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
6131: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
6132: }
6133: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
6134: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
6135: } else {
6136: if (hasJac) {
6137: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
6138: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
6139: }
6140: }
6141: }
6142: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
6143: PetscCall(PetscQuadratureDestroy(&qGeom));
6144: PetscCall(ISRestoreIndices(pointIS, &points));
6145: PetscCall(ISDestroy(&pointIS));
6146: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
6147: }
6148: PetscCall(DMDestroy(&plex));
6149: PetscCall(DMDestroy(&plexA));
6150: PetscFunctionReturn(PETSC_SUCCESS);
6151: }
6153: /*@
6154: DMPlexComputeBdJacobianSingle - Compute the local boundary Jacobian
6156: Not collective
6158: Input Parameters:
6159: + dm - The output `DM`
6160: . wf - The `PetscWeakForm` holding forms on this boundary
6161: . label - The `DMLabel` indicating what faces should be integrated over
6162: . numValues - The number of label values
6163: . values - The array of label values
6164: . fieldI - The test field for these integrals
6165: . locX - The local solution
6166: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
6167: . t - The time
6168: - X_tShift - The multiplier for dF/dxdot
6170: Output Parameters:
6171: + Jac - The local Jacobian
6172: - JacP - The local Jacobian preconditioner
6174: Level: developer
6176: .seealso: `DMPlexComputeBdJacobianSingleByLabel()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
6177: @*/
6178: PetscErrorCode DMPlexComputeBdJacobianSingle(DM dm, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP)
6179: {
6180: DMField coordField;
6181: DMLabel depthLabel;
6182: IS facetIS;
6183: PetscInt dim;
6185: PetscFunctionBegin;
6186: PetscCall(DMGetDimension(dm, &dim));
6187: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6188: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
6189: PetscCall(DMGetCoordinateField(dm, &coordField));
6190: PetscCall(DMPlexComputeBdJacobianSingleByLabel(dm, wf, label, numValues, values, fieldI, facetIS, locX, locX_t, t, coordField, X_tShift, Jac, JacP));
6191: PetscCall(ISDestroy(&facetIS));
6192: PetscFunctionReturn(PETSC_SUCCESS);
6193: }
6195: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, PetscCtx ctx)
6196: {
6197: PetscDS prob;
6198: PetscInt dim, numBd, bd;
6199: DMLabel depthLabel;
6200: DMField coordField = NULL;
6201: IS facetIS;
6203: PetscFunctionBegin;
6204: PetscCall(DMGetDS(dm, &prob));
6205: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6206: PetscCall(DMGetDimension(dm, &dim));
6207: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
6208: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
6209: PetscCall(DMGetCoordinateField(dm, &coordField));
6210: for (bd = 0; bd < numBd; ++bd) {
6211: PetscWeakForm wf;
6212: DMBoundaryConditionType type;
6213: DMLabel label;
6214: const PetscInt *values;
6215: PetscInt fieldI, numValues;
6216: PetscObject obj;
6217: PetscClassId id;
6219: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
6220: if (type & DM_BC_ESSENTIAL) continue;
6221: PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
6222: PetscCall(PetscObjectGetClassId(obj, &id));
6223: if (id != PETSCFE_CLASSID) continue;
6224: PetscCall(DMPlexComputeBdJacobianSingleByLabel(dm, wf, label, numValues, values, fieldI, facetIS, locX, locX_t, t, coordField, X_tShift, Jac, JacP));
6225: }
6226: PetscCall(ISDestroy(&facetIS));
6227: PetscFunctionReturn(PETSC_SUCCESS);
6228: }
6230: /*@
6231: DMPlexComputeJacobianByKey - Compute the local Jacobian for terms matching the input key
6233: Collective
6235: Input Parameters:
6236: + dm - The output `DM`
6237: . key - The `PetscFormKey` indicating what should be integrated
6238: . cellIS - The `IS` give a set of cells to integrate over
6239: . t - The time
6240: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
6241: . locX - The local solution
6242: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
6243: - ctx - An optional application context, passed to the pointwise functions
6245: Output Parameters:
6246: + Jac - The local Jacobian
6247: - JacP - The local Jacobian preconditioner
6249: Level: developer
6251: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
6252: @*/
6253: PetscErrorCode DMPlexComputeJacobianByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
6254: {
6255: DM_Plex *mesh = (DM_Plex *)dm->data;
6256: const char *name = "Jacobian";
6257: DM dmAux = NULL, plex, tdm;
6258: DMEnclosureType encAux;
6259: Vec A, tv;
6260: DMField coordField;
6261: PetscDS prob, probAux = NULL;
6262: PetscSection section, globalSection, sectionAux;
6263: PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
6264: const PetscInt *cells;
6265: PetscInt Nf, fieldI, fieldJ;
6266: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6267: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
6269: PetscFunctionBegin;
6270: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6271: PetscCall(DMGetLocalSection(dm, §ion));
6272: PetscCall(DMGetGlobalSection(dm, &globalSection));
6273: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6274: if (A) {
6275: PetscCall(VecGetDM(A, &dmAux));
6276: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6277: PetscCall(DMConvert(dmAux, DMPLEX, &plex));
6278: PetscCall(DMGetLocalSection(plex, §ionAux));
6279: PetscCall(DMGetDS(dmAux, &probAux));
6280: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6281: }
6282: PetscCall(DMGetCoordinateField(dm, &coordField));
6283: if (!cellIS) goto end;
6284: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6285: PetscCall(ISGetLocalSize(cellIS, &numCells));
6286: if (cStart >= cEnd) goto end;
6287: PetscCall(DMHasBasisTransform(dm, &transform));
6288: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
6289: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
6290: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6291: PetscCall(PetscDSGetNumFields(prob, &Nf));
6292: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6293: PetscCall(PetscDSHasJacobian(prob, &hasJac));
6294: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
6295: /* user passed in the same matrix, avoid double contributions and
6296: only assemble the Jacobian */
6297: if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
6298: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6299: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6300: PetscCall(PetscMalloc5(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, (hasJac ? (size_t)numCells * totDim * totDim : 0), &elemMat, (hasPrec ? (size_t)numCells * totDim * totDim : 0), &elemMatP, (hasDyn ? (size_t)numCells * totDim * totDim : 0), &elemMatD));
6301: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6302: for (c = cStart; c < cEnd; ++c) {
6303: const PetscInt cell = cells ? cells[c] : c;
6304: const PetscInt cind = c - cStart;
6305: PetscScalar *x = NULL, *x_t = NULL;
6306: PetscInt i;
6308: PetscCall(DMPlexVecGetClosure(dm, section, locX, cell, NULL, &x));
6309: for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6310: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cell, NULL, &x));
6311: if (locX_t) {
6312: PetscCall(DMPlexVecGetClosure(dm, section, locX_t, cell, NULL, &x_t));
6313: for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6314: PetscCall(DMPlexVecRestoreClosure(dm, section, locX_t, cell, NULL, &x_t));
6315: }
6316: if (dmAux) {
6317: PetscInt subcell;
6318: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6319: PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
6320: for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6321: PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
6322: }
6323: }
6324: if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6325: if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
6326: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6327: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6328: PetscClassId id;
6329: PetscFE fe;
6330: PetscQuadrature qGeom = NULL;
6331: PetscInt Nb;
6332: /* Conforming batches */
6333: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6334: /* Remainder */
6335: PetscInt Nr, offset, Nq;
6336: PetscInt maxDegree;
6337: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6339: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6340: PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
6341: if (id == PETSCFV_CLASSID) {
6342: hasFV = PETSC_TRUE;
6343: continue;
6344: }
6345: PetscCall(PetscFEGetDimension(fe, &Nb));
6346: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6347: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6348: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6349: if (!qGeom) {
6350: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6351: PetscCall(PetscObjectReference((PetscObject)qGeom));
6352: }
6353: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6354: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
6355: blockSize = Nb;
6356: batchSize = numBlocks * blockSize;
6357: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6358: numChunks = numCells / (numBatches * batchSize);
6359: Ne = numChunks * numBatches * batchSize;
6360: Nr = numCells % (numBatches * batchSize);
6361: offset = numCells - Nr;
6362: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6363: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6364: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6365: key.field = fieldI * Nf + fieldJ;
6366: if (hasJac) {
6367: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6368: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
6369: }
6370: if (hasPrec) {
6371: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
6372: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
6373: }
6374: if (hasDyn) {
6375: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6376: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatD[offset * totDim * totDim]));
6377: }
6378: }
6379: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6380: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6381: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6382: PetscCall(PetscQuadratureDestroy(&qGeom));
6383: }
6384: /* Add contribution from X_t */
6385: if (hasDyn) {
6386: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6387: }
6388: if (hasFV) {
6389: PetscClassId id;
6390: PetscFV fv;
6391: PetscInt offsetI, NcI, NbI = 1, fc, f;
6393: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6394: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
6395: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
6396: PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
6397: if (id != PETSCFV_CLASSID) continue;
6398: /* Put in the weighted identity */
6399: PetscCall(PetscFVGetNumComponents(fv, &NcI));
6400: for (c = cStart; c < cEnd; ++c) {
6401: const PetscInt cind = c - cStart;
6402: const PetscInt eOffset = cind * totDim * totDim;
6403: PetscReal vol;
6405: PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
6406: for (fc = 0; fc < NcI; ++fc) {
6407: for (f = 0; f < NbI; ++f) {
6408: const PetscInt i = offsetI + f * NcI + fc;
6409: if (hasPrec) {
6410: if (hasJac) elemMat[eOffset + i * totDim + i] = vol;
6411: elemMatP[eOffset + i * totDim + i] = vol;
6412: } else {
6413: elemMat[eOffset + i * totDim + i] = vol;
6414: }
6415: }
6416: }
6417: }
6418: }
6419: /* No allocated space for FV stuff, so ignore the zero entries */
6420: PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
6421: }
6422: /* Insert values into matrix */
6423: for (c = cStart; c < cEnd; ++c) {
6424: const PetscInt cell = cells ? cells[c] : c;
6425: const PetscInt cind = c - cStart;
6427: /* Transform to global basis before insertion in Jacobian */
6428: if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
6429: if (hasPrec) {
6430: if (hasJac) {
6431: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6432: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6433: }
6434: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
6435: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
6436: } else {
6437: if (hasJac) {
6438: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6439: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6440: }
6441: }
6442: }
6443: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6444: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
6445: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
6446: if (dmAux) PetscCall(PetscFree(a));
6447: /* Compute boundary integrals */
6448: PetscCall(DMPlexComputeBdJacobian_Internal(dm, locX, locX_t, t, X_tShift, Jac, JacP, ctx));
6449: /* Assemble matrix */
6450: end: {
6451: PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
6453: if (dmAux) PetscCall(DMDestroy(&plex));
6454: PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
6455: if (hasJac && hasPrec) {
6456: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6457: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6458: }
6459: }
6460: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6461: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6462: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6463: PetscFunctionReturn(PETSC_SUCCESS);
6464: }
6466: /*@
6467: DMPlexComputeJacobianByKeyGeneral - Assemble the Jacobian and its preconditioning matrix over a cell range
6468: described by a `PetscFormKey` for a general (possibly non-square, non-nested) pair of row/column `DM`s.
6470: Collective
6472: Input Parameters:
6473: + dmr - the row `DMPLEX`
6474: . dmc - the column `DMPLEX`
6475: . key - the `PetscFormKey` selecting the label, value, part, and field for assembly
6476: . cellIS - the `IS` listing cells to process, or `NULL`
6477: . t - the current time
6478: . X_tShift - the time-derivative shift used to combine dynamic and static Jacobian contributions
6479: . locX - the local solution vector
6480: . locX_t - the local time-derivative vector, or `NULL`
6481: - ctx - the application context (unused; kept for API symmetry)
6483: Output Parameters:
6484: + Jac - the assembled Jacobian matrix
6485: - JacP - the assembled matrix from which the preconditioner is constructed
6487: Level: developer
6489: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscFormKey`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeInterpolatorGeneral()`
6490: @*/
6491: PetscErrorCode DMPlexComputeJacobianByKeyGeneral(DM dmr, DM dmc, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
6492: {
6493: DM_Plex *mesh = (DM_Plex *)dmr->data;
6494: const char *name = "Jacobian";
6495: DM dmAux = NULL, plex, tdm;
6496: PetscInt printFEM = mesh->printFEM;
6497: PetscBool clPerm = mesh->useMatClPerm;
6498: DMEnclosureType encAux;
6499: Vec A, tv;
6500: DMField coordField;
6501: PetscDS rds, cds, dsAux = NULL;
6502: PetscSection rsection, rglobalSection, csection, cglobalSection, sectionAux;
6503: PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
6504: const PetscInt *cells;
6505: PetscInt Nf, cNf;
6506: PetscInt totDim, ctotDim, totDimAux = 0, cStart, cEnd, numCells;
6507: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
6508: MPI_Comm comm;
6510: PetscFunctionBegin;
6511: PetscCall(PetscObjectGetComm((PetscObject)dmr, &comm));
6512: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dmr, 0, 0, 0));
6513: PetscCall(DMGetLocalSection(dmr, &rsection));
6514: PetscCall(DMGetGlobalSection(dmr, &rglobalSection));
6515: PetscCall(DMGetLocalSection(dmc, &csection));
6516: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
6517: PetscCall(DMGetAuxiliaryVec(dmr, key.label, key.value, key.part, &A));
6518: if (A) {
6519: PetscCall(VecGetDM(A, &dmAux));
6520: PetscCall(DMGetEnclosureRelation(dmAux, dmr, &encAux));
6521: PetscCall(DMConvert(dmAux, DMPLEX, &plex));
6522: PetscCall(DMGetLocalSection(plex, §ionAux));
6523: PetscCall(DMGetDS(dmAux, &dsAux));
6524: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
6525: }
6526: PetscCall(DMGetCoordinateField(dmr, &coordField));
6527: if (!cellIS) goto end;
6528: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6529: PetscCall(ISGetLocalSize(cellIS, &numCells));
6530: if (cStart >= cEnd) goto end;
6531: PetscCall(DMHasBasisTransform(dmr, &transform));
6532: PetscCall(DMGetBasisTransformDM_Internal(dmr, &tdm));
6533: PetscCall(DMGetBasisTransformVec_Internal(dmr, &tv));
6534: PetscCall(DMGetCellDS(dmr, cells ? cells[cStart] : cStart, &rds, NULL));
6535: PetscCall(DMGetCellDS(dmc, cells ? cells[cStart] : cStart, &cds, NULL));
6536: PetscCall(PetscDSGetNumFields(rds, &Nf));
6537: PetscCall(PetscDSGetNumFields(cds, &cNf));
6538: PetscCheck(Nf == cNf, comm, PETSC_ERR_ARG_WRONG, "Number of row fields %" PetscInt_FMT " != %" PetscInt_FMT " number of columns field", Nf, cNf);
6539: PetscCall(PetscDSGetTotalDimension(rds, &totDim));
6540: PetscCall(PetscDSGetTotalDimension(cds, &ctotDim));
6541: PetscCall(PetscDSHasJacobian(rds, &hasJac));
6542: PetscCall(PetscDSHasJacobianPreconditioner(rds, &hasPrec));
6543: /* user passed in the same matrix, avoid double contributions and
6544: only assemble the Jacobian */
6545: if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
6546: PetscCall(PetscDSHasDynamicJacobian(rds, &hasDyn));
6547: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6548: PetscCall(PetscMalloc5(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, (hasJac ? (size_t)numCells * totDim * ctotDim : 0), &elemMat, (hasPrec ? (size_t)numCells * totDim * ctotDim : 0), &elemMatP, (hasDyn ? (size_t)numCells * totDim * ctotDim : 0), &elemMatD));
6549: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6550: for (PetscInt c = cStart; c < cEnd; ++c) {
6551: const PetscInt cell = cells ? cells[c] : c;
6552: const PetscInt cind = c - cStart;
6553: PetscScalar *x = NULL, *x_t = NULL;
6554: PetscInt i;
6556: PetscCall(DMPlexVecGetClosure(dmr, rsection, locX, cell, NULL, &x));
6557: for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6558: PetscCall(DMPlexVecRestoreClosure(dmr, rsection, locX, cell, NULL, &x));
6559: if (locX_t) {
6560: PetscCall(DMPlexVecGetClosure(dmr, rsection, locX_t, cell, NULL, &x_t));
6561: for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6562: PetscCall(DMPlexVecRestoreClosure(dmr, rsection, locX_t, cell, NULL, &x_t));
6563: }
6564: if (dmAux) {
6565: PetscInt subcell;
6566: PetscCall(DMGetEnclosurePoint(dmAux, dmr, encAux, cell, &subcell));
6567: PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
6568: for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6569: PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
6570: }
6571: }
6572: if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * ctotDim));
6573: if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * ctotDim));
6574: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * ctotDim));
6575: for (PetscInt fieldI = 0; fieldI < Nf; ++fieldI) {
6576: PetscClassId id;
6577: PetscFE fe;
6578: PetscQuadrature qGeom = NULL;
6579: PetscInt Nb;
6580: /* Conforming batches */
6581: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6582: /* Remainder */
6583: PetscInt Nr, offset, Nq;
6584: PetscInt maxDegree;
6585: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6587: PetscCall(PetscDSGetDiscretization(rds, fieldI, (PetscObject *)&fe));
6588: PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
6589: if (id == PETSCFV_CLASSID) {
6590: hasFV = PETSC_TRUE;
6591: continue;
6592: }
6593: PetscCall(PetscFEGetDimension(fe, &Nb));
6594: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6595: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6596: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6597: if (!qGeom) {
6598: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6599: PetscCall(PetscObjectReference((PetscObject)qGeom));
6600: }
6601: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6602: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
6603: blockSize = Nb;
6604: batchSize = numBlocks * blockSize;
6605: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6606: numChunks = numCells / (numBatches * batchSize);
6607: Ne = numChunks * numBatches * batchSize;
6608: Nr = numCells % (numBatches * batchSize);
6609: offset = numCells - Nr;
6610: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6611: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6612: for (PetscInt fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6613: key.field = fieldI * Nf + fieldJ;
6614: if (hasJac) {
6615: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
6616: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * ctotDim]));
6617: }
6618: if (hasPrec) {
6619: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
6620: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * ctotDim]));
6621: }
6622: if (hasDyn) {
6623: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatD));
6624: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatD[offset * totDim * ctotDim]));
6625: }
6626: }
6627: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6628: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6629: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6630: PetscCall(PetscQuadratureDestroy(&qGeom));
6631: }
6632: /* Add contribution from X_t */
6633: if (hasDyn) {
6634: for (PetscInt c = 0; c < numCells * totDim * ctotDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6635: }
6636: if (hasFV) {
6637: PetscClassId id;
6638: PetscFV fv;
6639: PetscInt offsetI, NcI, NbI = 1;
6641: for (PetscInt fieldI = 0; fieldI < Nf; ++fieldI) {
6642: PetscCall(PetscDSGetDiscretization(rds, fieldI, (PetscObject *)&fv));
6643: PetscCall(PetscDSGetFieldOffset(rds, fieldI, &offsetI));
6644: PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
6645: if (id != PETSCFV_CLASSID) continue;
6646: /* Put in the weighted identity */
6647: PetscCall(PetscFVGetNumComponents(fv, &NcI));
6648: for (PetscInt c = cStart; c < cEnd; ++c) {
6649: const PetscInt cind = c - cStart;
6650: const PetscInt eOffset = cind * totDim * ctotDim;
6651: PetscReal vol;
6653: PetscCall(DMPlexComputeCellGeometryFVM(dmr, c, &vol, NULL, NULL));
6654: for (PetscInt fc = 0; fc < NcI; ++fc) {
6655: for (PetscInt f = 0; f < NbI; ++f) {
6656: const PetscInt i = offsetI + f * NcI + fc;
6657: if (hasPrec) {
6658: if (hasJac) elemMat[eOffset + i * ctotDim + i] = vol;
6659: elemMatP[eOffset + i * ctotDim + i] = vol;
6660: } else {
6661: elemMat[eOffset + i * ctotDim + i] = vol;
6662: }
6663: }
6664: }
6665: }
6666: }
6667: /* No allocated space for FV stuff, so ignore the zero entries */
6668: PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
6669: }
6670: /* Insert values into matrix */
6671: for (PetscInt c = cStart; c < cEnd; ++c) {
6672: const PetscInt cell = cells ? cells[c] : c;
6673: const PetscInt cind = c - cStart;
6675: /* Transform to global basis before insertion in Jacobian */
6676: if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dmr, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * ctotDim]));
6677: if (hasPrec) {
6678: if (hasJac) {
6679: if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMat[cind * totDim * ctotDim]));
6680: PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, Jac, cell, &elemMat[cind * totDim * ctotDim], ADD_VALUES));
6681: }
6682: if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMatP[cind * totDim * ctotDim]));
6683: PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, JacP, cell, &elemMatP[cind * totDim * ctotDim], ADD_VALUES));
6684: } else {
6685: if (hasJac) {
6686: if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMat[cind * totDim * ctotDim]));
6687: PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, JacP, cell, &elemMat[cind * totDim * ctotDim], ADD_VALUES));
6688: }
6689: }
6690: }
6691: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6692: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
6693: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
6694: if (dmAux) PetscCall(PetscFree(a));
6695: /* Compute boundary integrals */
6696: PetscCall(DMPlexComputeBdJacobian_Internal(dmr, locX, locX_t, t, X_tShift, Jac, JacP, ctx));
6697: /* Assemble matrix */
6698: end: {
6699: PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
6701: if (dmAux) PetscCall(DMDestroy(&plex));
6702: PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPI_C_BOOL, MPI_LOR, comm));
6703: if (hasJac && hasPrec) {
6704: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6705: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6706: }
6707: }
6708: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6709: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6710: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dmr, 0, 0, 0));
6711: PetscFunctionReturn(PETSC_SUCCESS);
6712: }
6714: /*@
6715: DMPlexComputeJacobianHybridByKey - Compute the local Jacobian over hybrid cells for terms matching the input key
6717: Collective
6719: Input Parameters:
6720: + dm - The output `DM`
6721: . key - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
6722: . cellIS - The `IS` give a set of cells to integrate over
6723: . t - The time
6724: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
6725: . locX - The local solution
6726: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
6727: - ctx - An optional application context, passed to the pointwise functions
6729: Output Parameters:
6730: + Jac - The local Jacobian
6731: - JacP - The local Jacobian preconditioner
6733: Level: developer
6735: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `PetscFormKey`
6736: @*/
6737: PetscErrorCode DMPlexComputeJacobianHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
6738: {
6739: DM_Plex *mesh = (DM_Plex *)dm->data;
6740: const char *name = "Hybrid Jacobian";
6741: DM dmAux[3] = {NULL, NULL, NULL};
6742: DMLabel ghostLabel = NULL;
6743: DM plex = NULL;
6744: DM plexA = NULL;
6745: PetscDS ds = NULL;
6746: PetscDS dsIn = NULL;
6747: PetscDS dsAux[3] = {NULL, NULL, NULL};
6748: Vec locA[3] = {NULL, NULL, NULL};
6749: DM dmScale[3] = {NULL, NULL, NULL};
6750: PetscDS dsScale[3] = {NULL, NULL, NULL};
6751: Vec locS[3] = {NULL, NULL, NULL};
6752: PetscSection section = NULL;
6753: PetscSection sectionAux[3] = {NULL, NULL, NULL};
6754: DMField coordField = NULL;
6755: PetscScalar *a[3] = {NULL, NULL, NULL};
6756: PetscScalar *s[3] = {NULL, NULL, NULL};
6757: PetscScalar *u = NULL, *u_t;
6758: PetscScalar *elemMatNeg, *elemMatPos, *elemMatCoh;
6759: PetscScalar *elemMatNegP, *elemMatPosP, *elemMatCohP;
6760: PetscSection globalSection;
6761: IS chunkISF, chunkISN;
6762: const PetscInt *cells;
6763: PetscInt *faces, *neighbors;
6764: PetscInt cStart, cEnd, numCells;
6765: PetscInt Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
6766: PetscInt maxDegree = PETSC_INT_MAX;
6767: PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
6768: PetscFEGeom *affineGeomF = NULL, **geomsF = NULL;
6769: PetscQuadrature affineQuadN = NULL;
6770: PetscFEGeom *affineGeomN = NULL;
6771: PetscBool hasBdJac, hasBdPrec;
6773: PetscFunctionBegin;
6774: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6775: if (!cellIS) goto end;
6776: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6777: PetscCall(ISGetLocalSize(cellIS, &numCells));
6778: if (cStart >= cEnd) goto end;
6779: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
6780: const char *name;
6781: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
6782: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Form keys for each side of a cohesive surface must be different (%s, %" PetscInt_FMT ", %" PetscInt_FMT ")", name, key[0].value, key[0].part);
6783: }
6784: PetscCall(DMConvert(dm, DMPLEX, &plex));
6785: PetscCall(DMGetLocalSection(dm, §ion));
6786: PetscCall(DMGetGlobalSection(dm, &globalSection));
6787: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
6788: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
6789: PetscCall(PetscDSGetNumFields(ds, &Nf));
6790: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
6791: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
6792: PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
6793: PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
6794: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
6795: if (locA[2]) {
6796: const PetscInt cellStart = cells ? cells[cStart] : cStart;
6798: PetscCall(VecGetDM(locA[2], &dmAux[2]));
6799: PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
6800: PetscCall(DMGetLocalSection(dmAux[2], §ionAux[2]));
6801: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
6802: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
6803: {
6804: const PetscInt *cone;
6805: PetscInt c;
6807: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6808: for (c = 0; c < 2; ++c) {
6809: const PetscInt *support;
6810: PetscInt ssize, s;
6812: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6813: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6814: PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
6815: if (support[0] == cellStart) s = 1;
6816: else if (support[1] == cellStart) s = 0;
6817: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6818: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
6819: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
6820: else dmAux[c] = dmAux[2];
6821: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
6822: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
6823: }
6824: }
6825: }
6826: /* Handle mass matrix scaling
6827: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
6828: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
6829: if (locS[2]) {
6830: const PetscInt cellStart = cells ? cells[cStart] : cStart;
6831: PetscInt Nb, Nbs;
6833: PetscCall(VecGetDM(locS[2], &dmScale[2]));
6834: PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6835: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6836: // BRAD: This is not set correctly
6837: key[2].field = 2;
6838: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6839: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6840: PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs);
6841: {
6842: const PetscInt *cone;
6843: PetscInt c;
6845: locS[1] = locS[0] = locS[2];
6846: dmScale[1] = dmScale[0] = dmScale[2];
6847: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6848: for (c = 0; c < 2; ++c) {
6849: const PetscInt *support;
6850: PetscInt ssize, s;
6852: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6853: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6854: PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
6855: if (support[0] == cellStart) s = 1;
6856: else if (support[1] == cellStart) s = 0;
6857: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6858: PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
6859: PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
6860: }
6861: }
6862: }
6863: /* 2: Setup geometric data */
6864: PetscCall(DMGetCoordinateField(dm, &coordField));
6865: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6866: if (maxDegree > 1) {
6867: PetscInt f;
6868: PetscCall(PetscCalloc2(Nf, &quadsF, Nf, &geomsF));
6869: for (f = 0; f < Nf; ++f) {
6870: PetscFE fe;
6872: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
6873: if (fe) {
6874: PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
6875: PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
6876: }
6877: }
6878: }
6879: /* Loop over chunks */
6880: cellChunkSize = numCells;
6881: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
6882: PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
6883: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
6884: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
6885: /* Extract field coefficients */
6886: /* NOTE This needs the end cap faces to have identical orientations */
6887: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6888: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6889: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
6890: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6891: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6892: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6893: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6894: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6895: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6896: for (chunk = 0; chunk < numChunks; ++chunk) {
6897: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
6899: if (hasBdJac) {
6900: PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
6901: PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
6902: PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
6903: }
6904: if (hasBdPrec) {
6905: PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
6906: PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
6907: PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
6908: }
6909: /* Get faces */
6910: for (c = cS; c < cE; ++c) {
6911: const PetscInt cell = cells ? cells[c] : c;
6912: const PetscInt *cone, *support;
6913: PetscCall(DMPlexGetCone(plex, cell, &cone));
6914: faces[(c - cS) * 2 + 0] = cone[0];
6915: faces[(c - cS) * 2 + 1] = cone[1];
6916: PetscCall(DMPlexGetSupport(dm, cone[0], &support));
6917: neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
6918: PetscCall(DMPlexGetSupport(dm, cone[1], &support));
6919: neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
6920: }
6921: PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
6922: PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
6923: if (maxDegree <= 1) {
6924: if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
6925: if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
6926: if (!affineQuadN) {
6927: PetscInt dim;
6928: PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
6929: PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
6930: PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
6931: }
6932: if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
6933: } else {
6934: PetscInt f;
6935: for (f = 0; f < Nf; ++f) {
6936: if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
6937: }
6938: }
6940: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6941: PetscFE feI;
6942: PetscFEGeom *geomF = affineGeomF ? affineGeomF : geomsF[fieldI];
6943: PetscFEGeom *chunkGeomF = NULL, *remGeomF = NULL;
6944: PetscFEGeom *geomN = affineGeomN ? affineGeomN : geomsF[fieldI];
6945: PetscFEGeom *chunkGeomN = NULL, *remGeomN = NULL;
6946: PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[fieldI];
6947: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6948: PetscBool isCohesiveField;
6950: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6951: if (!feI) continue;
6952: PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6953: PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
6954: PetscCall(PetscFEGetDimension(feI, &Nb));
6955: blockSize = Nb;
6956: batchSize = numBlocks * blockSize;
6957: PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6958: numChunks = numCells / (numBatches * batchSize);
6959: Ne = numChunks * numBatches * batchSize;
6960: Nr = numCells % (numBatches * batchSize);
6961: offset = numCells - Nr;
6962: PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
6963: PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
6964: PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
6965: PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
6966: PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6967: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6968: PetscFE feJ;
6970: PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
6971: if (!feJ) continue;
6972: key[0].field = fieldI * Nf + fieldJ;
6973: key[1].field = fieldI * Nf + fieldJ;
6974: key[2].field = fieldI * Nf + fieldJ;
6975: if (hasBdJac) {
6976: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
6977: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
6978: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
6979: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, X_tShift, &elemMatPos[offset * totDim * totDim]));
6980: }
6981: if (hasBdPrec) {
6982: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
6983: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
6984: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
6985: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
6986: }
6987: if (hasBdJac) {
6988: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
6989: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
6990: }
6991: if (hasBdPrec) {
6992: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
6993: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
6994: }
6995: }
6996: PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
6997: PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
6998: PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
6999: PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
7000: }
7001: /* Insert values into matrix */
7002: for (c = cS; c < cE; ++c) {
7003: const PetscInt cell = cells ? cells[c] : c;
7004: const PetscInt cind = c - cS, coff = cind * totDim * totDim;
7005: PetscInt i, j;
7007: /* Scale element values */
7008: if (locS[0]) {
7009: PetscInt Nb, soff = cind * totDimScale[0], off = 0;
7010: PetscBool cohesive;
7012: for (fieldI = 0; fieldI < Nf; ++fieldI) {
7013: PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
7014: PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));
7016: if (fieldI == key[2].field) {
7017: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
7018: for (i = 0; i < Nb; ++i) {
7019: for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNeg[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPos[coff + (off + i) * totDim + j];
7020: if (hasBdPrec)
7021: for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNegP[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPosP[coff + (off + i) * totDim + j];
7022: }
7023: off += Nb;
7024: } else {
7025: const PetscInt N = cohesive ? Nb : Nb * 2;
7027: for (i = 0; i < N; ++i) {
7028: for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
7029: if (hasBdPrec)
7030: for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
7031: }
7032: off += N;
7033: }
7034: }
7035: } else {
7036: for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
7037: if (hasBdPrec)
7038: for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
7039: }
7040: if (hasBdPrec) {
7041: if (hasBdJac) {
7042: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
7043: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
7044: }
7045: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
7046: PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
7047: } else if (hasBdJac) {
7048: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
7049: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
7050: }
7051: }
7052: }
7053: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
7054: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
7055: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
7056: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
7057: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
7058: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
7059: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
7060: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
7061: PetscCall(PetscFree2(faces, neighbors));
7062: PetscCall(ISDestroy(&chunkISF));
7063: PetscCall(ISDestroy(&chunkISN));
7064: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
7065: if (maxDegree <= 1) {
7066: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
7067: PetscCall(PetscQuadratureDestroy(&affineQuadF));
7068: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
7069: PetscCall(PetscQuadratureDestroy(&affineQuadN));
7070: } else {
7071: PetscInt f;
7072: for (f = 0; f < Nf; ++f) {
7073: if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
7074: if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
7075: }
7076: PetscCall(PetscFree2(quadsF, geomsF));
7077: }
7078: if (dmAux[2]) PetscCall(DMDestroy(&plexA));
7079: PetscCall(DMDestroy(&plex));
7080: end:
7081: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
7082: PetscFunctionReturn(PETSC_SUCCESS);
7083: }
7085: /*@
7086: DMPlexComputeJacobianActionByKey - Compute the local Jacobian for terms matching the input key
7088: Collective
7090: Input Parameters:
7091: + dm - The output `DM`
7092: . key - The `PetscFormKey` indicating what should be integrated
7093: . cellIS - The `IS` give a set of cells to integrate over
7094: . t - The time
7095: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
7096: . locX - The local solution
7097: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
7098: . locY - The local vector acted on by J
7099: - ctx - An optional application context, passed to the pointwise functions
7101: Output Parameter:
7102: . locF - The local residual F = J(X) Y
7104: Level: developer
7106: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
7107: @*/
7108: PetscErrorCode DMPlexComputeJacobianActionByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Vec locY, Vec locF, PetscCtx ctx)
7109: {
7110: DM_Plex *mesh = (DM_Plex *)dm->data;
7111: const char *name = "Jacobian";
7112: DM dmAux = NULL, plex, plexAux = NULL;
7113: DMEnclosureType encAux;
7114: Vec A;
7115: DMField coordField;
7116: PetscDS prob, probAux = NULL;
7117: PetscQuadrature quad;
7118: PetscSection section, globalSection, sectionAux;
7119: PetscScalar *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
7120: const PetscInt *cells;
7121: PetscInt Nf, fieldI, fieldJ;
7122: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
7123: PetscBool hasDyn;
7125: PetscFunctionBegin;
7126: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
7127: PetscCall(DMConvert(dm, DMPLEX, &plex));
7128: PetscCall(ISGetLocalSize(cellIS, &numCells));
7129: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
7130: PetscCall(DMGetLocalSection(dm, §ion));
7131: PetscCall(DMGetGlobalSection(dm, &globalSection));
7132: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
7133: PetscCall(PetscDSGetNumFields(prob, &Nf));
7134: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
7135: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
7136: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
7137: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
7138: if (A) {
7139: PetscCall(VecGetDM(A, &dmAux));
7140: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
7141: PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
7142: PetscCall(DMGetLocalSection(plexAux, §ionAux));
7143: PetscCall(DMGetDS(dmAux, &probAux));
7144: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
7145: }
7146: PetscCall(VecSet(locF, 0.0));
7147: PetscCall(PetscMalloc6(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, numCells * totDim * totDim, &elemMat, (hasDyn ? (size_t)numCells * totDim * totDim : 0), &elemMatD, numCells * totDim, &y, totDim, &z));
7148: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
7149: PetscCall(DMGetCoordinateField(dm, &coordField));
7150: for (c = cStart; c < cEnd; ++c) {
7151: const PetscInt cell = cells ? cells[c] : c;
7152: const PetscInt cind = c - cStart;
7153: PetscScalar *x = NULL, *x_t = NULL;
7154: PetscInt i;
7156: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
7157: for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
7158: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
7159: if (locX_t) {
7160: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
7161: for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
7162: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
7163: }
7164: if (dmAux) {
7165: PetscInt subcell;
7166: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
7167: PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
7168: for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
7169: PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
7170: }
7171: PetscCall(DMPlexVecGetClosure(plex, section, locY, cell, NULL, &x));
7172: for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
7173: PetscCall(DMPlexVecRestoreClosure(plex, section, locY, cell, NULL, &x));
7174: }
7175: PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
7176: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
7177: for (fieldI = 0; fieldI < Nf; ++fieldI) {
7178: PetscFE fe;
7179: PetscInt Nb;
7180: /* Conforming batches */
7181: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
7182: /* Remainder */
7183: PetscInt Nr, offset, Nq;
7184: PetscQuadrature qGeom = NULL;
7185: PetscInt maxDegree;
7186: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
7188: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
7189: PetscCall(PetscFEGetQuadrature(fe, &quad));
7190: PetscCall(PetscFEGetDimension(fe, &Nb));
7191: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
7192: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
7193: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
7194: if (!qGeom) {
7195: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
7196: PetscCall(PetscObjectReference((PetscObject)qGeom));
7197: }
7198: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
7199: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
7200: blockSize = Nb;
7201: batchSize = numBlocks * blockSize;
7202: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
7203: numChunks = numCells / (numBatches * batchSize);
7204: Ne = numChunks * numBatches * batchSize;
7205: Nr = numCells % (numBatches * batchSize);
7206: offset = numCells - Nr;
7207: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
7208: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
7209: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
7210: key.field = fieldI * Nf + fieldJ;
7211: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
7212: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
7213: if (hasDyn) {
7214: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
7215: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
7216: }
7217: }
7218: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
7219: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
7220: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
7221: PetscCall(PetscQuadratureDestroy(&qGeom));
7222: }
7223: if (hasDyn) {
7224: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
7225: }
7226: for (c = cStart; c < cEnd; ++c) {
7227: const PetscInt cell = cells ? cells[c] : c;
7228: const PetscInt cind = c - cStart;
7229: const PetscBLASInt one = 1;
7230: PetscBLASInt M;
7231: const PetscScalar a = 1.0, b = 0.0;
7233: PetscCall(PetscBLASIntCast(totDim, &M));
7234: PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
7235: if (mesh->printFEM > 1) {
7236: PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
7237: PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
7238: PetscCall(DMPrintCellVector(c, "Z", totDim, z));
7239: }
7240: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, z, ADD_VALUES));
7241: }
7242: PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
7243: if (mesh->printFEM) {
7244: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)locF), "Z:\n"));
7245: PetscCall(VecView(locF, NULL));
7246: }
7247: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
7248: PetscCall(PetscFree(a));
7249: PetscCall(DMDestroy(&plexAux));
7250: PetscCall(DMDestroy(&plex));
7251: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
7252: PetscFunctionReturn(PETSC_SUCCESS);
7253: }
7255: static void f0_1(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
7256: {
7257: f0[0] = u[0];
7258: }
7260: static void f0_x(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
7261: {
7262: f0[0] = x[(int)PetscRealPart(constants[0])] * u[0];
7263: }
7265: static void f0_x2(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
7266: {
7267: PetscInt d;
7269: f0[0] = 0.0;
7270: for (d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
7271: }
7273: /*@
7274: DMPlexComputeMoments - Compute the first three moments for a field
7276: Noncollective
7278: Input Parameters:
7279: + dm - the `DMPLEX`
7280: - u - the field
7282: Output Parameter:
7283: . moments - the field moments
7285: Level: intermediate
7287: Note:
7288: The `moments` array should be of length cdim + 2, where cdim is the number of components for the coordinate field.
7290: .seealso: `DM`, `DMPLEX`, `DMSwarmComputeMoments()`
7291: @*/
7292: PetscErrorCode DMPlexComputeMoments(DM dm, Vec u, PetscReal moments[])
7293: {
7294: PetscDS ds;
7295: PetscScalar mom, constants[1];
7296: const PetscScalar *oldConstants;
7297: PetscInt cdim, Nf, field = 0, Ncon;
7298: MPI_Comm comm;
7299: void *ctx;
7301: PetscFunctionBeginUser;
7302: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
7303: PetscCall(DMGetCoordinateDim(dm, &cdim));
7304: PetscCall(DMGetApplicationContext(dm, &ctx));
7305: PetscCall(DMGetDS(dm, &ds));
7306: PetscCall(PetscDSGetNumFields(ds, &Nf));
7307: PetscCall(PetscDSGetConstants(ds, &Ncon, &oldConstants));
7308: PetscCheck(Nf == 1, comm, PETSC_ERR_ARG_WRONG, "We currently only support 1 field, not %" PetscInt_FMT, Nf);
7309: PetscCall(PetscDSSetObjective(ds, field, &f0_1));
7310: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7311: moments[0] = PetscRealPart(mom);
7312: for (PetscInt c = 0; c < cdim; ++c) {
7313: constants[0] = c;
7314: PetscCall(PetscDSSetConstants(ds, 1, constants));
7315: PetscCall(PetscDSSetObjective(ds, field, &f0_x));
7316: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7317: moments[c + 1] = PetscRealPart(mom);
7318: }
7319: PetscCall(PetscDSSetObjective(ds, field, &f0_x2));
7320: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7321: moments[cdim + 1] = PetscRealPart(mom);
7322: PetscCall(PetscDSSetConstants(ds, Ncon, (PetscScalar *)oldConstants));
7323: PetscFunctionReturn(PETSC_SUCCESS);
7324: }