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;
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 (PetscInt 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];
1097: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
1098: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
1099: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
1100: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
1101: DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
1102: for (PetscInt d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
1103: PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
1104: } else {
1105: PetscScalar *xI;
1106: PetscScalar *xG;
1108: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
1109: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
1110: ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
1111: if (ierru) {
1112: PetscCall(ISRestoreIndices(faceIS, &faces));
1113: PetscCall(ISDestroy(&faceIS));
1114: goto cleanup;
1115: }
1116: }
1117: }
1118: PetscCall(ISRestoreIndices(faceIS, &faces));
1119: PetscCall(ISDestroy(&faceIS));
1120: }
1121: cleanup:
1122: PetscCall(VecRestoreArray(locX, &x));
1123: if (Grad) {
1124: PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
1125: PetscCall(VecRestoreArrayRead(Grad, &grad));
1126: }
1127: if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
1128: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
1129: PetscCall(ierru);
1130: PetscFunctionReturn(PETSC_SUCCESS);
1131: }
1133: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx)
1134: {
1135: for (PetscInt c = 0; c < Nc; ++c) u[c] = 0.0;
1136: return PETSC_SUCCESS;
1137: }
1139: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1140: {
1141: PetscObject isZero;
1142: PetscDS prob;
1143: PetscInt numBd;
1145: PetscFunctionBegin;
1146: PetscCall(DMGetDS(dm, &prob));
1147: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1148: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1149: PetscCall(PetscDSUpdateBoundaryLabels(prob, dm));
1150: for (PetscInt b = 0; b < numBd; ++b) {
1151: PetscWeakForm wf;
1152: DMBoundaryConditionType type;
1153: const char *name;
1154: DMLabel label;
1155: PetscInt field, Nc;
1156: const PetscInt *comps;
1157: PetscObject obj;
1158: PetscClassId id;
1159: PetscVoidFn *bvfunc;
1160: PetscInt numids;
1161: const PetscInt *ids;
1162: void *ctx;
1164: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1165: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1166: PetscCall(DMGetField(dm, field, NULL, &obj));
1167: PetscCall(PetscObjectGetClassId(obj, &id));
1168: if (id == PETSCFE_CLASSID) {
1169: switch (type) {
1170: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1171: case DM_BC_ESSENTIAL: {
1172: PetscSimplePointFn *func = (PetscSimplePointFn *)bvfunc;
1174: if (isZero) func = zero;
1175: PetscCall(DMPlexLabelAddCells(dm, label));
1176: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
1177: PetscCall(DMPlexLabelClearCells(dm, label));
1178: } break;
1179: case DM_BC_ESSENTIAL_FIELD: {
1180: PetscPointFn *func = (PetscPointFn *)bvfunc;
1182: PetscCall(DMPlexLabelAddCells(dm, label));
1183: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
1184: PetscCall(DMPlexLabelClearCells(dm, label));
1185: } break;
1186: default:
1187: break;
1188: }
1189: } else if (id == PETSCFV_CLASSID) {
1190: {
1191: PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode (*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;
1193: if (!faceGeomFVM) continue;
1194: PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
1195: }
1196: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1197: }
1198: PetscFunctionReturn(PETSC_SUCCESS);
1199: }
1201: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1202: {
1203: PetscObject isZero;
1204: PetscDS prob;
1205: PetscInt numBd;
1207: PetscFunctionBegin;
1208: if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
1209: PetscCall(DMGetDS(dm, &prob));
1210: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1211: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1212: for (PetscInt b = 0; b < numBd; ++b) {
1213: PetscWeakForm wf;
1214: DMBoundaryConditionType type;
1215: const char *name;
1216: DMLabel label;
1217: PetscInt field, Nc;
1218: const PetscInt *comps;
1219: PetscObject obj;
1220: PetscClassId id;
1221: PetscInt numids;
1222: const PetscInt *ids;
1223: PetscVoidFn *bvfunc;
1224: void *ctx;
1226: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
1227: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1228: PetscCall(DMGetField(dm, field, NULL, &obj));
1229: PetscCall(PetscObjectGetClassId(obj, &id));
1230: if (id == PETSCFE_CLASSID) {
1231: switch (type) {
1232: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1233: case DM_BC_ESSENTIAL: {
1234: PetscSimplePointFn *func_t = (PetscSimplePointFn *)bvfunc;
1236: if (isZero) func_t = zero;
1237: PetscCall(DMPlexLabelAddCells(dm, label));
1238: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1239: PetscCall(DMPlexLabelClearCells(dm, label));
1240: } break;
1241: case DM_BC_ESSENTIAL_FIELD: {
1242: PetscPointFn *func_t = (PetscPointFn *)bvfunc;
1244: PetscCall(DMPlexLabelAddCells(dm, label));
1245: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1246: PetscCall(DMPlexLabelClearCells(dm, label));
1247: } break;
1248: default:
1249: break;
1250: }
1251: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1252: }
1253: PetscFunctionReturn(PETSC_SUCCESS);
1254: }
1256: PetscErrorCode DMPlexInsertBounds_Plex(DM dm, PetscBool lower, PetscReal time, Vec locB)
1257: {
1258: PetscDS ds;
1259: PetscInt numBd;
1261: PetscFunctionBegin;
1262: PetscCall(DMGetDS(dm, &ds));
1263: PetscCall(PetscDSGetNumBoundary(ds, &numBd));
1264: PetscCall(PetscDSUpdateBoundaryLabels(ds, dm));
1265: for (PetscInt b = 0; b < numBd; ++b) {
1266: PetscWeakForm wf;
1267: DMBoundaryConditionType type;
1268: const char *name;
1269: DMLabel label;
1270: PetscInt numids;
1271: const PetscInt *ids;
1272: PetscInt field, Nc;
1273: const PetscInt *comps;
1274: PetscVoidFn *bvfunc;
1275: void *ctx;
1277: PetscCall(PetscDSGetBoundary(ds, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1278: if (lower && type != DM_BC_LOWER_BOUND) continue;
1279: if (!lower && type != DM_BC_UPPER_BOUND) continue;
1280: PetscCall(DMPlexLabelAddCells(dm, label));
1281: {
1282: PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, PetscCtx ctx);
1283: void **ctxs;
1284: PetscInt Nf;
1286: PetscCall(DMGetNumFields(dm, &Nf));
1287: PetscCall(PetscCalloc2(Nf, &funcs, Nf, &ctxs));
1288: funcs[field] = (PetscSimplePointFn *)bvfunc;
1289: ctxs[field] = ctx;
1290: PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_ALL_VALUES, locB));
1291: PetscCall(PetscFree2(funcs, ctxs));
1292: }
1293: PetscCall(DMPlexLabelClearCells(dm, label));
1294: }
1295: PetscFunctionReturn(PETSC_SUCCESS);
1296: }
1298: /*@
1299: DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector
1301: Not Collective
1303: Input Parameters:
1304: + dm - The `DM`
1305: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1306: . time - The time
1307: . faceGeomFVM - Face geometry data for FV discretizations
1308: . cellGeomFVM - Cell geometry data for FV discretizations
1309: - gradFVM - Gradient reconstruction data for FV discretizations
1311: Output Parameter:
1312: . locX - Solution updated with boundary values
1314: Level: intermediate
1316: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1317: @*/
1318: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1319: {
1320: PetscFunctionBegin;
1326: PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1327: PetscFunctionReturn(PETSC_SUCCESS);
1328: }
1330: /*@
1331: DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector
1333: Input Parameters:
1334: + dm - The `DM`
1335: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1336: . time - The time
1337: . faceGeomFVM - Face geometry data for FV discretizations
1338: . cellGeomFVM - Cell geometry data for FV discretizations
1339: - gradFVM - Gradient reconstruction data for FV discretizations
1341: Output Parameter:
1342: . locX_t - Solution updated with boundary values
1344: Level: developer
1346: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1347: @*/
1348: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1349: {
1350: PetscFunctionBegin;
1356: PetscTryMethod(dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1357: PetscFunctionReturn(PETSC_SUCCESS);
1358: }
1360: /*@
1361: DMPlexInsertBounds - Puts coefficients which represent solution bounds into the local bounds vector
1363: Not Collective
1365: Input Parameters:
1366: + dm - The `DM`
1367: . lower - If `PETSC_TRUE` use `DM_BC_LOWER_BOUND` conditions, otherwise use `DM_BC_UPPER_BOUND`
1368: - time - The time
1370: Output Parameter:
1371: . locB - Bounds vector updated with new bounds
1373: Level: intermediate
1375: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `PetscDSAddBoundary()`
1376: @*/
1377: PetscErrorCode DMPlexInsertBounds(DM dm, PetscBool lower, PetscReal time, Vec locB)
1378: {
1379: PetscFunctionBegin;
1382: PetscTryMethod(dm, "DMPlexInsertBounds_C", (DM, PetscBool, PetscReal, Vec), (dm, lower, time, locB));
1383: PetscFunctionReturn(PETSC_SUCCESS);
1384: }
1386: // Handle non-essential (e.g. outflow) boundary values
1387: PetscErrorCode DMPlexInsertBoundaryValuesFVM(DM dm, PetscFV fv, Vec locX, PetscReal time, Vec *locGradient)
1388: {
1389: DM dmGrad;
1390: Vec cellGeometryFVM, faceGeometryFVM, locGrad = NULL;
1392: PetscFunctionBegin;
1396: if (locGradient) {
1397: PetscAssertPointer(locGradient, 5);
1398: *locGradient = NULL;
1399: }
1400: PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
1401: /* Reconstruct and limit cell gradients */
1402: PetscCall(DMPlexGetGradientDM(dm, fv, &dmGrad));
1403: if (dmGrad) {
1404: Vec grad;
1405: PetscInt fStart, fEnd;
1407: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1408: PetscCall(DMGetGlobalVector(dmGrad, &grad));
1409: PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
1410: /* Communicate gradient values */
1411: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
1412: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
1413: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
1414: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
1415: }
1416: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
1417: if (locGradient) *locGradient = locGrad;
1418: else if (locGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
1419: PetscFunctionReturn(PETSC_SUCCESS);
1420: }
1422: PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1423: {
1424: Vec localX;
1426: PetscFunctionBegin;
1427: PetscCall(DMGetLocalVector(dm, &localX));
1428: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1429: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1430: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1431: PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1432: PetscCall(DMRestoreLocalVector(dm, &localX));
1433: PetscFunctionReturn(PETSC_SUCCESS);
1434: }
1436: /*@C
1437: DMPlexComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
1439: Collective
1441: Input Parameters:
1442: + dm - The `DM`
1443: . time - The time
1444: . funcs - The functions to evaluate for each field component
1445: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1446: - localX - The coefficient vector u_h, a local vector
1448: Output Parameter:
1449: . diff - The diff ||u - u_h||_2
1451: Level: developer
1453: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1454: @*/
1455: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1456: {
1457: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1458: DM tdm;
1459: Vec tv;
1460: PetscSection section;
1461: PetscQuadrature quad;
1462: PetscFEGeom fegeom;
1463: PetscScalar *funcVal, *interpolant;
1464: PetscReal *coords, *gcoords;
1465: PetscReal localDiff = 0.0;
1466: const PetscReal *quadWeights;
1467: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1468: PetscBool transform;
1470: PetscFunctionBegin;
1471: PetscCall(DMGetDimension(dm, &dim));
1472: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1473: fegeom.dimEmbed = coordDim;
1474: PetscCall(DMGetLocalSection(dm, §ion));
1475: PetscCall(PetscSectionGetNumFields(section, &numFields));
1476: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1477: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1478: PetscCall(DMHasBasisTransform(dm, &transform));
1479: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1480: for (field = 0; field < numFields; ++field) {
1481: PetscObject obj;
1482: PetscClassId id;
1483: PetscInt Nc;
1485: PetscCall(DMGetField(dm, field, NULL, &obj));
1486: PetscCall(PetscObjectGetClassId(obj, &id));
1487: if (id == PETSCFE_CLASSID) {
1488: PetscFE fe = (PetscFE)obj;
1490: PetscCall(PetscFEGetQuadrature(fe, &quad));
1491: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1492: } else if (id == PETSCFV_CLASSID) {
1493: PetscFV fv = (PetscFV)obj;
1495: PetscCall(PetscFVGetQuadrature(fv, &quad));
1496: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1497: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1498: numComponents += Nc;
1499: }
1500: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1501: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1502: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1503: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1504: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1505: for (c = cStart; c < cEnd; ++c) {
1506: PetscScalar *x = NULL;
1507: PetscReal elemDiff = 0.0;
1508: PetscInt qc = 0;
1510: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1511: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1513: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1514: PetscObject obj;
1515: PetscClassId id;
1516: void *const ctx = ctxs ? ctxs[field] : NULL;
1517: PetscInt Nb, Nc, q, fc;
1519: PetscCall(DMGetField(dm, field, NULL, &obj));
1520: PetscCall(PetscObjectGetClassId(obj, &id));
1521: if (id == PETSCFE_CLASSID) {
1522: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1523: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1524: } else if (id == PETSCFV_CLASSID) {
1525: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1526: Nb = 1;
1527: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1528: if (debug) {
1529: char title[1024];
1530: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1531: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1532: }
1533: for (q = 0; q < Nq; ++q) {
1534: PetscFEGeom qgeom;
1535: PetscErrorCode ierr;
1537: qgeom.dimEmbed = fegeom.dimEmbed;
1538: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1539: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1540: qgeom.detJ = &fegeom.detJ[q];
1541: 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);
1542: if (transform) {
1543: gcoords = &coords[coordDim * Nq];
1544: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1545: } else {
1546: gcoords = &coords[coordDim * q];
1547: }
1548: PetscCall(PetscArrayzero(funcVal, Nc));
1549: ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1550: if (ierr) {
1551: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1552: PetscCall(DMRestoreLocalVector(dm, &localX));
1553: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1554: }
1555: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1556: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1557: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1558: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1559: for (fc = 0; fc < Nc; ++fc) {
1560: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1561: if (debug)
1562: 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),
1563: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1564: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1565: }
1566: }
1567: fieldOffset += Nb;
1568: qc += Nc;
1569: }
1570: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1571: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1572: localDiff += elemDiff;
1573: }
1574: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1575: PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1576: *diff = PetscSqrtReal(*diff);
1577: PetscFunctionReturn(PETSC_SUCCESS);
1578: }
1580: 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)
1581: {
1582: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1583: DM tdm;
1584: PetscSection section;
1585: PetscQuadrature quad;
1586: Vec localX, tv;
1587: PetscScalar *funcVal, *interpolant;
1588: const PetscReal *quadWeights;
1589: PetscFEGeom fegeom;
1590: PetscReal *coords, *gcoords;
1591: PetscReal localDiff = 0.0;
1592: PetscInt dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1593: PetscBool transform;
1595: PetscFunctionBegin;
1596: PetscCall(DMGetDimension(dm, &dim));
1597: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1598: fegeom.dimEmbed = coordDim;
1599: PetscCall(DMGetLocalSection(dm, §ion));
1600: PetscCall(PetscSectionGetNumFields(section, &numFields));
1601: PetscCall(DMGetLocalVector(dm, &localX));
1602: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1603: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1604: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1605: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1606: PetscCall(DMHasBasisTransform(dm, &transform));
1607: for (field = 0; field < numFields; ++field) {
1608: PetscFE fe;
1609: PetscInt Nc;
1611: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1612: PetscCall(PetscFEGetQuadrature(fe, &quad));
1613: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1614: numComponents += Nc;
1615: }
1616: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1617: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1618: /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1619: PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1620: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1621: for (c = cStart; c < cEnd; ++c) {
1622: PetscScalar *x = NULL;
1623: PetscReal elemDiff = 0.0;
1624: PetscInt qc = 0;
1626: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1627: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1629: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1630: PetscFE fe;
1631: void *const ctx = ctxs ? ctxs[field] : NULL;
1632: PetscInt Nb, Nc, q, fc;
1634: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1635: PetscCall(PetscFEGetDimension(fe, &Nb));
1636: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1637: if (debug) {
1638: char title[1024];
1639: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1640: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1641: }
1642: for (q = 0; q < Nq; ++q) {
1643: PetscFEGeom qgeom;
1644: PetscErrorCode ierr;
1646: qgeom.dimEmbed = fegeom.dimEmbed;
1647: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1648: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1649: qgeom.detJ = &fegeom.detJ[q];
1650: 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);
1651: if (transform) {
1652: gcoords = &coords[coordDim * Nq];
1653: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1654: } else {
1655: gcoords = &coords[coordDim * q];
1656: }
1657: PetscCall(PetscArrayzero(funcVal, Nc));
1658: ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1659: if (ierr) {
1660: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1661: PetscCall(DMRestoreLocalVector(dm, &localX));
1662: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1663: }
1664: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1665: PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1666: /* Overwrite with the dot product if the normal is given */
1667: if (n) {
1668: for (fc = 0; fc < Nc; ++fc) {
1669: PetscScalar sum = 0.0;
1670: for (PetscInt d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1671: interpolant[fc] = sum;
1672: }
1673: }
1674: for (fc = 0; fc < Nc; ++fc) {
1675: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1676: 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])));
1677: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1678: }
1679: }
1680: fieldOffset += Nb;
1681: qc += Nc;
1682: }
1683: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1684: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1685: localDiff += elemDiff;
1686: }
1687: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1688: PetscCall(DMRestoreLocalVector(dm, &localX));
1689: PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1690: *diff = PetscSqrtReal(*diff);
1691: PetscFunctionReturn(PETSC_SUCCESS);
1692: }
1694: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1695: {
1696: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1697: DM tdm;
1698: DMLabel depthLabel;
1699: PetscSection section;
1700: Vec localX, tv;
1701: PetscReal *localDiff;
1702: PetscInt dim, depth, dE, Nf, f, Nds, s;
1703: PetscBool transform;
1705: PetscFunctionBegin;
1706: PetscCall(DMGetDimension(dm, &dim));
1707: PetscCall(DMGetCoordinateDim(dm, &dE));
1708: PetscCall(DMGetLocalSection(dm, §ion));
1709: PetscCall(DMGetLocalVector(dm, &localX));
1710: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1711: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1712: PetscCall(DMHasBasisTransform(dm, &transform));
1713: PetscCall(DMGetNumFields(dm, &Nf));
1714: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1715: PetscCall(DMLabelGetNumValues(depthLabel, &depth));
1717: PetscCall(VecSet(localX, 0.0));
1718: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1719: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1720: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1721: PetscCall(DMGetNumDS(dm, &Nds));
1722: PetscCall(PetscCalloc1(Nf, &localDiff));
1723: for (s = 0; s < Nds; ++s) {
1724: PetscDS ds;
1725: DMLabel label;
1726: IS fieldIS, pointIS;
1727: const PetscInt *fields, *points = NULL;
1728: PetscQuadrature quad;
1729: const PetscReal *quadPoints, *quadWeights;
1730: PetscFEGeom fegeom;
1731: PetscReal *coords, *gcoords;
1732: PetscScalar *funcVal, *interpolant;
1733: PetscBool isCohesive;
1734: PetscInt qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;
1736: PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1737: PetscCall(ISGetIndices(fieldIS, &fields));
1738: PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1739: PetscCall(PetscDSGetNumFields(ds, &dsNf));
1740: PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1741: PetscCall(PetscDSGetQuadrature(ds, &quad));
1742: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1743: PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1744: PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1745: if (!label) {
1746: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1747: } else {
1748: PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1749: PetscCall(ISGetLocalSize(pointIS, &cEnd));
1750: PetscCall(ISGetIndices(pointIS, &points));
1751: }
1752: for (c = cStart; c < cEnd; ++c) {
1753: const PetscInt cell = points ? points[c] : c;
1754: PetscScalar *x = NULL;
1755: const PetscInt *cone;
1756: PetscInt qc = 0, fOff = 0, dep;
1758: PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1759: if (dep != depth - 1) continue;
1760: if (isCohesive) {
1761: PetscCall(DMPlexGetCone(dm, cell, &cone));
1762: PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1763: } else {
1764: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1765: }
1766: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x));
1767: for (f = 0; f < dsNf; ++f) {
1768: PetscObject obj;
1769: PetscClassId id;
1770: void *const ctx = ctxs ? ctxs[fields[f]] : NULL;
1771: PetscInt Nb, Nc, q, fc;
1772: PetscReal elemDiff = 0.0;
1773: PetscBool cohesive;
1775: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1776: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1777: PetscCall(PetscObjectGetClassId(obj, &id));
1778: if (id == PETSCFE_CLASSID) {
1779: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1780: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1781: } else if (id == PETSCFV_CLASSID) {
1782: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1783: Nb = 1;
1784: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1785: if (isCohesive && !cohesive) {
1786: fOff += Nb * 2;
1787: qc += Nc;
1788: continue;
1789: }
1790: if (debug) {
1791: char title[1024];
1792: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1793: PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1794: }
1795: for (q = 0; q < Nq; ++q) {
1796: PetscFEGeom qgeom;
1797: PetscErrorCode ierr;
1799: qgeom.dimEmbed = fegeom.dimEmbed;
1800: qgeom.J = &fegeom.J[q * dE * dE];
1801: qgeom.invJ = &fegeom.invJ[q * dE * dE];
1802: qgeom.detJ = &fegeom.detJ[q];
1803: 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);
1804: if (transform) {
1805: gcoords = &coords[dE * Nq];
1806: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1807: } else {
1808: gcoords = &coords[dE * q];
1809: }
1810: for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1811: ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1812: if (ierr) {
1813: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1814: PetscCall(DMRestoreLocalVector(dm, &localX));
1815: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1816: }
1817: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1818: /* Call once for each face, except for lagrange field */
1819: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1820: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1821: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1822: for (fc = 0; fc < Nc; ++fc) {
1823: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1824: if (debug)
1825: 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),
1826: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1827: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1828: }
1829: }
1830: fOff += Nb;
1831: qc += Nc;
1832: localDiff[fields[f]] += elemDiff;
1833: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1834: }
1835: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1836: }
1837: if (label) {
1838: PetscCall(ISRestoreIndices(pointIS, &points));
1839: PetscCall(ISDestroy(&pointIS));
1840: }
1841: PetscCall(ISRestoreIndices(fieldIS, &fields));
1842: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1843: }
1844: PetscCall(DMRestoreLocalVector(dm, &localX));
1845: PetscCallMPI(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1846: PetscCall(PetscFree(localDiff));
1847: for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1848: PetscFunctionReturn(PETSC_SUCCESS);
1849: }
1851: /*@C
1852: 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.
1854: Collective
1856: Input Parameters:
1857: + dm - The `DM`
1858: . time - The time
1859: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1860: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1861: - X - The coefficient vector u_h
1863: Output Parameter:
1864: . D - A `Vec` which holds the difference ||u - u_h||_2 for each cell
1866: Level: developer
1868: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1869: @*/
1870: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1871: {
1872: PetscSection section;
1873: PetscQuadrature quad;
1874: Vec localX;
1875: PetscFEGeom fegeom;
1876: PetscScalar *funcVal, *interpolant;
1877: PetscReal *coords;
1878: const PetscReal *quadPoints, *quadWeights;
1879: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1881: PetscFunctionBegin;
1882: PetscCall(VecSet(D, 0.0));
1883: PetscCall(DMGetDimension(dm, &dim));
1884: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1885: PetscCall(DMGetLocalSection(dm, §ion));
1886: PetscCall(PetscSectionGetNumFields(section, &numFields));
1887: PetscCall(DMGetLocalVector(dm, &localX));
1888: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1889: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1890: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1891: for (field = 0; field < numFields; ++field) {
1892: PetscObject obj;
1893: PetscClassId id;
1894: PetscInt Nc;
1896: PetscCall(DMGetField(dm, field, NULL, &obj));
1897: PetscCall(PetscObjectGetClassId(obj, &id));
1898: if (id == PETSCFE_CLASSID) {
1899: PetscFE fe = (PetscFE)obj;
1901: PetscCall(PetscFEGetQuadrature(fe, &quad));
1902: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1903: } else if (id == PETSCFV_CLASSID) {
1904: PetscFV fv = (PetscFV)obj;
1906: PetscCall(PetscFVGetQuadrature(fv, &quad));
1907: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1908: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1909: numComponents += Nc;
1910: }
1911: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1912: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1913: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1914: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1915: for (c = cStart; c < cEnd; ++c) {
1916: PetscScalar *x = NULL;
1917: PetscScalar elemDiff = 0.0;
1918: PetscInt qc = 0;
1920: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1921: PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1923: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1924: PetscObject obj;
1925: PetscClassId id;
1926: void *const ctx = ctxs ? ctxs[field] : NULL;
1927: PetscInt Nb, Nc, q, fc;
1929: PetscCall(DMGetField(dm, field, NULL, &obj));
1930: PetscCall(PetscObjectGetClassId(obj, &id));
1931: if (id == PETSCFE_CLASSID) {
1932: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1933: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1934: } else if (id == PETSCFV_CLASSID) {
1935: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1936: Nb = 1;
1937: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1938: if (funcs[field]) {
1939: for (q = 0; q < Nq; ++q) {
1940: PetscFEGeom qgeom;
1942: qgeom.dimEmbed = fegeom.dimEmbed;
1943: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1944: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1945: qgeom.detJ = &fegeom.detJ[q];
1946: 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);
1947: PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1948: #if defined(needs_fix_with_return_code_argument)
1949: if (ierr) {
1950: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1951: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1952: PetscCall(DMRestoreLocalVector(dm, &localX));
1953: }
1954: #endif
1955: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1956: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1957: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1958: for (fc = 0; fc < Nc; ++fc) {
1959: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1960: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1961: }
1962: }
1963: }
1964: fieldOffset += Nb;
1965: qc += Nc;
1966: }
1967: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1968: PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1969: }
1970: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1971: PetscCall(DMRestoreLocalVector(dm, &localX));
1972: PetscCall(VecSqrtAbs(D));
1973: PetscFunctionReturn(PETSC_SUCCESS);
1974: }
1976: /*@
1977: DMPlexComputeL2FluxDiffVecLocal - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`
1979: Collective
1981: Input Parameters:
1982: + lu - The local `Vec` containing the primal solution
1983: . f - The field number for the potential
1984: . lmu - The local `Vec` containing the mixed solution
1985: - mf - The field number for the flux
1987: Output Parameter:
1988: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$
1990: Level: advanced
1992: Notes:
1993: We assume that the `DM` for each solution has the same topology, geometry, and quadrature.
1995: This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.
1997: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVec()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1998: @*/
1999: PetscErrorCode DMPlexComputeL2FluxDiffVecLocal(Vec lu, PetscInt f, Vec lmu, PetscInt mf, Vec eFlux)
2000: {
2001: DM dm, mdm, edm;
2002: PetscFE fe, mfe;
2003: PetscFEGeom fegeom;
2004: PetscQuadrature quad;
2005: const PetscReal *quadWeights;
2006: PetscReal *coords;
2007: PetscScalar *interpolant, *minterpolant, *earray;
2008: PetscInt cdim, mcdim, cStart, cEnd, Nc, mNc, qNc, Nq;
2009: MPI_Comm comm;
2011: PetscFunctionBegin;
2012: PetscCall(VecGetDM(lu, &dm));
2013: PetscCall(VecGetDM(lmu, &mdm));
2014: PetscCall(VecGetDM(eFlux, &edm));
2015: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2016: PetscCall(VecSet(eFlux, 0.0));
2018: // Check if the both problems are on the same mesh
2019: PetscCall(DMGetCoordinateDim(dm, &cdim));
2020: PetscCall(DMGetCoordinateDim(mdm, &mcdim));
2021: PetscCheck(cdim == mcdim, comm, PETSC_ERR_ARG_SIZ, "primal coordinate Dim %" PetscInt_FMT " != %" PetscInt_FMT " mixed coordinate Dim", cdim, mcdim);
2022: fegeom.dimEmbed = cdim;
2024: PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
2025: PetscCall(DMGetField(mdm, mf, NULL, (PetscObject *)&mfe));
2026: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2027: PetscCall(PetscFEGetNumComponents(mfe, &mNc));
2028: PetscCall(PetscFEGetQuadrature(fe, &quad));
2029: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
2030: PetscCheck(qNc == 1 || qNc == mNc, comm, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, mNc);
2032: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2033: PetscCall(VecGetArrayWrite(eFlux, &earray));
2034: 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));
2035: for (PetscInt c = cStart; c < cEnd; ++c) {
2036: PetscScalar *x = NULL;
2037: PetscScalar *mx = NULL;
2038: PetscScalar *eval = NULL;
2039: PetscReal fluxElemDiff = 0.0;
2041: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2042: PetscCall(DMPlexVecGetClosure(dm, NULL, lu, c, NULL, &x));
2043: PetscCall(DMPlexVecGetClosure(mdm, NULL, lmu, c, NULL, &mx));
2045: for (PetscInt q = 0; q < Nq; ++q) {
2046: PetscFEGeom qgeom;
2048: qgeom.dimEmbed = fegeom.dimEmbed;
2049: qgeom.J = &fegeom.J[q * cdim * cdim];
2050: qgeom.invJ = &fegeom.invJ[q * cdim * cdim];
2051: qgeom.detJ = &fegeom.detJ[q];
2053: 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);
2055: PetscCall(PetscFEInterpolate_Static(mfe, &mx[0], &qgeom, q, minterpolant));
2056: PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[0], &qgeom, q, interpolant));
2058: /* Now take the elementwise difference and store that in a vector. */
2059: for (PetscInt fc = 0; fc < mNc; ++fc) {
2060: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : fc)];
2061: fluxElemDiff += PetscSqr(PetscRealPart(interpolant[fc] - minterpolant[fc])) * wt * fegeom.detJ[q];
2062: }
2063: }
2064: PetscCall(DMPlexVecRestoreClosure(dm, NULL, lu, c, NULL, &x));
2065: PetscCall(DMPlexVecRestoreClosure(mdm, NULL, lmu, c, NULL, &mx));
2066: PetscCall(DMPlexPointGlobalRef(edm, c, earray, (void *)&eval));
2067: if (eval) eval[0] = fluxElemDiff;
2068: }
2069: PetscCall(PetscFree6(interpolant, minterpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2070: PetscCall(VecRestoreArrayWrite(eFlux, &earray));
2072: PetscCall(VecAssemblyBegin(eFlux));
2073: PetscCall(VecAssemblyEnd(eFlux));
2074: PetscCall(VecSqrtAbs(eFlux));
2075: PetscFunctionReturn(PETSC_SUCCESS);
2076: }
2078: /*@
2079: DMPlexComputeL2FluxDiffVec - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`
2081: Collective
2083: Input Parameters:
2084: + u - The global `Vec` containing the primal solution
2085: . f - The field number for the potential
2086: . mu - The global `Vec` containing the mixed solution
2087: - mf - The field number for the flux
2089: Output Parameter:
2090: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$
2092: Level: advanced
2094: Notes:
2095: We assume that the `DM` for each solution has the same topology, geometry, and quadrature.
2097: This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.
2099: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2100: @*/
2101: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
2102: {
2103: DM dm, mdm;
2104: Vec lu, lmu;
2106: PetscFunctionBegin;
2107: PetscCall(VecGetDM(u, &dm));
2108: PetscCall(DMGetLocalVector(dm, &lu));
2109: PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
2110: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));
2112: PetscCall(VecGetDM(mu, &mdm));
2113: PetscCall(DMGetLocalVector(mdm, &lmu));
2114: PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
2115: PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));
2117: PetscCall(DMPlexComputeL2FluxDiffVecLocal(lu, f, lmu, mf, eFlux));
2119: PetscCall(DMRestoreLocalVector(dm, &lu));
2120: PetscCall(DMRestoreLocalVector(mdm, &lmu));
2121: PetscFunctionReturn(PETSC_SUCCESS);
2122: }
2124: /*@
2125: DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1
2127: Collective
2129: Input Parameters:
2130: + dm - The `DM`
2131: - locX - The coefficient vector u_h
2133: Output Parameter:
2134: . locC - A `Vec` which holds the Clement interpolant of the function
2136: Level: developer
2138: Note:
2139: $ 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
2141: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2142: @*/
2143: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
2144: {
2145: PetscInt debug = ((DM_Plex *)dm->data)->printFEM;
2146: DM dmc;
2147: PetscQuadrature quad;
2148: PetscScalar *interpolant, *valsum;
2149: PetscFEGeom fegeom;
2150: PetscReal *coords;
2151: const PetscReal *quadPoints, *quadWeights;
2152: PetscInt dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;
2154: PetscFunctionBegin;
2155: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2156: PetscCall(VecGetDM(locC, &dmc));
2157: PetscCall(VecSet(locC, 0.0));
2158: PetscCall(DMGetDimension(dm, &dim));
2159: PetscCall(DMGetCoordinateDim(dm, &cdim));
2160: fegeom.dimEmbed = cdim;
2161: PetscCall(DMGetNumFields(dm, &Nf));
2162: PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2163: for (f = 0; f < Nf; ++f) {
2164: PetscObject obj;
2165: PetscClassId id;
2166: PetscInt fNc;
2168: PetscCall(DMGetField(dm, f, NULL, &obj));
2169: PetscCall(PetscObjectGetClassId(obj, &id));
2170: if (id == PETSCFE_CLASSID) {
2171: PetscFE fe = (PetscFE)obj;
2173: PetscCall(PetscFEGetQuadrature(fe, &quad));
2174: PetscCall(PetscFEGetNumComponents(fe, &fNc));
2175: } else if (id == PETSCFV_CLASSID) {
2176: PetscFV fv = (PetscFV)obj;
2178: PetscCall(PetscFVGetQuadrature(fv, &quad));
2179: PetscCall(PetscFVGetNumComponents(fv, &fNc));
2180: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2181: Nc += fNc;
2182: }
2183: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2184: PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
2185: PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
2186: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2187: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2188: for (v = vStart; v < vEnd; ++v) {
2189: PetscScalar volsum = 0.0;
2190: PetscInt *star = NULL;
2191: PetscInt starSize, st, fc;
2193: PetscCall(PetscArrayzero(valsum, Nc));
2194: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2195: for (st = 0; st < starSize * 2; st += 2) {
2196: const PetscInt cell = star[st];
2197: PetscScalar *val = &valsum[Nc];
2198: PetscScalar *x = NULL;
2199: PetscReal vol = 0.0;
2200: PetscInt foff = 0;
2202: if ((cell < cStart) || (cell >= cEnd)) continue;
2203: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2204: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2205: for (f = 0; f < Nf; ++f) {
2206: PetscObject obj;
2207: PetscClassId id;
2208: PetscInt Nb, fNc, q;
2210: PetscCall(PetscArrayzero(val, Nc));
2211: PetscCall(DMGetField(dm, f, NULL, &obj));
2212: PetscCall(PetscObjectGetClassId(obj, &id));
2213: if (id == PETSCFE_CLASSID) {
2214: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
2215: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2216: } else if (id == PETSCFV_CLASSID) {
2217: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
2218: Nb = 1;
2219: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2220: for (q = 0; q < Nq; ++q) {
2221: const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
2222: PetscFEGeom qgeom;
2224: qgeom.dimEmbed = fegeom.dimEmbed;
2225: qgeom.J = &fegeom.J[q * cdim * cdim];
2226: qgeom.invJ = &fegeom.invJ[q * cdim * cdim];
2227: qgeom.detJ = &fegeom.detJ[q];
2228: 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);
2229: PetscCheck(id == PETSCFE_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2230: PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
2231: for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
2232: vol += wt;
2233: }
2234: foff += Nb;
2235: }
2236: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2237: for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
2238: volsum += vol;
2239: if (debug) {
2240: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
2241: for (fc = 0; fc < Nc; ++fc) {
2242: if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2243: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
2244: }
2245: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2246: }
2247: }
2248: for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
2249: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2250: PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
2251: }
2252: PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2253: PetscFunctionReturn(PETSC_SUCCESS);
2254: }
2256: /*@
2257: DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1
2259: Collective
2261: Input Parameters:
2262: + dm - The `DM`
2263: - locX - The coefficient vector u_h
2265: Output Parameter:
2266: . locC - A `Vec` which holds the Clement interpolant of the gradient
2268: Level: developer
2270: Note:
2271: $\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
2273: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2274: @*/
2275: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
2276: {
2277: DM_Plex *mesh = (DM_Plex *)dm->data;
2278: PetscInt debug = mesh->printFEM;
2279: DM dmC;
2280: PetscQuadrature quad;
2281: PetscScalar *interpolant, *gradsum;
2282: PetscFEGeom fegeom;
2283: PetscReal *coords;
2284: const PetscReal *quadPoints, *quadWeights;
2285: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
2287: PetscFunctionBegin;
2288: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2289: PetscCall(VecGetDM(locC, &dmC));
2290: PetscCall(VecSet(locC, 0.0));
2291: PetscCall(DMGetDimension(dm, &dim));
2292: PetscCall(DMGetCoordinateDim(dm, &coordDim));
2293: fegeom.dimEmbed = coordDim;
2294: PetscCall(DMGetNumFields(dm, &numFields));
2295: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2296: for (field = 0; field < numFields; ++field) {
2297: PetscObject obj;
2298: PetscClassId id;
2299: PetscInt Nc;
2301: PetscCall(DMGetField(dm, field, NULL, &obj));
2302: PetscCall(PetscObjectGetClassId(obj, &id));
2303: if (id == PETSCFE_CLASSID) {
2304: PetscFE fe = (PetscFE)obj;
2306: PetscCall(PetscFEGetQuadrature(fe, &quad));
2307: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2308: } else if (id == PETSCFV_CLASSID) {
2309: PetscFV fv = (PetscFV)obj;
2311: PetscCall(PetscFVGetQuadrature(fv, &quad));
2312: PetscCall(PetscFVGetNumComponents(fv, &Nc));
2313: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2314: numComponents += Nc;
2315: }
2316: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2317: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
2318: 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));
2319: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2320: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2321: for (v = vStart; v < vEnd; ++v) {
2322: PetscScalar volsum = 0.0;
2323: PetscInt *star = NULL;
2324: PetscInt starSize, st, d, fc;
2326: PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
2327: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2328: for (st = 0; st < starSize * 2; st += 2) {
2329: const PetscInt cell = star[st];
2330: PetscScalar *grad = &gradsum[coordDim * numComponents];
2331: PetscScalar *x = NULL;
2332: PetscReal vol = 0.0;
2334: if ((cell < cStart) || (cell >= cEnd)) continue;
2335: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2336: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2337: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
2338: PetscObject obj;
2339: PetscClassId id;
2340: PetscInt Nb, Nc, q, qc = 0;
2342: PetscCall(PetscArrayzero(grad, coordDim * numComponents));
2343: PetscCall(DMGetField(dm, field, NULL, &obj));
2344: PetscCall(PetscObjectGetClassId(obj, &id));
2345: if (id == PETSCFE_CLASSID) {
2346: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
2347: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2348: } else if (id == PETSCFV_CLASSID) {
2349: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
2350: Nb = 1;
2351: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2352: for (q = 0; q < Nq; ++q) {
2353: PetscFEGeom qgeom;
2355: qgeom.dimEmbed = fegeom.dimEmbed;
2356: qgeom.J = &fegeom.J[q * coordDim * coordDim];
2357: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
2358: qgeom.detJ = &fegeom.detJ[q];
2359: 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);
2360: PetscCheck(id == PETSCFE_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2361: PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
2362: for (fc = 0; fc < Nc; ++fc) {
2363: const PetscReal wt = quadWeights[q * qNc + qc];
2365: for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
2366: }
2367: vol += quadWeights[q * qNc] * fegeom.detJ[q];
2368: }
2369: fieldOffset += Nb;
2370: qc += Nc;
2371: }
2372: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2373: for (fc = 0; fc < numComponents; ++fc) {
2374: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
2375: }
2376: volsum += vol;
2377: if (debug) {
2378: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
2379: for (fc = 0; fc < numComponents; ++fc) {
2380: for (d = 0; d < coordDim; ++d) {
2381: if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2382: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
2383: }
2384: }
2385: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2386: }
2387: }
2388: for (fc = 0; fc < numComponents; ++fc) {
2389: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2390: }
2391: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2392: PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2393: }
2394: PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2395: PetscFunctionReturn(PETSC_SUCCESS);
2396: }
2398: PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec locX, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, PetscCtx ctx)
2399: {
2400: DM dmAux = NULL, plexA = NULL;
2401: PetscDS prob, probAux = NULL;
2402: PetscSection section, sectionAux;
2403: Vec locA;
2404: PetscInt dim, numCells = cEnd - cStart, c, f;
2405: PetscBool useFVM = PETSC_FALSE;
2406: /* DS */
2407: PetscInt Nf, totDim, *uOff, *uOff_x, numConstants;
2408: PetscInt NfAux, totDimAux, *aOff;
2409: PetscScalar *u, *a = NULL;
2410: const PetscScalar *constants;
2411: /* Geometry */
2412: PetscFEGeom *cgeomFEM;
2413: DM dmGrad;
2414: PetscQuadrature affineQuad = NULL;
2415: Vec cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2416: PetscFVCellGeom *cgeomFVM;
2417: const PetscScalar *lgrad;
2418: PetscInt maxDegree;
2419: DMField coordField;
2420: IS cellIS;
2422: PetscFunctionBegin;
2423: PetscCall(DMGetDS(dm, &prob));
2424: PetscCall(DMGetDimension(dm, &dim));
2425: PetscCall(DMGetLocalSection(dm, §ion));
2426: PetscCall(DMGetNumFields(dm, &Nf));
2427: /* Determine which discretizations we have */
2428: for (f = 0; f < Nf; ++f) {
2429: PetscObject obj;
2430: PetscClassId id;
2432: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2433: PetscCall(PetscObjectGetClassId(obj, &id));
2434: if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2435: }
2436: /* Read DS information */
2437: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2438: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2439: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2440: PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2441: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2442: /* Read Auxiliary DS information */
2443: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2444: if (locA) {
2445: PetscCall(VecGetDM(locA, &dmAux));
2446: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2447: PetscCall(DMGetDS(dmAux, &probAux));
2448: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2449: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2450: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2451: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2452: }
2453: /* Allocate data arrays */
2454: PetscCall(PetscCalloc1(numCells * totDim, &u));
2455: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2456: /* Read out geometry */
2457: PetscCall(DMGetCoordinateField(dm, &coordField));
2458: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2459: if (maxDegree <= 1) {
2460: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2461: if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &cgeomFEM));
2462: }
2463: if (useFVM) {
2464: PetscFV fv = NULL;
2465: Vec grad;
2466: PetscInt fStart, fEnd;
2467: PetscBool compGrad;
2469: for (f = 0; f < Nf; ++f) {
2470: PetscObject obj;
2471: PetscClassId id;
2473: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2474: PetscCall(PetscObjectGetClassId(obj, &id));
2475: if (id == PETSCFV_CLASSID) {
2476: fv = (PetscFV)obj;
2477: break;
2478: }
2479: }
2480: PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2481: PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2482: PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2483: PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2484: PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2485: PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2486: /* Reconstruct and limit cell gradients */
2487: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2488: PetscCall(DMGetGlobalVector(dmGrad, &grad));
2489: PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2490: /* Communicate gradient values */
2491: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2492: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2493: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2494: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2495: /* Handle non-essential (e.g. outflow) boundary values */
2496: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2497: PetscCall(VecGetArrayRead(locGrad, &lgrad));
2498: }
2499: /* Read out data from inputs */
2500: for (c = cStart; c < cEnd; ++c) {
2501: PetscScalar *x = NULL;
2503: PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2504: for (PetscInt i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2505: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2506: if (dmAux) {
2507: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, c, NULL, &x));
2508: for (PetscInt i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2509: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, c, NULL, &x));
2510: }
2511: }
2512: /* Do integration for each field */
2513: for (f = 0; f < Nf; ++f) {
2514: PetscObject obj;
2515: PetscClassId id;
2516: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
2518: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2519: PetscCall(PetscObjectGetClassId(obj, &id));
2520: if (id == PETSCFE_CLASSID) {
2521: PetscFE fe = (PetscFE)obj;
2522: PetscQuadrature q;
2523: PetscFEGeom *chunkGeom = NULL;
2524: PetscInt Nq, Nb;
2526: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2527: PetscCall(PetscFEGetQuadrature(fe, &q));
2528: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2529: PetscCall(PetscFEGetDimension(fe, &Nb));
2530: blockSize = Nb * Nq;
2531: batchSize = numBlocks * blockSize;
2532: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2533: numChunks = numCells / (numBatches * batchSize);
2534: Ne = numChunks * numBatches * batchSize;
2535: Nr = numCells % (numBatches * batchSize);
2536: offset = numCells - Nr;
2537: if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FEGEOM_BASIC, &cgeomFEM));
2538: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2539: PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2540: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2541: PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &cintegral[offset * Nf]));
2542: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2543: if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2544: } else if (id == PETSCFV_CLASSID) {
2545: PetscInt foff;
2546: PetscPointFn *obj_func;
2548: PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2549: PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2550: if (obj_func) {
2551: for (c = 0; c < numCells; ++c) {
2552: PetscScalar *u_x;
2553: PetscScalar lint = 0.;
2555: PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2556: 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);
2557: cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2558: }
2559: }
2560: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2561: }
2562: /* Cleanup data arrays */
2563: if (useFVM) {
2564: PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2565: PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2566: PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2567: PetscCall(VecDestroy(&faceGeometryFVM));
2568: PetscCall(VecDestroy(&cellGeometryFVM));
2569: PetscCall(DMDestroy(&dmGrad));
2570: }
2571: if (dmAux) PetscCall(PetscFree(a));
2572: PetscCall(DMDestroy(&plexA));
2573: PetscCall(PetscFree(u));
2574: /* Cleanup */
2575: if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2576: PetscCall(PetscQuadratureDestroy(&affineQuad));
2577: PetscCall(ISDestroy(&cellIS));
2578: PetscFunctionReturn(PETSC_SUCCESS);
2579: }
2581: /*@
2582: DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user
2584: Input Parameters:
2585: + dm - The mesh
2586: . X - Global input vector
2587: - ctx - The application context
2589: Output Parameter:
2590: . integral - Integral for each field
2592: Level: developer
2594: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2595: @*/
2596: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, PetscCtx ctx)
2597: {
2598: PetscInt printFEM;
2599: PetscScalar *cintegral, *lintegral;
2600: PetscInt Nf, f, cellHeight, cStart, cEnd, cell;
2601: Vec locX;
2603: PetscFunctionBegin;
2606: PetscAssertPointer(integral, 3);
2607: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2608: PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2609: PetscCall(DMGetNumFields(dm, &Nf));
2610: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2611: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2612: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2613: PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2614: /* Get local solution with boundary values */
2615: PetscCall(DMGetLocalVector(dm, &locX));
2616: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2617: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2618: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2619: PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, ctx));
2620: PetscCall(DMRestoreLocalVector(dm, &locX));
2621: printFEM = ((DM_Plex *)dm->data)->printFEM;
2622: /* Sum up values */
2623: for (cell = cStart; cell < cEnd; ++cell) {
2624: const PetscInt c = cell - cStart;
2626: if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2627: for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2628: }
2629: PetscCallMPI(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2630: if (printFEM) {
2631: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2632: for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2633: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2634: }
2635: PetscCall(PetscFree2(lintegral, cintegral));
2636: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2637: PetscCall(DMDestroy(&dm));
2638: PetscFunctionReturn(PETSC_SUCCESS);
2639: }
2641: /*@
2642: DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user
2644: Input Parameters:
2645: + dm - The mesh
2646: . X - Global input vector
2647: - ctx - The application context
2649: Output Parameter:
2650: . F - Cellwise integrals for each field
2652: Level: developer
2654: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2655: @*/
2656: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, PetscCtx ctx)
2657: {
2658: PetscInt printFEM;
2659: DM dmF;
2660: PetscSection sectionF = NULL;
2661: PetscScalar *cintegral, *af;
2662: PetscInt Nf, f, cellHeight, cStart, cEnd, cell, n;
2663: Vec locX;
2665: PetscFunctionBegin;
2669: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2670: PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2671: PetscCall(DMGetNumFields(dm, &Nf));
2672: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2673: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2674: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2675: PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2676: /* Get local solution with boundary values */
2677: PetscCall(DMGetLocalVector(dm, &locX));
2678: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2679: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2680: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2681: PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, ctx));
2682: PetscCall(DMRestoreLocalVector(dm, &locX));
2683: /* Put values in F */
2684: PetscCall(VecGetArray(F, &af));
2685: PetscCall(VecGetDM(F, &dmF));
2686: if (dmF) PetscCall(DMGetLocalSection(dmF, §ionF));
2687: PetscCall(VecGetLocalSize(F, &n));
2688: PetscCheck(n >= (cEnd - cStart) * Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Vector size %" PetscInt_FMT " < %" PetscInt_FMT, n, (cEnd - cStart) * Nf);
2689: printFEM = ((DM_Plex *)dm->data)->printFEM;
2690: for (cell = cStart; cell < cEnd; ++cell) {
2691: const PetscInt c = cell - cStart;
2692: PetscInt dof = Nf, off = c * Nf;
2694: if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2695: if (sectionF) {
2696: PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2697: PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2698: }
2699: PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2700: for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2701: }
2702: PetscCall(VecRestoreArray(F, &af));
2703: PetscCall(PetscFree(cintegral));
2704: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2705: PetscCall(DMDestroy(&dm));
2706: PetscFunctionReturn(PETSC_SUCCESS);
2707: }
2709: 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)
2710: {
2711: DM plex = NULL, plexA = NULL;
2712: DMEnclosureType encAux;
2713: PetscDS prob, probAux = NULL;
2714: PetscSection section, sectionAux = NULL;
2715: Vec locA = NULL;
2716: DMField coordField;
2717: PetscInt Nf, totDim, *uOff, *uOff_x;
2718: PetscInt NfAux = 0, totDimAux = 0, *aOff = NULL;
2719: PetscScalar *u, *a = NULL;
2720: const PetscScalar *constants;
2721: PetscInt numConstants, f;
2723: PetscFunctionBegin;
2724: PetscCall(DMGetCoordinateField(dm, &coordField));
2725: PetscCall(DMConvert(dm, DMPLEX, &plex));
2726: PetscCall(DMGetDS(dm, &prob));
2727: PetscCall(DMGetLocalSection(dm, §ion));
2728: PetscCall(PetscSectionGetNumFields(section, &Nf));
2729: /* Determine which discretizations we have */
2730: for (f = 0; f < Nf; ++f) {
2731: PetscObject obj;
2732: PetscClassId id;
2734: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2735: PetscCall(PetscObjectGetClassId(obj, &id));
2736: PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2737: }
2738: /* Read DS information */
2739: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2740: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2741: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2742: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2743: /* Read Auxiliary DS information */
2744: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2745: if (locA) {
2746: DM dmAux;
2748: PetscCall(VecGetDM(locA, &dmAux));
2749: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2750: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2751: PetscCall(DMGetDS(dmAux, &probAux));
2752: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2753: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2754: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2755: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2756: }
2757: /* Integrate over points */
2758: {
2759: PetscFEGeom *fgeom, *chunkGeom = NULL;
2760: PetscInt maxDegree;
2761: PetscQuadrature qGeom = NULL;
2762: const PetscInt *points;
2763: PetscInt numFaces, face, Nq, field;
2764: PetscInt numChunks, chunkSize, chunk, Nr, offset;
2766: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2767: PetscCall(ISGetIndices(pointIS, &points));
2768: PetscCall(PetscCalloc2(numFaces * totDim, &u, (locA ? (size_t)numFaces * totDimAux : 0), &a));
2769: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2770: for (face = 0; face < numFaces; ++face) {
2771: const PetscInt point = points[face], *support;
2772: PetscScalar *x = NULL;
2774: PetscCall(DMPlexGetSupport(dm, point, &support));
2775: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2776: for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2777: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2778: if (locA) {
2779: PetscInt subp;
2780: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2781: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2782: for (PetscInt i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2783: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2784: }
2785: }
2786: for (field = 0; field < Nf; ++field) {
2787: PetscFE fe;
2789: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2790: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2791: if (!qGeom) {
2792: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2793: PetscCall(PetscObjectReference((PetscObject)qGeom));
2794: }
2795: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2796: PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
2797: /* Get blocking */
2798: {
2799: PetscQuadrature q;
2800: PetscInt numBatches, batchSize, numBlocks, blockSize;
2801: PetscInt Nq, Nb;
2803: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2804: PetscCall(PetscFEGetQuadrature(fe, &q));
2805: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2806: PetscCall(PetscFEGetDimension(fe, &Nb));
2807: blockSize = Nb * Nq;
2808: batchSize = numBlocks * blockSize;
2809: chunkSize = numBatches * batchSize;
2810: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2811: numChunks = numFaces / chunkSize;
2812: Nr = numFaces % chunkSize;
2813: offset = numFaces - Nr;
2814: }
2815: /* Do integration for each field */
2816: for (chunk = 0; chunk < numChunks; ++chunk) {
2817: PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2818: PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], chunkSize, chunkGeom, &u[chunk * chunkSize * totDim], probAux, PetscSafePointerPlusOffset(a, chunk * chunkSize * totDimAux), &fintegral[chunk * chunkSize * Nf]));
2819: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2820: }
2821: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2822: PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf]));
2823: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2824: /* Cleanup data arrays */
2825: PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
2826: PetscCall(PetscQuadratureDestroy(&qGeom));
2827: }
2828: PetscCall(PetscFree2(u, a));
2829: PetscCall(ISRestoreIndices(pointIS, &points));
2830: }
2831: PetscCall(DMDestroy(&plex));
2832: PetscCall(DMDestroy(&plexA));
2833: PetscFunctionReturn(PETSC_SUCCESS);
2834: }
2836: /*@C
2837: DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2839: Input Parameters:
2840: + dm - The mesh
2841: . X - Global input vector
2842: . label - The boundary `DMLabel`
2843: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2844: . vals - The label values to use, or NULL for all values
2845: . funcs - The functions to integrate along the boundary for each field
2846: - ctx - The application context
2848: Output Parameter:
2849: . integral - Integral for each field
2851: Level: developer
2853: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2854: @*/
2855: 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)
2856: {
2857: Vec locX;
2858: PetscSection section;
2859: DMLabel depthLabel;
2860: IS facetIS;
2861: PetscInt dim, Nf, f, v;
2863: PetscFunctionBegin;
2867: if (vals) PetscAssertPointer(vals, 5);
2868: PetscAssertPointer(integral, 7);
2869: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2870: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2871: PetscCall(DMGetDimension(dm, &dim));
2872: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2873: /* Filter out ghost facets (SF leaves) so that each boundary facet is only
2874: counted on one rank. Without this, shared facets at partition boundaries
2875: are integrated on multiple ranks, causing double-counting after MPI sum. */
2876: if (facetIS) {
2877: PetscSF sf;
2878: PetscInt nleaves;
2879: const PetscInt *leaves;
2881: PetscCall(DMGetPointSF(dm, &sf));
2882: PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
2883: if (nleaves > 0 && leaves) {
2884: IS leafIS, ownedFacetIS;
2886: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nleaves, leaves, PETSC_USE_POINTER, &leafIS));
2887: PetscCall(ISDifference(facetIS, leafIS, &ownedFacetIS));
2888: PetscCall(ISDestroy(&leafIS));
2889: PetscCall(ISDestroy(&facetIS));
2890: facetIS = ownedFacetIS;
2891: }
2892: }
2893: PetscCall(DMGetLocalSection(dm, §ion));
2894: PetscCall(PetscSectionGetNumFields(section, &Nf));
2895: /* Get local solution with boundary values */
2896: PetscCall(DMGetLocalVector(dm, &locX));
2897: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2898: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2899: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2900: /* Loop over label values */
2901: PetscCall(PetscArrayzero(integral, Nf));
2902: for (v = 0; v < numVals; ++v) {
2903: IS pointIS;
2904: PetscInt numFaces;
2905: PetscScalar *fintegral;
2907: PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2908: if (!pointIS) continue; /* No points with that id on this process */
2909: {
2910: IS isectIS;
2912: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2913: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2914: PetscCall(ISDestroy(&pointIS));
2915: pointIS = isectIS;
2916: }
2917: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2918: PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2919: PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, funcs, fintegral, ctx));
2920: /* Sum point contributions into integral */
2921: for (f = 0; f < Nf; ++f)
2922: for (PetscInt face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2923: PetscCall(PetscFree(fintegral));
2924: PetscCall(ISDestroy(&pointIS));
2925: }
2926: PetscCall(DMRestoreLocalVector(dm, &locX));
2927: PetscCall(ISDestroy(&facetIS));
2928: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2929: PetscFunctionReturn(PETSC_SUCCESS);
2930: }
2932: /*@
2933: DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix from the coarse `DM` to a uniformly refined `DM`.
2935: Input Parameters:
2936: + dmc - The coarse mesh
2937: . dmf - The fine mesh
2938: . isRefined - Flag indicating regular refinement, rather than the same topology
2939: - ctx - The application context
2941: Output Parameter:
2942: . In - The interpolation matrix
2944: Level: developer
2946: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`
2947: @*/
2948: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, PetscCtx ctx)
2949: {
2950: DM_Plex *mesh = (DM_Plex *)dmc->data;
2951: const char *name = "Interpolator";
2952: PetscFE *feRef;
2953: PetscFV *fvRef;
2954: PetscSection fsection, fglobalSection;
2955: PetscSection csection, cglobalSection;
2956: PetscScalar *elemMat;
2957: PetscInt dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2958: PetscInt cTotDim = 0, rTotDim = 0;
2960: PetscFunctionBegin;
2961: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2962: PetscCall(DMGetDimension(dmf, &dim));
2963: PetscCall(DMGetLocalSection(dmf, &fsection));
2964: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2965: PetscCall(DMGetLocalSection(dmc, &csection));
2966: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2967: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2968: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2969: PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2970: for (f = 0; f < Nf; ++f) {
2971: PetscObject obj, objc;
2972: PetscClassId id, idc;
2973: PetscInt rNb = 0, Nc = 0, cNb = 0;
2975: PetscCall(DMGetField(dmf, f, NULL, &obj));
2976: PetscCall(PetscObjectGetClassId(obj, &id));
2977: if (id == PETSCFE_CLASSID) {
2978: PetscFE fe = (PetscFE)obj;
2980: if (isRefined) PetscCall(PetscFERefine(fe, &feRef[f]));
2981: else {
2982: PetscCall(PetscObjectReference((PetscObject)fe));
2983: feRef[f] = fe;
2984: }
2985: PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2986: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2987: } else if (id == PETSCFV_CLASSID) {
2988: PetscFV fv = (PetscFV)obj;
2989: PetscDualSpace Q;
2991: if (isRefined) PetscCall(PetscFVRefine(fv, &fvRef[f]));
2992: else {
2993: PetscCall(PetscObjectReference((PetscObject)fv));
2994: fvRef[f] = fv;
2995: }
2996: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2997: PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2998: PetscCall(PetscFVGetDualSpace(fv, &Q));
2999: PetscCall(PetscFVGetNumComponents(fv, &Nc));
3000: }
3001: PetscCall(DMGetField(dmc, f, NULL, &objc));
3002: PetscCall(PetscObjectGetClassId(objc, &idc));
3003: if (idc == PETSCFE_CLASSID) {
3004: PetscFE fe = (PetscFE)objc;
3006: PetscCall(PetscFEGetDimension(fe, &cNb));
3007: } else if (id == PETSCFV_CLASSID) {
3008: PetscFV fv = (PetscFV)obj;
3009: PetscDualSpace Q;
3011: PetscCall(PetscFVGetDualSpace(fv, &Q));
3012: PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
3013: }
3014: rTotDim += rNb;
3015: cTotDim += cNb;
3016: }
3017: PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
3018: PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
3019: for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
3020: PetscDualSpace Qref;
3021: PetscQuadrature f;
3022: const PetscReal *qpoints, *qweights;
3023: PetscReal *points;
3024: PetscInt npoints = 0, Nc, Np, fpdim, i, k, p, d;
3026: /* Compose points from all dual basis functionals */
3027: if (feRef[fieldI]) {
3028: PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
3029: PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
3030: } else {
3031: PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
3032: PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
3033: }
3034: PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
3035: for (i = 0; i < fpdim; ++i) {
3036: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3037: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
3038: npoints += Np;
3039: }
3040: PetscCall(PetscMalloc1(npoints * dim, &points));
3041: for (i = 0, k = 0; i < fpdim; ++i) {
3042: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3043: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
3044: for (p = 0; p < Np; ++p, ++k)
3045: for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
3046: }
3048: for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
3049: PetscObject obj;
3050: PetscClassId id;
3051: PetscInt NcJ = 0, cpdim = 0, j, qNc;
3053: PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
3054: PetscCall(PetscObjectGetClassId(obj, &id));
3055: if (id == PETSCFE_CLASSID) {
3056: PetscFE fe = (PetscFE)obj;
3057: PetscTabulation T = NULL;
3059: /* Evaluate basis at points */
3060: PetscCall(PetscFEGetNumComponents(fe, &NcJ));
3061: PetscCall(PetscFEGetDimension(fe, &cpdim));
3062: /* For now, fields only interpolate themselves */
3063: if (fieldI == fieldJ) {
3064: 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);
3065: PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
3066: for (i = 0, k = 0; i < fpdim; ++i) {
3067: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3068: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3069: 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);
3070: for (p = 0; p < Np; ++p, ++k) {
3071: for (j = 0; j < cpdim; ++j) {
3072: /*
3073: cTotDim: Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
3074: offsetI, offsetJ: Offsets into the larger element interpolation matrix for different fields
3075: fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
3076: qNC, Nc, Ncj, c: Number of components in this field
3077: Np, p: Number of quad points in the fine grid functional i
3078: k: i*Np + p, overall point number for the interpolation
3079: */
3080: 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];
3081: }
3082: }
3083: }
3084: PetscCall(PetscTabulationDestroy(&T));
3085: }
3086: } else if (id == PETSCFV_CLASSID) {
3087: PetscFV fv = (PetscFV)obj;
3089: /* Evaluate constant function at points */
3090: PetscCall(PetscFVGetNumComponents(fv, &NcJ));
3091: cpdim = 1;
3092: /* For now, fields only interpolate themselves */
3093: if (fieldI == fieldJ) {
3094: 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);
3095: for (i = 0, k = 0; i < fpdim; ++i) {
3096: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3097: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3098: 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);
3099: for (p = 0; p < Np; ++p, ++k) {
3100: for (j = 0; j < cpdim; ++j) {
3101: for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
3102: }
3103: }
3104: }
3105: }
3106: }
3107: offsetJ += cpdim;
3108: }
3109: offsetI += fpdim;
3110: PetscCall(PetscFree(points));
3111: }
3112: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
3113: /* Preallocate matrix */
3114: {
3115: Mat preallocator;
3116: PetscScalar *vals;
3117: PetscInt *cellCIndices, *cellFIndices;
3118: PetscInt locRows, locCols, cell;
3120: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3121: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
3122: PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
3123: PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3124: PetscCall(MatSetUp(preallocator));
3125: PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
3126: if (locRows || locCols) {
3127: for (cell = cStart; cell < cEnd; ++cell) {
3128: if (isRefined) {
3129: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
3130: PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
3131: } else {
3132: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
3133: }
3134: }
3135: }
3136: PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
3137: PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
3138: PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
3139: PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
3140: PetscCall(MatDestroy(&preallocator));
3141: }
3142: /* Fill matrix */
3143: PetscCall(MatZeroEntries(In));
3144: for (c = cStart; c < cEnd; ++c) {
3145: if (isRefined) {
3146: PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
3147: } else {
3148: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
3149: }
3150: }
3151: for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
3152: PetscCall(PetscFree2(feRef, fvRef));
3153: PetscCall(PetscFree(elemMat));
3154: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3155: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3156: if (mesh->printFEM > 1) {
3157: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
3158: PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
3159: PetscCall(MatView(In, NULL));
3160: }
3161: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3162: PetscFunctionReturn(PETSC_SUCCESS);
3163: }
3165: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, PetscCtx ctx)
3166: {
3167: SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
3168: }
3170: /*@
3171: DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix from the coarse `DM` to a non-nested fine `DM`.
3173: Input Parameters:
3174: + dmf - The fine mesh
3175: . dmc - The coarse mesh
3176: - ctx - The application context
3178: Output Parameter:
3179: . In - The interpolation matrix
3181: Level: developer
3183: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3184: @*/
3185: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, PetscCtx ctx)
3186: {
3187: DM_Plex *mesh = (DM_Plex *)dmf->data;
3188: const char *name = "Interpolator";
3189: PetscDS prob;
3190: Mat interp;
3191: PetscSection fsection, globalFSection;
3192: PetscSection csection, globalCSection;
3193: PetscInt locRows, locCols;
3194: PetscReal *x, *v0, *J, *invJ, detJ;
3195: PetscReal *v0c, *Jc, *invJc, detJc;
3196: PetscScalar *elemMat;
3197: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;
3199: PetscFunctionBegin;
3200: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3201: PetscCall(DMGetCoordinateDim(dmc, &dim));
3202: PetscCall(DMGetDS(dmc, &prob));
3203: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3204: PetscCall(PetscDSGetNumFields(prob, &Nf));
3205: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3206: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3207: PetscCall(DMGetLocalSection(dmf, &fsection));
3208: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3209: PetscCall(DMGetLocalSection(dmc, &csection));
3210: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3211: PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
3212: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3213: PetscCall(PetscMalloc1(totDim, &elemMat));
3215: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3216: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
3217: PetscCall(MatSetType(interp, MATPREALLOCATOR));
3218: PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3219: PetscCall(MatSetUp(interp));
3220: for (s = 0; s < 2; ++s) {
3221: for (field = 0; field < Nf; ++field) {
3222: PetscObject obj;
3223: PetscClassId id;
3224: PetscDualSpace Q = NULL;
3225: PetscTabulation T = NULL;
3226: PetscQuadrature f;
3227: const PetscReal *qpoints, *qweights;
3228: PetscInt Nc, qNc, Np, fpdim, off, i, d;
3230: PetscCall(PetscDSGetFieldOffset(prob, field, &off));
3231: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3232: PetscCall(PetscObjectGetClassId(obj, &id));
3233: if (id == PETSCFE_CLASSID) {
3234: PetscFE fe = (PetscFE)obj;
3236: PetscCall(PetscFEGetDualSpace(fe, &Q));
3237: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3238: if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
3239: } else if (id == PETSCFV_CLASSID) {
3240: PetscFV fv = (PetscFV)obj;
3242: PetscCall(PetscFVGetDualSpace(fv, &Q));
3243: Nc = 1;
3244: } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
3245: PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
3246: /* For each fine grid cell */
3247: for (cell = cStart; cell < cEnd; ++cell) {
3248: PetscInt *findices, *cindices;
3249: PetscInt numFIndices, numCIndices;
3251: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3252: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3253: PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
3254: for (i = 0; i < fpdim; ++i) {
3255: Vec pointVec;
3256: PetscScalar *pV;
3257: PetscSF coarseCellSF = NULL;
3258: const PetscSFNode *coarseCells;
3259: PetscInt numCoarseCells, cpdim, row = findices[i + off], q, c, j;
3261: /* Get points from the dual basis functional quadrature */
3262: PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
3263: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
3264: 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);
3265: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
3266: PetscCall(VecSetBlockSize(pointVec, dim));
3267: PetscCall(VecGetArray(pointVec, &pV));
3268: for (q = 0; q < Np; ++q) {
3269: const PetscReal xi0[3] = {-1., -1., -1.};
3271: /* Transform point to real space */
3272: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3273: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3274: }
3275: PetscCall(VecRestoreArray(pointVec, &pV));
3276: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3277: /* OPT: Read this out from preallocation information */
3278: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3279: /* Update preallocation info */
3280: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3281: PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3282: PetscCall(VecGetArray(pointVec, &pV));
3283: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3284: PetscReal pVReal[3];
3285: const PetscReal xi0[3] = {-1., -1., -1.};
3287: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3288: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim));
3289: else cpdim = 1;
3291: if (s) {
3292: /* Transform points from real space to coarse reference space */
3293: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3294: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3295: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3297: if (id == PETSCFE_CLASSID) {
3298: /* Evaluate coarse basis on contained point */
3299: PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
3300: PetscCall(PetscArrayzero(elemMat, cpdim));
3301: /* Get elemMat entries by multiplying by weight */
3302: for (j = 0; j < cpdim; ++j) {
3303: for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
3304: }
3305: } else {
3306: for (j = 0; j < cpdim; ++j) {
3307: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
3308: }
3309: }
3310: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3311: }
3312: /* Update interpolator */
3313: PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
3314: PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
3315: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3316: }
3317: PetscCall(VecRestoreArray(pointVec, &pV));
3318: PetscCall(PetscSFDestroy(&coarseCellSF));
3319: PetscCall(VecDestroy(&pointVec));
3320: }
3321: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3322: }
3323: if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3324: }
3325: if (!s) {
3326: PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
3327: PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
3328: PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
3329: PetscCall(MatDestroy(&interp));
3330: interp = In;
3331: }
3332: }
3333: PetscCall(PetscFree3(v0, J, invJ));
3334: PetscCall(PetscFree3(v0c, Jc, invJc));
3335: PetscCall(PetscFree(elemMat));
3336: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3337: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3338: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3339: PetscFunctionReturn(PETSC_SUCCESS);
3340: }
3342: /*@
3343: DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix from the coarse `DM` to a non-nested fine `DM`.
3345: Input Parameters:
3346: + dmf - The fine mesh
3347: . dmc - The coarse mesh
3348: - ctx - The application context
3350: Output Parameter:
3351: . mass - The mass matrix
3353: Level: developer
3355: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`
3356: @*/
3357: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, PetscCtx ctx)
3358: {
3359: DM_Plex *mesh = (DM_Plex *)dmf->data;
3360: const char *name = "Mass Matrix";
3361: PetscDS prob;
3362: PetscSection fsection, csection, globalFSection, globalCSection;
3363: PetscHSetIJ ht;
3364: PetscLayout rLayout;
3365: PetscInt *dnz, *onz;
3366: PetscInt locRows, rStart, rEnd;
3367: PetscReal *x, *v0, *J, *invJ, detJ;
3368: PetscReal *v0c, *Jc, *invJc, detJc;
3369: PetscScalar *elemMat;
3370: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
3372: PetscFunctionBegin;
3373: PetscCall(DMGetCoordinateDim(dmc, &dim));
3374: PetscCall(DMGetDS(dmc, &prob));
3375: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3376: PetscCall(PetscDSGetNumFields(prob, &Nf));
3377: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3378: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3379: PetscCall(DMGetLocalSection(dmf, &fsection));
3380: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3381: PetscCall(DMGetLocalSection(dmc, &csection));
3382: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3383: PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
3384: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3385: PetscCall(PetscMalloc1(totDim, &elemMat));
3387: PetscCall(MatGetLocalSize(mass, &locRows, NULL));
3388: PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
3389: PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
3390: PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
3391: PetscCall(PetscLayoutSetUp(rLayout));
3392: PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
3393: PetscCall(PetscLayoutDestroy(&rLayout));
3394: PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
3395: PetscCall(PetscHSetIJCreate(&ht));
3396: for (field = 0; field < Nf; ++field) {
3397: PetscObject obj;
3398: PetscClassId id;
3399: PetscQuadrature quad;
3400: const PetscReal *qpoints;
3401: PetscInt Nq, Nc, i, d;
3403: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3404: PetscCall(PetscObjectGetClassId(obj, &id));
3405: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3406: else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3407: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
3408: /* For each fine grid cell */
3409: for (cell = cStart; cell < cEnd; ++cell) {
3410: Vec pointVec;
3411: PetscScalar *pV;
3412: PetscSF coarseCellSF = NULL;
3413: const PetscSFNode *coarseCells;
3414: PetscInt numCoarseCells, q, c;
3415: PetscInt *findices, *cindices;
3416: PetscInt numFIndices, numCIndices;
3418: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3419: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3420: /* Get points from the quadrature */
3421: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3422: PetscCall(VecSetBlockSize(pointVec, dim));
3423: PetscCall(VecGetArray(pointVec, &pV));
3424: for (q = 0; q < Nq; ++q) {
3425: const PetscReal xi0[3] = {-1., -1., -1.};
3427: /* Transform point to real space */
3428: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3429: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3430: }
3431: PetscCall(VecRestoreArray(pointVec, &pV));
3432: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3433: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3434: PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3435: /* Update preallocation info */
3436: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3437: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3438: {
3439: PetscHashIJKey key;
3440: PetscBool missing;
3442: for (i = 0; i < numFIndices; ++i) {
3443: key.i = findices[i];
3444: if (key.i >= 0) {
3445: /* Get indices for coarse elements */
3446: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3447: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3448: for (c = 0; c < numCIndices; ++c) {
3449: key.j = cindices[c];
3450: if (key.j < 0) continue;
3451: PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3452: if (missing) {
3453: if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3454: else ++onz[key.i - rStart];
3455: }
3456: }
3457: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3458: }
3459: }
3460: }
3461: }
3462: PetscCall(PetscSFDestroy(&coarseCellSF));
3463: PetscCall(VecDestroy(&pointVec));
3464: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3465: }
3466: }
3467: PetscCall(PetscHSetIJDestroy(&ht));
3468: PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3469: PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3470: PetscCall(PetscFree2(dnz, onz));
3471: for (field = 0; field < Nf; ++field) {
3472: PetscObject obj;
3473: PetscClassId id;
3474: PetscTabulation T, Tfine;
3475: PetscQuadrature quad;
3476: const PetscReal *qpoints, *qweights;
3477: PetscInt Nq, Nc, i, d;
3479: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3480: PetscCall(PetscObjectGetClassId(obj, &id));
3481: if (id == PETSCFE_CLASSID) {
3482: PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3483: PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3484: PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3485: } else {
3486: PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3487: }
3488: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3489: /* For each fine grid cell */
3490: for (cell = cStart; cell < cEnd; ++cell) {
3491: Vec pointVec;
3492: PetscScalar *pV;
3493: PetscSF coarseCellSF = NULL;
3494: const PetscSFNode *coarseCells;
3495: PetscInt numCoarseCells, cpdim, q, c, j;
3496: PetscInt *findices, *cindices;
3497: PetscInt numFIndices, numCIndices;
3499: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3500: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3501: /* Get points from the quadrature */
3502: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3503: PetscCall(VecSetBlockSize(pointVec, dim));
3504: PetscCall(VecGetArray(pointVec, &pV));
3505: for (q = 0; q < Nq; ++q) {
3506: const PetscReal xi0[3] = {-1., -1., -1.};
3508: /* Transform point to real space */
3509: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3510: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3511: }
3512: PetscCall(VecRestoreArray(pointVec, &pV));
3513: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3514: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3515: /* Update matrix */
3516: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3517: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3518: PetscCall(VecGetArray(pointVec, &pV));
3519: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3520: PetscReal pVReal[3];
3521: const PetscReal xi0[3] = {-1., -1., -1.};
3523: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3524: /* Transform points from real space to coarse reference space */
3525: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3526: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3527: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3529: if (id == PETSCFE_CLASSID) {
3530: PetscFE fe = (PetscFE)obj;
3532: /* Evaluate coarse basis on contained point */
3533: PetscCall(PetscFEGetDimension(fe, &cpdim));
3534: PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3535: /* Get elemMat entries by multiplying by weight */
3536: for (i = 0; i < numFIndices; ++i) {
3537: PetscCall(PetscArrayzero(elemMat, cpdim));
3538: for (j = 0; j < cpdim; ++j) {
3539: 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;
3540: }
3541: /* Update interpolator */
3542: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3543: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3544: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3545: }
3546: } else {
3547: cpdim = 1;
3548: for (i = 0; i < numFIndices; ++i) {
3549: PetscCall(PetscArrayzero(elemMat, cpdim));
3550: for (j = 0; j < cpdim; ++j) {
3551: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3552: }
3553: /* Update interpolator */
3554: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3555: 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));
3556: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3557: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3558: }
3559: }
3560: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3561: }
3562: PetscCall(VecRestoreArray(pointVec, &pV));
3563: PetscCall(PetscSFDestroy(&coarseCellSF));
3564: PetscCall(VecDestroy(&pointVec));
3565: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3566: }
3567: if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3568: }
3569: PetscCall(PetscFree3(v0, J, invJ));
3570: PetscCall(PetscFree3(v0c, Jc, invJc));
3571: PetscCall(PetscFree(elemMat));
3572: PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3573: PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3574: PetscFunctionReturn(PETSC_SUCCESS);
3575: }
3577: /*@
3578: DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
3580: Input Parameters:
3581: + dmc - The coarse mesh
3582: . dmf - The fine mesh
3583: - ctx - The application context
3585: Output Parameter:
3586: . sc - The mapping
3588: Level: developer
3590: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3591: @*/
3592: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, PetscCtx ctx)
3593: {
3594: PetscDS prob;
3595: PetscFE *feRef;
3596: PetscFV *fvRef;
3597: Vec fv, cv;
3598: IS fis, cis;
3599: PetscSection fsection, fglobalSection, csection, cglobalSection;
3600: PetscInt *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3601: PetscInt cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3602: PetscBool *needAvg;
3604: PetscFunctionBegin;
3605: PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3606: PetscCall(DMGetDimension(dmf, &dim));
3607: PetscCall(DMGetLocalSection(dmf, &fsection));
3608: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3609: PetscCall(DMGetLocalSection(dmc, &csection));
3610: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3611: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3612: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3613: PetscCall(DMGetDS(dmc, &prob));
3614: PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3615: for (f = 0; f < Nf; ++f) {
3616: PetscObject obj;
3617: PetscClassId id;
3618: PetscInt fNb = 0, Nc = 0;
3620: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3621: PetscCall(PetscObjectGetClassId(obj, &id));
3622: if (id == PETSCFE_CLASSID) {
3623: PetscFE fe = (PetscFE)obj;
3624: PetscSpace sp;
3625: PetscInt maxDegree;
3627: PetscCall(PetscFERefine(fe, &feRef[f]));
3628: PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3629: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3630: PetscCall(PetscFEGetBasisSpace(fe, &sp));
3631: PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3632: if (!maxDegree) needAvg[f] = PETSC_TRUE;
3633: } else if (id == PETSCFV_CLASSID) {
3634: PetscFV fv = (PetscFV)obj;
3635: PetscDualSpace Q;
3637: PetscCall(PetscFVRefine(fv, &fvRef[f]));
3638: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3639: PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3640: PetscCall(PetscFVGetNumComponents(fv, &Nc));
3641: needAvg[f] = PETSC_TRUE;
3642: }
3643: fTotDim += fNb;
3644: }
3645: PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3646: PetscCall(PetscMalloc1(cTotDim, &cmap));
3647: for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3648: PetscFE feC;
3649: PetscFV fvC;
3650: PetscDualSpace QF, QC;
3651: PetscInt order = -1, NcF, NcC, fpdim, cpdim;
3653: if (feRef[field]) {
3654: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3655: PetscCall(PetscFEGetNumComponents(feC, &NcC));
3656: PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3657: PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3658: PetscCall(PetscDualSpaceGetOrder(QF, &order));
3659: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3660: PetscCall(PetscFEGetDualSpace(feC, &QC));
3661: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3662: } else {
3663: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3664: PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3665: PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3666: PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3667: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3668: PetscCall(PetscFVGetDualSpace(fvC, &QC));
3669: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3670: }
3671: 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);
3672: for (c = 0; c < cpdim; ++c) {
3673: PetscQuadrature cfunc;
3674: const PetscReal *cqpoints, *cqweights;
3675: PetscInt NqcC, NpC;
3676: PetscBool found = PETSC_FALSE;
3678: PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3679: PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3680: 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);
3681: PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3682: for (f = 0; f < fpdim; ++f) {
3683: PetscQuadrature ffunc;
3684: const PetscReal *fqpoints, *fqweights;
3685: PetscReal sum = 0.0;
3686: PetscInt NqcF, NpF;
3688: PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3689: PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3690: 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);
3691: if (NpC != NpF) continue;
3692: for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3693: if (sum > 1.0e-9) continue;
3694: for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3695: if (sum < 1.0e-9) continue;
3696: cmap[offsetC + c] = offsetF + f;
3697: found = PETSC_TRUE;
3698: break;
3699: }
3700: if (!found) {
3701: /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3702: PetscCheck(fvRef[field] || (feRef[field] && order == 0), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3703: cmap[offsetC + c] = offsetF + 0;
3704: }
3705: }
3706: offsetC += cpdim;
3707: offsetF += fpdim;
3708: }
3709: for (f = 0; f < Nf; ++f) {
3710: PetscCall(PetscFEDestroy(&feRef[f]));
3711: PetscCall(PetscFVDestroy(&fvRef[f]));
3712: }
3713: PetscCall(PetscFree3(feRef, fvRef, needAvg));
3715: PetscCall(DMGetGlobalVector(dmf, &fv));
3716: PetscCall(DMGetGlobalVector(dmc, &cv));
3717: PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3718: PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3719: PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3720: PetscCall(PetscMalloc1(m, &cindices));
3721: PetscCall(PetscMalloc1(m, &findices));
3722: for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3723: for (c = cStart; c < cEnd; ++c) {
3724: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3725: for (d = 0; d < cTotDim; ++d) {
3726: if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3727: 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]]);
3728: cindices[cellCIndices[d] - startC] = cellCIndices[d];
3729: findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3730: }
3731: }
3732: PetscCall(PetscFree(cmap));
3733: PetscCall(PetscFree2(cellCIndices, cellFIndices));
3735: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3736: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3737: PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3738: PetscCall(ISDestroy(&cis));
3739: PetscCall(ISDestroy(&fis));
3740: PetscCall(DMRestoreGlobalVector(dmf, &fv));
3741: PetscCall(DMRestoreGlobalVector(dmc, &cv));
3742: PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3743: PetscFunctionReturn(PETSC_SUCCESS);
3744: }
3746: /*@C
3747: DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3749: Input Parameters:
3750: + dm - The `DM`
3751: . cellIS - The cells to include
3752: . locX - A local vector with the solution fields
3753: . locX_t - A local vector with solution field time derivatives, or `NULL`
3754: - locA - A local vector with auxiliary fields, or `NULL`
3756: Output Parameters:
3757: + u - The field coefficients
3758: . u_t - The fields derivative coefficients
3759: - a - The auxiliary field coefficients
3761: Level: developer
3763: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3764: @*/
3765: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
3766: {
3767: DM plex, plexA = NULL;
3768: DMEnclosureType encAux;
3769: PetscSection section, sectionAux;
3770: PetscDS prob;
3771: const PetscInt *cells;
3772: PetscInt cStart, cEnd, numCells, totDim, totDimAux, c;
3774: PetscFunctionBegin;
3779: PetscAssertPointer(u, 6);
3780: PetscAssertPointer(u_t, 7);
3781: PetscAssertPointer(a, 8);
3782: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3783: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3784: PetscCall(DMGetLocalSection(dm, §ion));
3785: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3786: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3787: if (locA) {
3788: DM dmAux;
3789: PetscDS probAux;
3791: PetscCall(VecGetDM(locA, &dmAux));
3792: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3793: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3794: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3795: PetscCall(DMGetDS(dmAux, &probAux));
3796: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3797: }
3798: numCells = cEnd - cStart;
3799: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3800: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3801: else *u_t = NULL;
3802: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3803: else *a = NULL;
3804: for (c = cStart; c < cEnd; ++c) {
3805: const PetscInt cell = cells ? cells[c] : c;
3806: const PetscInt cind = c - cStart;
3807: PetscScalar *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3809: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3810: for (PetscInt i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3811: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3812: if (locX_t) {
3813: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3814: for (PetscInt i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3815: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3816: }
3817: if (locA) {
3818: PetscInt subcell;
3819: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3820: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3821: for (PetscInt i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3822: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3823: }
3824: }
3825: PetscCall(DMDestroy(&plex));
3826: if (locA) PetscCall(DMDestroy(&plexA));
3827: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3828: PetscFunctionReturn(PETSC_SUCCESS);
3829: }
3831: /*@C
3832: DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3834: Input Parameters:
3835: + dm - The `DM`
3836: . cellIS - The cells to include
3837: . locX - A local vector with the solution fields
3838: . locX_t - A local vector with solution field time derivatives, or `NULL`
3839: - locA - A local vector with auxiliary fields, or `NULL`
3841: Output Parameters:
3842: + u - The field coefficients
3843: . u_t - The fields derivative coefficients
3844: - a - The auxiliary field coefficients
3846: Level: developer
3848: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3849: @*/
3850: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
3851: {
3852: PetscFunctionBegin;
3853: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3854: if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3855: if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3856: PetscFunctionReturn(PETSC_SUCCESS);
3857: }
3859: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3860: {
3861: DM plex, plexA = NULL;
3862: DMEnclosureType encAux;
3863: PetscSection section, sectionAux;
3864: PetscDS ds, dsIn;
3865: const PetscInt *cells;
3866: PetscInt cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;
3868: PetscFunctionBegin;
3874: PetscAssertPointer(u, 6);
3875: PetscAssertPointer(u_t, 7);
3876: PetscAssertPointer(a, 8);
3877: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3878: numCells = cEnd - cStart;
3879: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3880: PetscCall(DMGetLocalSection(dm, §ion));
3881: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3882: PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3883: PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3884: if (locA) {
3885: DM dmAux;
3886: PetscDS probAux;
3888: PetscCall(VecGetDM(locA, &dmAux));
3889: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3890: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3891: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3892: PetscCall(DMGetDS(dmAux, &probAux));
3893: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3894: }
3895: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3896: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3897: else {
3898: *u_t = NULL;
3899: }
3900: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3901: else {
3902: *a = NULL;
3903: }
3904: // Loop over cohesive cells
3905: for (c = cStart; c < cEnd; ++c) {
3906: const PetscInt cell = cells ? cells[c] : c;
3907: const PetscInt cind = c - cStart;
3908: PetscScalar *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
3909: PetscScalar *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim);
3910: const PetscInt *cone, *ornt;
3911: PetscInt Nx = 0, Nxf, s;
3913: PetscCall(DMPlexGetCone(dm, cell, &cone));
3914: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3915: // Put in cohesive unknowns
3916: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3917: if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3918: for (f = 0; f < Nf; ++f) {
3919: PetscInt fdofIn, foff, foffIn;
3920: PetscBool cohesive;
3922: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3923: if (!cohesive) continue;
3924: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3925: PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3926: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3927: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3928: if (locX_t)
3929: for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3930: Nx += fdofIn;
3931: }
3932: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3933: if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3934: // Loop over sides of surface
3935: for (s = 0; s < 2; ++s) {
3936: const PetscInt *support;
3937: const PetscInt face = cone[s];
3938: PetscDS dsC;
3939: PetscInt ssize, ncell, Nxc;
3941: // I don't think I need the face to have 0 orientation in the hybrid cell
3942: //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]);
3943: PetscCall(DMPlexGetSupport(dm, face, &support));
3944: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3945: if (support[0] == cell) ncell = support[1];
3946: else if (support[1] == cell) ncell = support[0];
3947: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3948: // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3949: PetscCall(DMGetCellDS(dm, ncell, &dsC, NULL));
3950: PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3951: if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3952: for (f = 0; f < Nf; ++f) {
3953: PetscInt fdofIn, foffIn, foff;
3954: PetscBool cohesive;
3956: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3957: if (cohesive) continue;
3958: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3959: PetscCall(PetscDSGetFieldOffset(dsC, f, &foff));
3960: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3961: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foff + i];
3962: if (locX_t)
3963: for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foff + i];
3964: Nx += fdofIn;
3965: }
3966: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3967: if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3968: }
3969: 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);
3971: if (locA) {
3972: PetscScalar *al = &(*a)[cind * totDimAux];
3973: PetscInt subcell;
3975: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3976: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3977: 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);
3978: for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3979: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3980: }
3981: }
3982: PetscCall(DMDestroy(&plex));
3983: PetscCall(DMDestroy(&plexA));
3984: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3985: PetscFunctionReturn(PETSC_SUCCESS);
3986: }
3988: /*
3989: DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface
3991: Input Parameters:
3992: + dm - The full domain DM
3993: . dmX - An array of DM for the field, say an auxiliary DM, indexed by s
3994: . dsX - An array of PetscDS for the field, indexed by s
3995: . cellIS - The interface cells for which we want values
3996: . locX - An array of local vectors with the field values, indexed by s
3997: - useCell - Flag to have values come from neighboring cell rather than endcap face
3999: Output Parameter:
4000: . x - An array of field values, indexed by s
4002: Note:
4003: The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`.
4005: Level: advanced
4007: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
4008: */
4009: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
4010: {
4011: DM plexX[2];
4012: DMEnclosureType encX[2];
4013: PetscSection sectionX[2];
4014: const PetscInt *cells;
4015: PetscInt cStart, cEnd, numCells, c, s, totDimX[2];
4017: PetscFunctionBegin;
4018: PetscAssertPointer(locX, 5);
4019: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
4020: PetscAssertPointer(dmX, 2);
4021: PetscAssertPointer(dsX, 3);
4023: PetscAssertPointer(x, 7);
4024: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4025: numCells = cEnd - cStart;
4026: for (s = 0; s < 2; ++s) {
4030: PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
4031: PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
4032: PetscCall(DMGetLocalSection(dmX[s], §ionX[s]));
4033: PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
4034: PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
4035: }
4036: for (c = cStart; c < cEnd; ++c) {
4037: const PetscInt cell = cells ? cells[c] : c;
4038: const PetscInt cind = c - cStart;
4039: const PetscInt *cone, *ornt;
4041: PetscCall(DMPlexGetCone(dm, cell, &cone));
4042: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
4043: //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]);
4044: for (s = 0; s < 2; ++s) {
4045: const PetscInt tdX = totDimX[s];
4046: PetscScalar *closure = NULL, *xl = &x[s][cind * tdX];
4047: PetscInt face = cone[s], point = face, subpoint, Nx, i;
4049: if (useCell) {
4050: const PetscInt *support;
4051: PetscInt ssize;
4053: PetscCall(DMPlexGetSupport(dm, face, &support));
4054: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
4055: 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);
4056: if (support[0] == cell) point = support[1];
4057: else if (support[1] == cell) point = support[0];
4058: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
4059: }
4060: PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
4061: PetscCall(DMPlexVecGetOrientedClosure(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
4062: 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);
4063: for (i = 0; i < Nx; ++i) xl[i] = closure[i];
4064: PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
4065: }
4066: }
4067: for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
4068: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4069: PetscFunctionReturn(PETSC_SUCCESS);
4070: }
4072: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
4073: {
4074: PetscFunctionBegin;
4075: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
4076: PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
4077: PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
4078: PetscFunctionReturn(PETSC_SUCCESS);
4079: }
4081: /*@C
4082: DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
4084: Input Parameters:
4085: + dm - The `DM`
4086: . fStart - The first face to include
4087: . fEnd - The first face to exclude
4088: . locX - A local vector with the solution fields
4089: . locX_t - A local vector with solution field time derivatives, or `NULL`
4090: . faceGeometry - A local vector with face geometry
4091: . cellGeometry - A local vector with cell geometry
4092: - locGrad - A local vector with field gradients, or `NULL`
4094: Output Parameters:
4095: + Nface - The number of faces with field values
4096: . uL - The field values at the left side of the face
4097: - uR - The field values at the right side of the face
4099: Level: developer
4101: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4102: @*/
4103: 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[])
4104: {
4105: DM dmFace, dmCell, dmGrad = NULL;
4106: PetscSection section;
4107: PetscDS prob;
4108: DMLabel ghostLabel;
4109: const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
4110: PetscBool *isFE;
4111: PetscInt dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
4113: PetscFunctionBegin;
4120: PetscAssertPointer(uL, 10);
4121: PetscAssertPointer(uR, 11);
4122: PetscCall(DMGetDimension(dm, &dim));
4123: PetscCall(DMGetDS(dm, &prob));
4124: PetscCall(DMGetLocalSection(dm, §ion));
4125: PetscCall(PetscDSGetNumFields(prob, &Nf));
4126: PetscCall(PetscDSGetTotalComponents(prob, &Nc));
4127: PetscCall(PetscMalloc1(Nf, &isFE));
4128: for (f = 0; f < Nf; ++f) {
4129: PetscObject obj;
4130: PetscClassId id;
4132: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4133: PetscCall(PetscObjectGetClassId(obj, &id));
4134: if (id == PETSCFE_CLASSID) {
4135: isFE[f] = PETSC_TRUE;
4136: } else if (id == PETSCFV_CLASSID) {
4137: isFE[f] = PETSC_FALSE;
4138: } else {
4139: isFE[f] = PETSC_FALSE;
4140: }
4141: }
4142: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4143: PetscCall(VecGetArrayRead(locX, &x));
4144: PetscCall(VecGetDM(faceGeometry, &dmFace));
4145: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4146: PetscCall(VecGetDM(cellGeometry, &dmCell));
4147: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4148: if (locGrad) {
4149: PetscCall(VecGetDM(locGrad, &dmGrad));
4150: PetscCall(VecGetArrayRead(locGrad, &lgrad));
4151: }
4152: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
4153: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
4154: /* Right now just eat the extra work for FE (could make a cell loop) */
4155: for (face = fStart, iface = 0; face < fEnd; ++face) {
4156: const PetscInt *cells;
4157: PetscFVFaceGeom *fg;
4158: PetscFVCellGeom *cgL, *cgR;
4159: PetscScalar *xL, *xR, *gL, *gR;
4160: PetscScalar *uLl = *uL, *uRl = *uR;
4161: PetscInt ghost, nsupp, nchild;
4163: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4164: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4165: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4166: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4167: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4168: PetscCall(DMPlexGetSupport(dm, face, &cells));
4169: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4170: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4171: for (f = 0; f < Nf; ++f) {
4172: PetscInt off;
4174: PetscCall(PetscDSGetComponentOffset(prob, f, &off));
4175: if (isFE[f]) {
4176: const PetscInt *cone;
4177: PetscInt comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
4179: xL = xR = NULL;
4180: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4181: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, &xL));
4182: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, &xR));
4183: PetscCall(DMPlexGetCone(dm, cells[0], &cone));
4184: PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
4185: for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
4186: if (cone[faceLocL] == face) break;
4187: PetscCall(DMPlexGetCone(dm, cells[1], &cone));
4188: PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
4189: for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
4190: if (cone[faceLocR] == face) break;
4191: 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]);
4192: /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
4193: /* TODO: this is a hack that might not be right for nonconforming */
4194: if (faceLocL < coneSizeL) {
4195: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
4196: if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4197: else {
4198: for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
4199: }
4200: } else {
4201: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4202: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4203: for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
4204: }
4205: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, &xL));
4206: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, &xR));
4207: } else {
4208: PetscFV fv;
4209: PetscInt numComp;
4211: PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
4212: PetscCall(PetscFVGetNumComponents(fv, &numComp));
4213: PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
4214: PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
4215: if (dmGrad) {
4216: PetscReal dxL[3], dxR[3];
4218: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
4219: PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
4220: DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
4221: DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
4222: for (PetscInt c = 0; c < numComp; ++c) {
4223: uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
4224: uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
4225: }
4226: } else {
4227: for (PetscInt c = 0; c < numComp; ++c) {
4228: uLl[iface * Nc + off + c] = xL[c];
4229: uRl[iface * Nc + off + c] = xR[c];
4230: }
4231: }
4232: }
4233: }
4234: ++iface;
4235: }
4236: *Nface = iface;
4237: PetscCall(VecRestoreArrayRead(locX, &x));
4238: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4239: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4240: if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
4241: PetscCall(PetscFree(isFE));
4242: PetscFunctionReturn(PETSC_SUCCESS);
4243: }
4245: /*@C
4246: DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
4248: Input Parameters:
4249: + dm - The `DM`
4250: . fStart - The first face to include
4251: . fEnd - The first face to exclude
4252: . locX - A local vector with the solution fields
4253: . locX_t - A local vector with solution field time derivatives, or `NULL`
4254: . faceGeometry - A local vector with face geometry
4255: . cellGeometry - A local vector with cell geometry
4256: - locGrad - A local vector with field gradients, or `NULL`
4258: Output Parameters:
4259: + Nface - The number of faces with field values
4260: . uL - The field values at the left side of the face
4261: - uR - The field values at the right side of the face
4263: Level: developer
4265: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4266: @*/
4267: 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[])
4268: {
4269: PetscFunctionBegin;
4270: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
4271: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
4272: PetscFunctionReturn(PETSC_SUCCESS);
4273: }
4275: /*@C
4276: DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
4278: Input Parameters:
4279: + dm - The `DM`
4280: . fStart - The first face to include
4281: . fEnd - The first face to exclude
4282: . faceGeometry - A local vector with face geometry
4283: - cellGeometry - A local vector with cell geometry
4285: Output Parameters:
4286: + Nface - The number of faces with field values
4287: . fgeom - The face centroid and normals
4288: - vol - The cell volumes
4290: Level: developer
4292: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4293: @*/
4294: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
4295: {
4296: DM dmFace, dmCell;
4297: DMLabel ghostLabel;
4298: const PetscScalar *facegeom, *cellgeom;
4299: PetscInt dim, numFaces = fEnd - fStart, iface, face;
4301: PetscFunctionBegin;
4305: PetscAssertPointer(fgeom, 7);
4306: PetscAssertPointer(vol, 8);
4307: PetscCall(DMGetDimension(dm, &dim));
4308: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4309: PetscCall(VecGetDM(faceGeometry, &dmFace));
4310: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4311: PetscCall(VecGetDM(cellGeometry, &dmCell));
4312: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4313: PetscCall(PetscMalloc1(numFaces, fgeom));
4314: PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
4315: for (face = fStart, iface = 0; face < fEnd; ++face) {
4316: const PetscInt *cells;
4317: PetscFVFaceGeom *fg;
4318: PetscFVCellGeom *cgL, *cgR;
4319: PetscFVFaceGeom *fgeoml = *fgeom;
4320: PetscReal *voll = *vol;
4321: PetscInt ghost, d, nchild, nsupp;
4323: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4324: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4325: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4326: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4327: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4328: PetscCall(DMPlexGetSupport(dm, face, &cells));
4329: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4330: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4331: for (d = 0; d < dim; ++d) {
4332: fgeoml[iface].centroid[d] = fg->centroid[d];
4333: fgeoml[iface].normal[d] = fg->normal[d];
4334: }
4335: voll[iface * 2 + 0] = cgL->volume;
4336: voll[iface * 2 + 1] = cgR->volume;
4337: ++iface;
4338: }
4339: *Nface = iface;
4340: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4341: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4342: PetscFunctionReturn(PETSC_SUCCESS);
4343: }
4345: /*@C
4346: DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
4348: Input Parameters:
4349: + dm - The `DM`
4350: . fStart - The first face to include
4351: . fEnd - The first face to exclude
4352: . faceGeometry - A local vector with face geometry
4353: - cellGeometry - A local vector with cell geometry
4355: Output Parameters:
4356: + Nface - The number of faces with field values
4357: . fgeom - The face centroid and normals
4358: - vol - The cell volumes
4360: Level: developer
4362: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4363: @*/
4364: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
4365: {
4366: PetscFunctionBegin;
4367: PetscCall(PetscFree(*fgeom));
4368: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
4369: PetscFunctionReturn(PETSC_SUCCESS);
4370: }
4372: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
4373: {
4374: char composeStr[33] = {0};
4375: PetscObjectId id;
4376: PetscContainer container;
4378: PetscFunctionBegin;
4379: PetscCall(PetscObjectGetId((PetscObject)quad, &id));
4380: PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
4381: PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
4382: if (container) {
4383: PetscCall(PetscContainerGetPointer(container, geom));
4384: } else {
4385: PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, mode, geom));
4386: PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
4387: PetscCall(PetscContainerSetPointer(container, (void *)*geom));
4388: PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
4389: PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
4390: PetscCall(PetscContainerDestroy(&container));
4391: }
4392: PetscFunctionReturn(PETSC_SUCCESS);
4393: }
4395: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4396: {
4397: PetscFunctionBegin;
4398: *geom = NULL;
4399: PetscFunctionReturn(PETSC_SUCCESS);
4400: }
4402: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, PetscCtx ctx)
4403: {
4404: DM_Plex *mesh = (DM_Plex *)dm->data;
4405: const char *name = "Residual";
4406: DM dmAux = NULL;
4407: DMLabel ghostLabel = NULL;
4408: PetscDS prob = NULL;
4409: PetscDS probAux = NULL;
4410: PetscBool useFEM = PETSC_FALSE;
4411: PetscBool isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4412: DMField coordField = NULL;
4413: Vec locA;
4414: PetscScalar *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
4415: IS chunkIS;
4416: const PetscInt *cells;
4417: PetscInt cStart, cEnd, numCells;
4418: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
4419: PetscInt maxDegree = PETSC_INT_MAX;
4420: PetscFormKey key;
4421: PetscQuadrature affineQuad = NULL, *quads = NULL;
4422: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
4424: PetscFunctionBegin;
4425: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4426: /* FEM+FVM */
4427: /* 1: Get sizes from dm and dmAux */
4428: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4429: PetscCall(DMGetDS(dm, &prob));
4430: PetscCall(PetscDSGetNumFields(prob, &Nf));
4431: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4432: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4433: if (locA) {
4434: PetscCall(VecGetDM(locA, &dmAux));
4435: PetscCall(DMGetDS(dmAux, &probAux));
4436: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4437: }
4438: /* 2: Get geometric data */
4439: for (f = 0; f < Nf; ++f) {
4440: PetscObject obj;
4441: PetscClassId id;
4442: PetscBool fimp;
4444: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4445: if (isImplicit != fimp) continue;
4446: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4447: PetscCall(PetscObjectGetClassId(obj, &id));
4448: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4449: PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4450: }
4451: if (useFEM) {
4452: PetscCall(DMGetCoordinateField(dm, &coordField));
4453: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4454: if (maxDegree <= 1) {
4455: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4456: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
4457: } else {
4458: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4459: for (f = 0; f < Nf; ++f) {
4460: PetscObject obj;
4461: PetscClassId id;
4462: PetscBool fimp;
4464: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4465: if (isImplicit != fimp) continue;
4466: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4467: PetscCall(PetscObjectGetClassId(obj, &id));
4468: if (id == PETSCFE_CLASSID) {
4469: PetscFE fe = (PetscFE)obj;
4471: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4472: PetscCall(PetscObjectReference((PetscObject)quads[f]));
4473: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
4474: }
4475: }
4476: }
4477: }
4478: /* Loop over chunks */
4479: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4480: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4481: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4482: numCells = cEnd - cStart;
4483: numChunks = 1;
4484: cellChunkSize = numCells / numChunks;
4485: numChunks = PetscMin(1, numCells);
4486: key.label = NULL;
4487: key.value = 0;
4488: key.part = 0;
4489: for (chunk = 0; chunk < numChunks; ++chunk) {
4490: PetscScalar *elemVec, *fluxL = NULL, *fluxR = NULL;
4491: PetscReal *vol = NULL;
4492: PetscFVFaceGeom *fgeom = NULL;
4493: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4494: PetscInt numFaces = 0;
4496: /* Extract field coefficients */
4497: if (useFEM) {
4498: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4499: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4500: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4501: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4502: }
4503: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4504: /* Loop over fields */
4505: for (f = 0; f < Nf; ++f) {
4506: PetscObject obj;
4507: PetscClassId id;
4508: PetscBool fimp;
4509: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4511: key.field = f;
4512: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4513: if (isImplicit != fimp) continue;
4514: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4515: PetscCall(PetscObjectGetClassId(obj, &id));
4516: if (id == PETSCFE_CLASSID) {
4517: PetscFE fe = (PetscFE)obj;
4518: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
4519: PetscFEGeom *chunkGeom = NULL;
4520: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4521: PetscInt Nq, Nb;
4523: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4524: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4525: PetscCall(PetscFEGetDimension(fe, &Nb));
4526: blockSize = Nb;
4527: batchSize = numBlocks * blockSize;
4528: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4529: numChunks = numCells / (numBatches * batchSize);
4530: Ne = numChunks * numBatches * batchSize;
4531: Nr = numCells % (numBatches * batchSize);
4532: offset = numCells - Nr;
4533: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4534: /* 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) */
4535: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4536: PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4537: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4538: PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4539: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4540: } else if (id == PETSCFV_CLASSID) {
4541: PetscFV fv = (PetscFV)obj;
4543: Ne = numFaces;
4544: /* Riemann solve over faces (need fields at face centroids) */
4545: /* We need to evaluate FE fields at those coordinates */
4546: PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4547: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4548: }
4549: /* Loop over domain */
4550: if (useFEM) {
4551: /* Add elemVec to locX */
4552: for (c = cS; c < cE; ++c) {
4553: const PetscInt cell = cells ? cells[c] : c;
4554: const PetscInt cind = c - cStart;
4556: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4557: if (ghostLabel) {
4558: PetscInt ghostVal;
4560: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4561: if (ghostVal > 0) continue;
4562: }
4563: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4564: }
4565: }
4566: /* Handle time derivative */
4567: if (locX_t) {
4568: PetscScalar *x_t, *fa;
4570: PetscCall(VecGetArray(locF, &fa));
4571: PetscCall(VecGetArray(locX_t, &x_t));
4572: for (f = 0; f < Nf; ++f) {
4573: PetscFV fv;
4574: PetscObject obj;
4575: PetscClassId id;
4576: PetscInt pdim;
4578: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4579: PetscCall(PetscObjectGetClassId(obj, &id));
4580: if (id != PETSCFV_CLASSID) continue;
4581: fv = (PetscFV)obj;
4582: PetscCall(PetscFVGetNumComponents(fv, &pdim));
4583: for (c = cS; c < cE; ++c) {
4584: const PetscInt cell = cells ? cells[c] : c;
4585: PetscScalar *u_t, *r;
4587: if (ghostLabel) {
4588: PetscInt ghostVal;
4590: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4591: if (ghostVal > 0) continue;
4592: }
4593: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4594: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4595: for (PetscInt d = 0; d < pdim; ++d) r[d] += u_t[d];
4596: }
4597: }
4598: PetscCall(VecRestoreArray(locX_t, &x_t));
4599: PetscCall(VecRestoreArray(locF, &fa));
4600: }
4601: if (useFEM) {
4602: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4603: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4604: }
4605: }
4606: if (useFEM) PetscCall(ISDestroy(&chunkIS));
4607: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4608: /* TODO Could include boundary residual here (see DMPlexComputeResidualByKey) */
4609: if (useFEM) {
4610: if (maxDegree <= 1) {
4611: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4612: PetscCall(PetscQuadratureDestroy(&affineQuad));
4613: } else {
4614: for (f = 0; f < Nf; ++f) {
4615: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4616: PetscCall(PetscQuadratureDestroy(&quads[f]));
4617: }
4618: PetscCall(PetscFree2(quads, geoms));
4619: }
4620: }
4621: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4622: PetscFunctionReturn(PETSC_SUCCESS);
4623: }
4625: /*
4626: 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
4628: X - The local solution vector
4629: X_t - The local solution time derivative vector, or NULL
4630: */
4631: 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)
4632: {
4633: DM_Plex *mesh = (DM_Plex *)dm->data;
4634: const char *name = "Jacobian", *nameP = "JacobianPre";
4635: DM dmAux = NULL;
4636: PetscDS prob, probAux = NULL;
4637: PetscSection sectionAux = NULL;
4638: Vec A;
4639: DMField coordField;
4640: PetscFEGeom *cgeomFEM;
4641: PetscQuadrature qGeom = NULL;
4642: Mat J = Jac, JP = JacP;
4643: PetscScalar *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4644: PetscBool hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4645: const PetscInt *cells;
4646: PetscFormKey key;
4647: PetscInt Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
4649: PetscFunctionBegin;
4650: PetscCall(ISGetLocalSize(cellIS, &numCells));
4651: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4652: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4653: PetscCall(DMGetDS(dm, &prob));
4654: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4655: if (A) {
4656: PetscCall(VecGetDM(A, &dmAux));
4657: PetscCall(DMGetLocalSection(dmAux, §ionAux));
4658: PetscCall(DMGetDS(dmAux, &probAux));
4659: }
4660: /* Get flags */
4661: PetscCall(PetscDSGetNumFields(prob, &Nf));
4662: PetscCall(DMGetWorkArray(dm, Nf, MPI_C_BOOL, &isFE));
4663: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4664: PetscObject disc;
4665: PetscClassId id;
4666: PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4667: PetscCall(PetscObjectGetClassId(disc, &id));
4668: if (id == PETSCFE_CLASSID) {
4669: isFE[fieldI] = PETSC_TRUE;
4670: } else if (id == PETSCFV_CLASSID) {
4671: hasFV = PETSC_TRUE;
4672: isFE[fieldI] = PETSC_FALSE;
4673: }
4674: }
4675: PetscCall(PetscDSHasJacobian(prob, &hasJac));
4676: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4677: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4678: assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4679: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4680: if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4681: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4682: if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4683: /* Compute batch sizes */
4684: if (isFE[0]) {
4685: PetscFE fe;
4686: PetscQuadrature q;
4687: PetscInt numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
4689: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4690: PetscCall(PetscFEGetQuadrature(fe, &q));
4691: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4692: PetscCall(PetscFEGetDimension(fe, &Nb));
4693: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4694: blockSize = Nb * numQuadPoints;
4695: batchSize = numBlocks * blockSize;
4696: chunkSize = numBatches * batchSize;
4697: numChunks = numCells / chunkSize + numCells % chunkSize;
4698: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4699: } else {
4700: chunkSize = numCells;
4701: numChunks = 1;
4702: }
4703: /* Get work space */
4704: 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;
4705: PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4706: PetscCall(PetscArrayzero(work, wsz));
4707: off = 0;
4708: u = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4709: u_t = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4710: a = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4711: elemMat = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4712: elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4713: elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4714: PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4715: /* Setup geometry */
4716: PetscCall(DMGetCoordinateField(dm, &coordField));
4717: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4718: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4719: if (!qGeom) {
4720: PetscFE fe;
4722: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4723: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4724: PetscCall(PetscObjectReference((PetscObject)qGeom));
4725: }
4726: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
4727: /* Compute volume integrals */
4728: if (assembleJac) PetscCall(MatZeroEntries(J));
4729: PetscCall(MatZeroEntries(JP));
4730: key.label = NULL;
4731: key.value = 0;
4732: key.part = 0;
4733: for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4734: const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4736: /* Extract values */
4737: for (PetscInt c = 0; c < Ncell; ++c) {
4738: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4739: PetscScalar *x = NULL, *x_t = NULL;
4741: if (X) {
4742: PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4743: for (PetscInt i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4744: PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4745: }
4746: if (X_t) {
4747: PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4748: for (PetscInt i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4749: PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4750: }
4751: if (dmAux) {
4752: PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4753: for (PetscInt i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4754: PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4755: }
4756: }
4757: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4758: PetscFE fe;
4759: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4760: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4761: key.field = fieldI * Nf + fieldJ;
4762: if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4763: if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4764: if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4765: }
4766: /* For finite volume, add the identity */
4767: if (!isFE[fieldI]) {
4768: PetscFV fv;
4769: PetscInt eOffset = 0, Nc, fc, foff;
4771: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4772: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4773: PetscCall(PetscFVGetNumComponents(fv, &Nc));
4774: for (PetscInt c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4775: for (fc = 0; fc < Nc; ++fc) {
4776: const PetscInt i = foff + fc;
4777: if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4778: if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4779: }
4780: }
4781: }
4782: }
4783: /* Add contribution from X_t */
4784: if (hasDyn) {
4785: for (PetscInt c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4786: }
4787: /* Insert values into matrix */
4788: for (PetscInt c = 0; c < Ncell; ++c) {
4789: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4790: if (mesh->printFEM > 1) {
4791: if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4792: if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4793: }
4794: if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4795: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4796: }
4797: }
4798: /* Cleanup */
4799: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4800: PetscCall(PetscQuadratureDestroy(&qGeom));
4801: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4802: PetscCall(DMRestoreWorkArray(dm, Nf, MPI_C_BOOL, &isFE));
4803: 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));
4804: /* Compute boundary integrals */
4805: /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4806: /* Assemble matrix */
4807: if (assembleJac) {
4808: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4809: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4810: }
4811: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4812: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4813: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4814: PetscFunctionReturn(PETSC_SUCCESS);
4815: }
4817: /* FEM Assembly Function */
4819: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4820: {
4821: PetscBool isPlex;
4823: PetscFunctionBegin;
4824: PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4825: if (isPlex) {
4826: *plex = dm;
4827: PetscCall(PetscObjectReference((PetscObject)dm));
4828: } else {
4829: PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4830: if (!*plex) {
4831: PetscCall(DMConvert(dm, DMPLEX, plex));
4832: PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4833: } else {
4834: PetscCall(PetscObjectReference((PetscObject)*plex));
4835: }
4836: if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4837: }
4838: PetscFunctionReturn(PETSC_SUCCESS);
4839: }
4841: /*@
4842: DMPlexGetGeometryFVM - Return precomputed geometric data
4844: Collective
4846: Input Parameter:
4847: . dm - The `DM`
4849: Output Parameters:
4850: + facegeom - The values precomputed from face geometry
4851: . cellgeom - The values precomputed from cell geometry
4852: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell, or `NULL` if not needed
4854: Level: developer
4856: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4857: @*/
4858: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PeOp PetscReal *minRadius)
4859: {
4860: DM plex;
4862: PetscFunctionBegin;
4864: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4865: PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4866: if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4867: PetscCall(DMDestroy(&plex));
4868: PetscFunctionReturn(PETSC_SUCCESS);
4869: }
4871: /*@
4872: DMPlexGetGradientDM - Return gradient data layout
4874: Collective
4876: Input Parameters:
4877: + dm - The `DM`
4878: - fv - The `PetscFV`
4880: Output Parameter:
4881: . dmGrad - The layout for gradient values
4883: Level: developer
4885: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4886: @*/
4887: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4888: {
4889: DM plex;
4890: PetscBool computeGradients;
4892: PetscFunctionBegin;
4895: PetscAssertPointer(dmGrad, 3);
4896: PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4897: if (!computeGradients) {
4898: *dmGrad = NULL;
4899: PetscFunctionReturn(PETSC_SUCCESS);
4900: }
4901: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4902: PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4903: PetscCall(DMDestroy(&plex));
4904: PetscFunctionReturn(PETSC_SUCCESS);
4905: }
4907: /*@
4908: DMPlexComputeBdResidualSingleByKey - Compute the local boundary residual for terms matching the input key
4910: Not collective
4912: Input Parameters:
4913: + dm - The output `DM`
4914: . wf - The `PetscWeakForm` holding forms on this boundary
4915: . key - The `PetscFormKey` indicating what should be integrated
4916: . facetIS - The `IS` giving a set of faces to integrate over
4917: . locX - The local solution
4918: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
4919: . t - The time
4920: - coordField - The `DMField` object with coordinates for these faces
4922: Output Parameter:
4923: . locF - The local residual
4925: Level: developer
4927: .seealso: `DMPlexComputeBdResidualSingle()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
4928: @*/
4929: PetscErrorCode DMPlexComputeBdResidualSingleByKey(DM dm, PetscWeakForm wf, PetscFormKey key, IS facetIS, Vec locX, Vec locX_t, PetscReal t, DMField coordField, Vec locF)
4930: {
4931: DM_Plex *mesh = (DM_Plex *)dm->data;
4932: DM plex = NULL, plexA = NULL;
4933: const char *name = "BdResidual";
4934: DMEnclosureType encAux;
4935: PetscDS prob, probAux = NULL;
4936: PetscSection section, sectionAux = NULL;
4937: Vec locA = NULL;
4938: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4939: PetscInt totDim, totDimAux = 0;
4941: PetscFunctionBegin;
4942: PetscCall(DMConvert(dm, DMPLEX, &plex));
4943: PetscCall(DMGetLocalSection(dm, §ion));
4944: PetscCall(DMGetDS(dm, &prob));
4945: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4946: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4947: if (locA) {
4948: DM dmAux;
4950: PetscCall(VecGetDM(locA, &dmAux));
4951: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4952: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4953: PetscCall(DMGetDS(plexA, &probAux));
4954: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4955: PetscCall(DMGetLocalSection(plexA, §ionAux));
4956: }
4957: {
4958: PetscFEGeom *fgeom;
4959: PetscInt maxDegree;
4960: PetscQuadrature qGeom = NULL;
4961: IS pointIS;
4962: const PetscInt *points;
4963: PetscInt numFaces, face, Nq;
4965: PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4966: if (!pointIS) goto end; /* No points with that id on this process */
4967: {
4968: IS isectIS;
4970: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4971: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4972: PetscCall(ISDestroy(&pointIS));
4973: pointIS = isectIS;
4974: }
4975: PetscCall(ISGetLocalSize(pointIS, &numFaces));
4976: PetscCall(ISGetIndices(pointIS, &points));
4977: PetscCall(PetscMalloc4(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, numFaces * totDim, &elemVec, (locA ? (size_t)numFaces * totDimAux : 0), &a));
4978: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4979: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4980: if (!qGeom) {
4981: PetscFE fe;
4983: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4984: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4985: PetscCall(PetscObjectReference((PetscObject)qGeom));
4986: }
4987: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4988: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
4989: for (face = 0; face < numFaces; ++face) {
4990: const PetscInt point = points[face], *support;
4991: PetscScalar *x = NULL;
4993: PetscCall(DMPlexGetSupport(dm, point, &support));
4994: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4995: for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4996: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4997: if (locX_t) {
4998: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4999: for (PetscInt i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5000: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5001: }
5002: if (locA) {
5003: PetscInt subp;
5005: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5006: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5007: for (PetscInt i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5008: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5009: }
5010: }
5011: PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
5012: {
5013: PetscFE fe;
5014: PetscInt Nb;
5015: PetscFEGeom *chunkGeom = NULL;
5016: /* Conforming batches */
5017: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5018: /* Remainder */
5019: PetscInt Nr, offset;
5021: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
5022: PetscCall(PetscFEGetDimension(fe, &Nb));
5023: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5024: /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
5025: blockSize = Nb;
5026: batchSize = numBlocks * blockSize;
5027: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5028: numChunks = numFaces / (numBatches * batchSize);
5029: Ne = numChunks * numBatches * batchSize;
5030: Nr = numFaces % (numBatches * batchSize);
5031: offset = numFaces - Nr;
5032: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5033: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
5034: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
5035: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5036: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5037: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5038: }
5039: for (face = 0; face < numFaces; ++face) {
5040: const PetscInt point = points[face], *support;
5042: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, name, totDim, &elemVec[face * totDim]));
5043: PetscCall(DMPlexGetSupport(plex, point, &support));
5044: PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
5045: }
5046: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5047: PetscCall(PetscQuadratureDestroy(&qGeom));
5048: PetscCall(ISRestoreIndices(pointIS, &points));
5049: PetscCall(ISDestroy(&pointIS));
5050: PetscCall(PetscFree4(u, u_t, elemVec, a));
5051: }
5052: end:
5053: if (mesh->printFEM) {
5054: PetscSection s;
5055: Vec locFbc;
5056: PetscInt pStart, pEnd, maxDof;
5057: PetscScalar *zeroes;
5059: PetscCall(DMGetLocalSection(dm, &s));
5060: PetscCall(VecDuplicate(locF, &locFbc));
5061: PetscCall(VecCopy(locF, locFbc));
5062: PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
5063: PetscCall(PetscSectionGetMaxDof(s, &maxDof));
5064: PetscCall(PetscCalloc1(maxDof, &zeroes));
5065: for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES));
5066: PetscCall(PetscFree(zeroes));
5067: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5068: PetscCall(VecDestroy(&locFbc));
5069: }
5070: PetscCall(DMDestroy(&plex));
5071: PetscCall(DMDestroy(&plexA));
5072: PetscFunctionReturn(PETSC_SUCCESS);
5073: }
5075: /*@
5076: DMPlexComputeBdResidualSingle - Compute the local boundary residual
5078: Not collective
5080: Input Parameters:
5081: + dm - The output `DM`
5082: . wf - The `PetscWeakForm` holding forms on this boundary
5083: . key - The `PetscFormKey` indicating what should be integrated
5084: . locX - The local solution
5085: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5086: - t - The time
5088: Output Parameter:
5089: . locF - The local residual
5091: Level: developer
5093: .seealso: `DMPlexComputeBdResidualSingleByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5094: @*/
5095: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, PetscReal t, Vec locF)
5096: {
5097: DMField coordField;
5098: DMLabel depthLabel;
5099: IS facetIS;
5100: PetscInt dim;
5102: PetscFunctionBegin;
5103: PetscCall(DMGetDimension(dm, &dim));
5104: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5105: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5106: PetscCall(DMGetCoordinateField(dm, &coordField));
5107: PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
5108: PetscCall(ISDestroy(&facetIS));
5109: PetscFunctionReturn(PETSC_SUCCESS);
5110: }
5112: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
5113: {
5114: PetscDS prob;
5115: PetscInt numBd;
5116: DMField coordField = NULL;
5117: IS facetIS = NULL;
5118: DMLabel depthLabel;
5119: PetscInt dim;
5121: PetscFunctionBegin;
5122: PetscCall(DMGetDS(dm, &prob));
5123: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5124: PetscCall(DMGetDimension(dm, &dim));
5125: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5126: /* Filter out ghost facets (SF leaves) so that boundary residual contributions
5127: from shared facets are only assembled on the owning rank. Without this,
5128: internal boundary natural BCs at partition junctions get double-counted
5129: because LocalToGlobal with ADD_VALUES sums contributions from all ranks. */
5130: if (facetIS) {
5131: PetscSF sf;
5132: PetscInt nleaves;
5133: const PetscInt *leaves;
5135: PetscCall(DMGetPointSF(dm, &sf));
5136: PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
5137: if (nleaves > 0 && leaves) {
5138: IS leafIS, ownedFacetIS;
5140: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nleaves, leaves, PETSC_USE_POINTER, &leafIS));
5141: PetscCall(ISDifference(facetIS, leafIS, &ownedFacetIS));
5142: PetscCall(ISDestroy(&leafIS));
5143: PetscCall(ISDestroy(&facetIS));
5144: facetIS = ownedFacetIS;
5145: }
5146: }
5147: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5148: for (PetscInt bd = 0; bd < numBd; ++bd) {
5149: PetscWeakForm wf;
5150: DMBoundaryConditionType type;
5151: DMLabel label;
5152: const PetscInt *values;
5153: PetscInt field, numValues, v;
5154: PetscObject obj;
5155: PetscClassId id;
5156: PetscFormKey key;
5158: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
5159: if (type & DM_BC_ESSENTIAL) continue;
5160: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
5161: PetscCall(PetscObjectGetClassId(obj, &id));
5162: if (id != PETSCFE_CLASSID) continue;
5163: if (!facetIS) {
5164: DMLabel depthLabel;
5165: PetscInt dim;
5167: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5168: PetscCall(DMGetDimension(dm, &dim));
5169: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5170: }
5171: PetscCall(DMGetCoordinateField(dm, &coordField));
5172: for (v = 0; v < numValues; ++v) {
5173: key.label = label;
5174: key.value = values[v];
5175: key.field = field;
5176: key.part = 0;
5177: PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
5178: }
5179: }
5180: PetscCall(ISDestroy(&facetIS));
5181: PetscFunctionReturn(PETSC_SUCCESS);
5182: }
5184: /*@
5185: DMPlexComputeResidualByKey - Compute the local residual for terms matching the input key
5187: Collective
5189: Input Parameters:
5190: + dm - The output `DM`
5191: . key - The `PetscFormKey` indicating what should be integrated
5192: . cellIS - The `IS` giving a set of cells to integrate over
5193: . time - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5194: . locX - The local solution
5195: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5196: . t - The time
5197: - ctx - An optional application context, passed to the pointwise functions
5199: Output Parameter:
5200: . locF - The local residual
5202: Level: developer
5204: .seealso: `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5205: @*/
5206: PetscErrorCode DMPlexComputeResidualByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
5207: {
5208: DM_Plex *mesh = (DM_Plex *)dm->data;
5209: const char *name = "Residual";
5210: DM dmAux = NULL;
5211: DM dmGrad = NULL;
5212: DMLabel ghostLabel = NULL;
5213: PetscDS ds = NULL;
5214: PetscDS dsAux = NULL;
5215: PetscSection section = NULL;
5216: PetscBool useFEM = PETSC_FALSE;
5217: PetscBool useFVM = PETSC_FALSE;
5218: PetscBool isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
5219: PetscFV fvm = NULL;
5220: DMField coordField = NULL;
5221: Vec locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
5222: PetscScalar *u = NULL, *u_t, *a, *uL, *uR;
5223: IS chunkIS;
5224: const PetscInt *cells;
5225: PetscInt cStart, cEnd, numCells;
5226: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
5227: PetscInt maxDegree = PETSC_INT_MAX;
5228: PetscQuadrature affineQuad = NULL, *quads = NULL;
5229: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
5231: PetscFunctionBegin;
5232: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5233: if (!cellIS) goto end;
5234: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5235: if (cStart >= cEnd) goto end;
5236: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5237: /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
5238: /* FEM+FVM */
5239: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
5240: /* 1: Get sizes from dm and dmAux */
5241: PetscCall(DMGetLocalSection(dm, §ion));
5242: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5243: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
5244: PetscCall(PetscDSGetNumFields(ds, &Nf));
5245: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5246: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
5247: if (locA) {
5248: PetscInt subcell;
5249: PetscCall(VecGetDM(locA, &dmAux));
5250: PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
5251: PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
5252: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5253: }
5254: /* 2: Get geometric data */
5255: for (f = 0; f < Nf; ++f) {
5256: PetscObject obj;
5257: PetscClassId id;
5258: PetscBool fimp;
5260: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5261: if (isImplicit != fimp) continue;
5262: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5263: PetscCall(PetscObjectGetClassId(obj, &id));
5264: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
5265: if (id == PETSCFV_CLASSID) {
5266: useFVM = PETSC_TRUE;
5267: fvm = (PetscFV)obj;
5268: }
5269: }
5270: if (useFEM) {
5271: PetscCall(DMGetCoordinateField(dm, &coordField));
5272: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5273: if (maxDegree <= 1) {
5274: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
5275: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
5276: } else {
5277: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5278: for (f = 0; f < Nf; ++f) {
5279: PetscObject obj;
5280: PetscClassId id;
5281: PetscBool fimp;
5283: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5284: if (isImplicit != fimp) continue;
5285: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5286: PetscCall(PetscObjectGetClassId(obj, &id));
5287: if (id == PETSCFE_CLASSID) {
5288: PetscFE fe = (PetscFE)obj;
5290: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5291: PetscCall(PetscObjectReference((PetscObject)quads[f]));
5292: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
5293: }
5294: }
5295: }
5296: }
5297: // Handle non-essential (e.g. outflow) boundary values
5298: if (useFVM) {
5299: PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad));
5300: PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
5301: PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
5302: }
5303: /* Loop over chunks */
5304: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
5305: numCells = cEnd - cStart;
5306: numChunks = 1;
5307: cellChunkSize = numCells / numChunks;
5308: faceChunkSize = (fEnd - fStart) / numChunks;
5309: numChunks = PetscMin(1, numCells);
5310: for (chunk = 0; chunk < numChunks; ++chunk) {
5311: PetscScalar *elemVec, *fluxL, *fluxR;
5312: PetscReal *vol;
5313: PetscFVFaceGeom *fgeom;
5314: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5315: PetscInt fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;
5317: /* Extract field coefficients */
5318: if (useFEM) {
5319: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
5320: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5321: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5322: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
5323: }
5324: if (useFVM) {
5325: PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5326: PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5327: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5328: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5329: PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
5330: PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
5331: }
5332: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
5333: /* Loop over fields */
5334: for (f = 0; f < Nf; ++f) {
5335: PetscObject obj;
5336: PetscClassId id;
5337: PetscBool fimp;
5338: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
5340: key.field = f;
5341: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5342: if (isImplicit != fimp) continue;
5343: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5344: PetscCall(PetscObjectGetClassId(obj, &id));
5345: if (id == PETSCFE_CLASSID) {
5346: PetscFE fe = (PetscFE)obj;
5347: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
5348: PetscFEGeom *chunkGeom = NULL;
5349: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5350: PetscInt Nq, Nb;
5352: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5353: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5354: PetscCall(PetscFEGetDimension(fe, &Nb));
5355: blockSize = Nb;
5356: batchSize = numBlocks * blockSize;
5357: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5358: numChunks = numCells / (numBatches * batchSize);
5359: Ne = numChunks * numBatches * batchSize;
5360: Nr = numCells % (numBatches * batchSize);
5361: offset = numCells - Nr;
5362: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
5363: /* 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) */
5364: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5365: PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
5366: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
5367: PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5368: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
5369: } else if (id == PETSCFV_CLASSID) {
5370: PetscFV fv = (PetscFV)obj;
5372: Ne = numFaces;
5373: /* Riemann solve over faces (need fields at face centroids) */
5374: /* We need to evaluate FE fields at those coordinates */
5375: PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
5376: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
5377: }
5378: /* Loop over domain */
5379: if (useFEM) {
5380: /* Add elemVec to locX */
5381: for (c = cS; c < cE; ++c) {
5382: const PetscInt cell = cells ? cells[c] : c;
5383: const PetscInt cind = c - cStart;
5385: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
5386: if (ghostLabel) {
5387: PetscInt ghostVal;
5389: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5390: if (ghostVal > 0) continue;
5391: }
5392: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5393: }
5394: }
5395: if (useFVM) {
5396: PetscScalar *fa;
5397: PetscInt iface;
5399: PetscCall(VecGetArray(locF, &fa));
5400: for (f = 0; f < Nf; ++f) {
5401: PetscFV fv;
5402: PetscObject obj;
5403: PetscClassId id;
5404: PetscInt cdim, foff, pdim;
5406: PetscCall(DMGetCoordinateDim(dm, &cdim));
5407: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5408: PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
5409: PetscCall(PetscObjectGetClassId(obj, &id));
5410: if (id != PETSCFV_CLASSID) continue;
5411: fv = (PetscFV)obj;
5412: PetscCall(PetscFVGetNumComponents(fv, &pdim));
5413: /* Accumulate fluxes to cells */
5414: for (face = fS, iface = 0; face < fE; ++face) {
5415: const PetscInt *scells;
5416: PetscScalar *fL = NULL, *fR = NULL;
5417: PetscInt ghost, d, nsupp, nchild;
5419: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
5420: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
5421: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
5422: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
5423: PetscCall(DMPlexGetSupport(dm, face, &scells));
5424: PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
5425: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
5426: PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
5427: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
5428: if (mesh->printFVM > 1) {
5429: PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal));
5430: PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff]));
5431: PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff]));
5432: PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff]));
5433: PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff]));
5434: }
5435: for (d = 0; d < pdim; ++d) {
5436: if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
5437: if (fR) fR[d] += fluxR[iface * totDim + foff + d];
5438: }
5439: ++iface;
5440: }
5441: }
5442: PetscCall(VecRestoreArray(locF, &fa));
5443: }
5444: /* Handle time derivative */
5445: if (locX_t) {
5446: PetscScalar *x_t, *fa;
5448: PetscCall(VecGetArray(locF, &fa));
5449: PetscCall(VecGetArray(locX_t, &x_t));
5450: for (f = 0; f < Nf; ++f) {
5451: PetscFV fv;
5452: PetscObject obj;
5453: PetscClassId id;
5454: PetscInt pdim;
5456: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5457: PetscCall(PetscObjectGetClassId(obj, &id));
5458: if (id != PETSCFV_CLASSID) continue;
5459: fv = (PetscFV)obj;
5460: PetscCall(PetscFVGetNumComponents(fv, &pdim));
5461: for (c = cS; c < cE; ++c) {
5462: const PetscInt cell = cells ? cells[c] : c;
5463: PetscScalar *u_t, *r;
5465: if (ghostLabel) {
5466: PetscInt ghostVal;
5468: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5469: if (ghostVal > 0) continue;
5470: }
5471: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
5472: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
5473: for (PetscInt d = 0; d < pdim; ++d) r[d] += u_t[d];
5474: }
5475: }
5476: PetscCall(VecRestoreArray(locX_t, &x_t));
5477: PetscCall(VecRestoreArray(locF, &fa));
5478: }
5479: if (useFEM) {
5480: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5481: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5482: }
5483: if (useFVM) {
5484: PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5485: PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5486: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5487: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5488: if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
5489: }
5490: }
5491: if (useFEM) PetscCall(ISDestroy(&chunkIS));
5492: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5494: if (useFEM) {
5495: PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, ctx));
5497: if (maxDegree <= 1) {
5498: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5499: PetscCall(PetscQuadratureDestroy(&affineQuad));
5500: } else {
5501: for (f = 0; f < Nf; ++f) {
5502: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5503: PetscCall(PetscQuadratureDestroy(&quads[f]));
5504: }
5505: PetscCall(PetscFree2(quads, geoms));
5506: }
5507: }
5509: /* FEM */
5510: /* 1: Get sizes from dm and dmAux */
5511: /* 2: Get geometric data */
5512: /* 3: Handle boundary values */
5513: /* 4: Loop over domain */
5514: /* Extract coefficients */
5515: /* Loop over fields */
5516: /* Set tiling for FE*/
5517: /* Integrate FE residual to get elemVec */
5518: /* Loop over subdomain */
5519: /* Loop over quad points */
5520: /* Transform coords to real space */
5521: /* Evaluate field and aux fields at point */
5522: /* Evaluate residual at point */
5523: /* Transform residual to real space */
5524: /* Add residual to elemVec */
5525: /* Loop over domain */
5526: /* Add elemVec to locX */
5528: /* FVM */
5529: /* Get geometric data */
5530: /* If using gradients */
5531: /* Compute gradient data */
5532: /* Loop over domain faces */
5533: /* Count computational faces */
5534: /* Reconstruct cell gradient */
5535: /* Loop over domain cells */
5536: /* Limit cell gradients */
5537: /* Handle boundary values */
5538: /* Loop over domain faces */
5539: /* Read out field, centroid, normal, volume for each side of face */
5540: /* Riemann solve over faces */
5541: /* Loop over domain faces */
5542: /* Accumulate fluxes to cells */
5543: /* TODO Change printFEM to printDisc here */
5544: if (mesh->printFEM) {
5545: Vec locFbc;
5546: PetscInt pStart, pEnd, p, maxDof;
5547: PetscScalar *zeroes;
5549: PetscCall(VecDuplicate(locF, &locFbc));
5550: PetscCall(VecCopy(locF, locFbc));
5551: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5552: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5553: PetscCall(PetscCalloc1(maxDof, &zeroes));
5554: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5555: PetscCall(PetscFree(zeroes));
5556: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5557: PetscCall(VecDestroy(&locFbc));
5558: }
5559: end:
5560: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5561: PetscFunctionReturn(PETSC_SUCCESS);
5562: }
5564: /*@
5565: DMPlexComputeResidualHybridByKey - Compute the local residual over hybrid cells for terms matching the input key
5567: Collective
5569: Input Parameters:
5570: + dm - The output `DM`
5571: . key - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
5572: . cellIS - The `IS` give a set of cells to integrate over
5573: . time - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
5574: . locX - The local solution
5575: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5576: . t - The time
5577: - ctx - An optional application context, passed to the pointwise functions
5579: Output Parameter:
5580: . locF - The local residual
5582: Level: developer
5584: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5585: @*/
5586: PetscErrorCode DMPlexComputeResidualHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
5587: {
5588: DM_Plex *mesh = (DM_Plex *)dm->data;
5589: const char *name = "Hybrid Residual";
5590: DM dmAux[3] = {NULL, NULL, NULL};
5591: DMLabel ghostLabel = NULL;
5592: PetscDS ds = NULL;
5593: PetscDS dsIn = NULL;
5594: PetscDS dsAux[3] = {NULL, NULL, NULL};
5595: Vec locA[3] = {NULL, NULL, NULL};
5596: DM dmScale[3] = {NULL, NULL, NULL};
5597: PetscDS dsScale[3] = {NULL, NULL, NULL};
5598: Vec locS[3] = {NULL, NULL, NULL};
5599: PetscSection section = NULL;
5600: DMField coordField = NULL;
5601: PetscScalar *a[3] = {NULL, NULL, NULL};
5602: PetscScalar *s[3] = {NULL, NULL, NULL};
5603: PetscScalar *u = NULL, *u_t;
5604: PetscScalar *elemVecNeg, *elemVecPos, *elemVecCoh;
5605: IS chunkISF, chunkISN;
5606: const PetscInt *cells;
5607: PetscInt *faces, *neighbors;
5608: PetscInt cStart, cEnd, numCells;
5609: PetscInt Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5610: PetscInt maxDegree = PETSC_INT_MAX;
5611: PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
5612: PetscFEGeom *affineGeomF = NULL, **geomsF = NULL;
5613: PetscQuadrature affineQuadN = NULL, *quadsN = NULL;
5614: PetscFEGeom *affineGeomN = NULL, **geomsN = NULL;
5616: PetscFunctionBegin;
5617: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5618: if (!cellIS) goto end;
5619: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5620: PetscCall(ISGetLocalSize(cellIS, &numCells));
5621: if (cStart >= cEnd) goto end;
5622: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5623: const char *name;
5624: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5625: 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);
5626: }
5627: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5628: /* FEM */
5629: /* 1: Get sizes from dm and dmAux */
5630: PetscCall(DMGetLocalSection(dm, §ion));
5631: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5632: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5633: PetscCall(PetscDSGetNumFields(ds, &Nf));
5634: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5635: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5636: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5637: if (locA[2]) {
5638: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5640: PetscCall(VecGetDM(locA[2], &dmAux[2]));
5641: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5642: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5643: {
5644: const PetscInt *cone;
5645: PetscInt c;
5647: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5648: for (c = 0; c < 2; ++c) {
5649: const PetscInt *support;
5650: PetscInt ssize, s;
5652: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5653: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5654: 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);
5655: if (support[0] == cellStart) s = 1;
5656: else if (support[1] == cellStart) s = 0;
5657: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5658: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5659: 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);
5660: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5661: else dmAux[c] = dmAux[2];
5662: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5663: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5664: }
5665: }
5666: }
5667: /* Handle mass matrix scaling
5668: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5669: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5670: if (locS[2]) {
5671: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5672: PetscInt Nb, Nbs;
5674: PetscCall(VecGetDM(locS[2], &dmScale[2]));
5675: PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5676: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5677: // BRAD: This is not set correctly
5678: key[2].field = 2;
5679: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5680: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5681: 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);
5682: {
5683: const PetscInt *cone;
5685: locS[1] = locS[0] = locS[2];
5686: dmScale[1] = dmScale[0] = dmScale[2];
5687: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5688: for (PetscInt c = 0; c < 2; ++c) {
5689: const PetscInt *support;
5690: PetscInt ssize, s;
5692: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5693: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5694: 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);
5695: if (support[0] == cellStart) s = 1;
5696: else if (support[1] == cellStart) s = 0;
5697: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5698: PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5699: PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5700: }
5701: }
5702: }
5703: /* 2: Setup geometric data */
5704: PetscCall(DMGetCoordinateField(dm, &coordField));
5705: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5706: if (maxDegree > 1) {
5707: PetscCall(PetscCalloc4(Nf, &quadsF, Nf, &geomsF, Nf, &quadsN, Nf, &geomsN));
5708: for (f = 0; f < Nf; ++f) {
5709: PetscFE fe;
5710: PetscBool isCohesiveField;
5712: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5713: if (fe) {
5714: PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
5715: PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
5716: }
5717: PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fe));
5718: PetscCall(PetscDSGetCohesive(dsIn, f, &isCohesiveField));
5719: if (fe) {
5720: if (isCohesiveField) {
5721: for (PetscInt g = 0; g < Nf; ++g) {
5722: PetscCall(PetscDSGetDiscretization(dsIn, g, (PetscObject *)&fe));
5723: PetscCall(PetscDSGetCohesive(dsIn, g, &isCohesiveField));
5724: if (!isCohesiveField) break;
5725: }
5726: }
5727: PetscCall(PetscFEGetQuadrature(fe, &quadsN[f]));
5728: PetscCall(PetscObjectReference((PetscObject)quadsN[f]));
5729: }
5730: }
5731: }
5732: /* Loop over chunks */
5733: cellChunkSize = numCells;
5734: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5735: PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
5736: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
5737: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
5738: /* Extract field coefficients */
5739: /* NOTE This needs the end cap faces to have identical orientations */
5740: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5741: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5742: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5743: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5744: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5745: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5746: for (chunk = 0; chunk < numChunks; ++chunk) {
5747: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5749: PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5750: PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5751: PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5752: /* Get faces and neighbors */
5753: for (c = cS; c < cE; ++c) {
5754: const PetscInt cell = cells ? cells[c] : c;
5755: const PetscInt *cone, *support;
5756: PetscCall(DMPlexGetCone(dm, cell, &cone));
5757: faces[(c - cS) * 2 + 0] = cone[0];
5758: faces[(c - cS) * 2 + 1] = cone[1];
5759: PetscCall(DMPlexGetSupport(dm, cone[0], &support));
5760: neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
5761: PetscCall(DMPlexGetSupport(dm, cone[1], &support));
5762: neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
5763: }
5764: PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5765: PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
5766: /* Get geometric data */
5767: if (maxDegree <= 1) {
5768: if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
5769: if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
5770: if (!affineQuadN) {
5771: PetscInt dim;
5772: PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
5773: PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
5774: PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
5775: }
5776: if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
5777: } else {
5778: for (f = 0; f < Nf; ++f) {
5779: if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
5780: if (quadsN[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, quadsN[f], PETSC_FEGEOM_BASIC, &geomsN[f]));
5781: }
5782: }
5783: /* Loop over fields */
5784: for (f = 0; f < Nf; ++f) {
5785: PetscFE fe;
5786: PetscFEGeom *geomF = affineGeomF ? affineGeomF : geomsF[f];
5787: PetscFEGeom *chunkGeomF = NULL, *remGeomF = NULL;
5788: PetscFEGeom *geomN = affineGeomN ? affineGeomN : geomsN[f];
5789: PetscFEGeom *chunkGeomN = NULL, *remGeomN = NULL;
5790: PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[f];
5791: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5792: PetscBool isCohesiveField;
5794: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5795: if (!fe) continue;
5796: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5797: PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
5798: PetscCall(PetscFEGetDimension(fe, &Nb));
5799: blockSize = Nb;
5800: batchSize = numBlocks * blockSize;
5801: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5802: numChunks = numCells / (numBatches * batchSize);
5803: Ne = numChunks * numBatches * batchSize;
5804: Nr = numCells % (numBatches * batchSize);
5805: offset = numCells - Nr;
5806: PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
5807: PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
5808: PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
5809: PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
5810: PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5811: // TODO Do I need to set isCohesive on the chunks?
5812: key[0].field = f;
5813: key[1].field = f;
5814: key[2].field = f;
5815: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5816: 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]));
5817: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, elemVecPos));
5818: 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]));
5819: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5820: 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]));
5821: PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
5822: PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
5823: PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
5824: PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
5825: }
5826: /* Add elemVec to locX */
5827: for (c = cS; c < cE; ++c) {
5828: const PetscInt cell = cells ? cells[c] : c;
5829: const PetscInt cind = c - cStart;
5831: /* Scale element values */
5832: if (locS[0]) {
5833: PetscInt Nb, off = cind * totDim, soff = cind * totDimScale[0];
5834: PetscBool cohesive;
5836: for (f = 0; f < Nf; ++f) {
5837: PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5838: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5839: if (f == key[2].field) {
5840: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5841: // No cohesive scaling field is currently input
5842: for (PetscInt i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5843: off += Nb;
5844: } else {
5845: const PetscInt N = cohesive ? Nb : Nb * 2;
5847: for (PetscInt i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5848: off += N;
5849: }
5850: }
5851: } else {
5852: for (PetscInt i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5853: }
5854: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5855: if (ghostLabel) {
5856: PetscInt ghostVal;
5858: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5859: if (ghostVal > 0) continue;
5860: }
5861: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5862: }
5863: }
5864: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5865: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5866: PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5867: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5868: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5869: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5870: PetscCall(PetscFree2(faces, neighbors));
5871: PetscCall(ISDestroy(&chunkISF));
5872: PetscCall(ISDestroy(&chunkISN));
5873: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5874: if (maxDegree <= 1) {
5875: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
5876: PetscCall(PetscQuadratureDestroy(&affineQuadF));
5877: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
5878: PetscCall(PetscQuadratureDestroy(&affineQuadN));
5879: } else {
5880: for (f = 0; f < Nf; ++f) {
5881: if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
5882: if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
5883: if (geomsN) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsN[f], PETSC_FALSE, &geomsN[f]));
5884: if (quadsN) PetscCall(PetscQuadratureDestroy(&quadsN[f]));
5885: }
5886: PetscCall(PetscFree4(quadsF, geomsF, quadsN, geomsN));
5887: }
5888: if (mesh->printFEM) {
5889: Vec locFbc;
5890: PetscInt pStart, pEnd, p, maxDof;
5891: PetscScalar *zeroes;
5893: PetscCall(VecDuplicate(locF, &locFbc));
5894: PetscCall(VecCopy(locF, locFbc));
5895: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5896: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5897: PetscCall(PetscCalloc1(maxDof, &zeroes));
5898: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5899: PetscCall(PetscFree(zeroes));
5900: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5901: PetscCall(VecDestroy(&locFbc));
5902: }
5903: end:
5904: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5905: PetscFunctionReturn(PETSC_SUCCESS);
5906: }
5908: /*@
5909: DMPlexComputeBdJacobianSingleByLabel - Compute the local boundary Jacobian for terms matching the input label
5911: Not collective
5913: Input Parameters:
5914: + dm - The output `DM`
5915: . wf - The `PetscWeakForm` holding forms on this boundary
5916: . label - The `DMLabel` indicating what faces should be integrated over
5917: . numValues - The number of label values
5918: . values - The array of label values
5919: . fieldI - The test field for these integrals
5920: . facetIS - The `IS` giving the set of possible faces to integrate over (intersected with the label)
5921: . locX - The local solution
5922: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
5923: . t - The time
5924: . coordField - The `DMField` object with coordinates for these faces
5925: - X_tShift - The multiplier for dF/dxdot
5927: Output Parameters:
5928: + Jac - The local Jacobian
5929: - JacP - The local Jacobian preconditioner
5931: Level: developer
5933: .seealso: `DMPlexComputeBdJacobianSingle()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
5934: @*/
5935: 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)
5936: {
5937: DM_Plex *mesh = (DM_Plex *)dm->data;
5938: DM plex = NULL, plexA = NULL, tdm;
5939: DMEnclosureType encAux;
5940: PetscDS ds, dsAux = NULL;
5941: PetscSection section, sectionAux = NULL;
5942: PetscSection globalSection;
5943: Vec locA = NULL, tv;
5944: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
5945: PetscInt Nf, totDim, totDimAux = 0;
5946: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;
5948: PetscFunctionBegin;
5949: PetscCall(DMHasBasisTransform(dm, &transform));
5950: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5951: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5952: PetscCall(DMGetLocalSection(dm, §ion));
5953: PetscCall(DMGetDS(dm, &ds));
5954: PetscCall(PetscDSGetNumFields(ds, &Nf));
5955: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5956: PetscCall(PetscWeakFormHasBdJacobian(wf, &hasJac));
5957: PetscCall(PetscWeakFormHasBdJacobianPreconditioner(wf, &hasPrec));
5958: if (!hasJac && !hasPrec) PetscFunctionReturn(PETSC_SUCCESS);
5959: PetscCall(DMConvert(dm, DMPLEX, &plex));
5960: PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5961: if (locA) {
5962: DM dmAux;
5964: PetscCall(VecGetDM(locA, &dmAux));
5965: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5966: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5967: PetscCall(DMGetDS(plexA, &dsAux));
5968: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5969: PetscCall(DMGetLocalSection(plexA, §ionAux));
5970: }
5972: PetscCall(DMGetGlobalSection(dm, &globalSection));
5973: for (PetscInt v = 0; v < numValues; ++v) {
5974: PetscFEGeom *fgeom;
5975: PetscInt maxDegree;
5976: PetscQuadrature qGeom = NULL;
5977: IS pointIS;
5978: const PetscInt *points;
5979: PetscFormKey key;
5980: PetscInt numFaces, face, Nq;
5982: key.label = label;
5983: key.value = values[v];
5984: key.part = 0;
5985: PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5986: if (!pointIS) continue; /* No points with that id on this process */
5987: {
5988: IS isectIS;
5990: /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5991: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5992: PetscCall(ISDestroy(&pointIS));
5993: pointIS = isectIS;
5994: }
5995: PetscCall(ISGetLocalSize(pointIS, &numFaces));
5996: PetscCall(ISGetIndices(pointIS, &points));
5997: 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));
5998: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5999: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
6000: if (!qGeom) {
6001: PetscFE fe;
6003: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
6004: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
6005: PetscCall(PetscObjectReference((PetscObject)qGeom));
6006: }
6007: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6008: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
6009: for (face = 0; face < numFaces; ++face) {
6010: const PetscInt point = points[face], *support;
6011: PetscScalar *x = NULL;
6013: PetscCall(DMPlexGetSupport(dm, point, &support));
6014: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
6015: for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
6016: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
6017: if (locX_t) {
6018: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
6019: for (PetscInt i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
6020: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
6021: }
6022: if (locA) {
6023: PetscInt subp;
6024: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
6025: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
6026: for (PetscInt i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
6027: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
6028: }
6029: }
6030: if (elemMat) PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
6031: if (elemMatP) PetscCall(PetscArrayzero(elemMatP, numFaces * totDim * totDim));
6032: {
6033: PetscFE fe;
6034: PetscInt Nb;
6035: /* Conforming batches */
6036: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6037: /* Remainder */
6038: PetscFEGeom *chunkGeom = NULL;
6039: PetscInt fieldJ, Nr, offset;
6041: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
6042: PetscCall(PetscFEGetDimension(fe, &Nb));
6043: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6044: blockSize = Nb;
6045: batchSize = numBlocks * blockSize;
6046: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6047: numChunks = numFaces / (numBatches * batchSize);
6048: Ne = numChunks * numBatches * batchSize;
6049: Nr = numFaces % (numBatches * batchSize);
6050: offset = numFaces - Nr;
6051: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
6052: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6053: key.field = fieldI * Nf + fieldJ;
6054: if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
6055: if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
6056: }
6057: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
6058: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6059: key.field = fieldI * Nf + fieldJ;
6060: if (hasJac)
6061: 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]));
6062: if (hasPrec)
6063: 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]));
6064: }
6065: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
6066: }
6067: for (face = 0; face < numFaces; ++face) {
6068: const PetscInt point = points[face], *support;
6070: /* Transform to global basis before insertion in Jacobian */
6071: PetscCall(DMPlexGetSupport(plex, point, &support));
6072: if (hasJac && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
6073: if (hasPrec && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMatP[face * totDim * totDim]));
6074: if (hasPrec) {
6075: if (hasJac) {
6076: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
6077: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
6078: }
6079: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
6080: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
6081: } else {
6082: if (hasJac) {
6083: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
6084: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
6085: }
6086: }
6087: }
6088: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
6089: PetscCall(PetscQuadratureDestroy(&qGeom));
6090: PetscCall(ISRestoreIndices(pointIS, &points));
6091: PetscCall(ISDestroy(&pointIS));
6092: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
6093: }
6094: PetscCall(DMDestroy(&plex));
6095: PetscCall(DMDestroy(&plexA));
6096: PetscFunctionReturn(PETSC_SUCCESS);
6097: }
6099: /*@
6100: DMPlexComputeBdJacobianSingle - Compute the local boundary Jacobian
6102: Not collective
6104: Input Parameters:
6105: + dm - The output `DM`
6106: . wf - The `PetscWeakForm` holding forms on this boundary
6107: . label - The `DMLabel` indicating what faces should be integrated over
6108: . numValues - The number of label values
6109: . values - The array of label values
6110: . fieldI - The test field for these integrals
6111: . locX - The local solution
6112: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
6113: . t - The time
6114: - X_tShift - The multiplier for dF/dxdot
6116: Output Parameters:
6117: + Jac - The local Jacobian
6118: - JacP - The local Jacobian preconditioner
6120: Level: developer
6122: .seealso: `DMPlexComputeBdJacobianSingleByLabel()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
6123: @*/
6124: 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)
6125: {
6126: DMField coordField;
6127: DMLabel depthLabel;
6128: IS facetIS;
6129: PetscInt dim;
6131: PetscFunctionBegin;
6132: PetscCall(DMGetDimension(dm, &dim));
6133: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6134: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
6135: PetscCall(DMGetCoordinateField(dm, &coordField));
6136: PetscCall(DMPlexComputeBdJacobianSingleByLabel(dm, wf, label, numValues, values, fieldI, facetIS, locX, locX_t, t, coordField, X_tShift, Jac, JacP));
6137: PetscCall(ISDestroy(&facetIS));
6138: PetscFunctionReturn(PETSC_SUCCESS);
6139: }
6141: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, PetscCtx ctx)
6142: {
6143: PetscDS prob;
6144: PetscInt dim, numBd, bd;
6145: DMLabel depthLabel;
6146: DMField coordField = NULL;
6147: IS facetIS;
6149: PetscFunctionBegin;
6150: PetscCall(DMGetDS(dm, &prob));
6151: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6152: PetscCall(DMGetDimension(dm, &dim));
6153: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
6154: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
6155: PetscCall(DMGetCoordinateField(dm, &coordField));
6156: for (bd = 0; bd < numBd; ++bd) {
6157: PetscWeakForm wf;
6158: DMBoundaryConditionType type;
6159: DMLabel label;
6160: const PetscInt *values;
6161: PetscInt fieldI, numValues;
6162: PetscObject obj;
6163: PetscClassId id;
6165: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
6166: if (type & DM_BC_ESSENTIAL) continue;
6167: PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
6168: PetscCall(PetscObjectGetClassId(obj, &id));
6169: if (id != PETSCFE_CLASSID) continue;
6170: PetscCall(DMPlexComputeBdJacobianSingleByLabel(dm, wf, label, numValues, values, fieldI, facetIS, locX, locX_t, t, coordField, X_tShift, Jac, JacP));
6171: }
6172: PetscCall(ISDestroy(&facetIS));
6173: PetscFunctionReturn(PETSC_SUCCESS);
6174: }
6176: /*@
6177: DMPlexComputeJacobianByKey - Compute the local Jacobian for terms matching the input key
6179: Collective
6181: Input Parameters:
6182: + dm - The output `DM`
6183: . key - The `PetscFormKey` indicating what should be integrated
6184: . cellIS - The `IS` give a set of cells to integrate over
6185: . t - The time
6186: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
6187: . locX - The local solution
6188: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
6189: - ctx - An optional application context, passed to the pointwise functions
6191: Output Parameters:
6192: + Jac - The local Jacobian
6193: - JacP - The local Jacobian preconditioner
6195: Level: developer
6197: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
6198: @*/
6199: PetscErrorCode DMPlexComputeJacobianByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
6200: {
6201: DM_Plex *mesh = (DM_Plex *)dm->data;
6202: const char *name = "Jacobian";
6203: DM dmAux = NULL, plex, tdm;
6204: DMEnclosureType encAux;
6205: Vec A, tv;
6206: DMField coordField;
6207: PetscDS prob, probAux = NULL;
6208: PetscSection section, globalSection, sectionAux;
6209: PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
6210: const PetscInt *cells;
6211: PetscInt Nf, fieldI, fieldJ;
6212: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6213: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
6215: PetscFunctionBegin;
6216: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6217: PetscCall(DMGetLocalSection(dm, §ion));
6218: PetscCall(DMGetGlobalSection(dm, &globalSection));
6219: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6220: if (A) {
6221: PetscCall(VecGetDM(A, &dmAux));
6222: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6223: PetscCall(DMConvert(dmAux, DMPLEX, &plex));
6224: PetscCall(DMGetLocalSection(plex, §ionAux));
6225: PetscCall(DMGetDS(dmAux, &probAux));
6226: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6227: }
6228: PetscCall(DMGetCoordinateField(dm, &coordField));
6229: if (!cellIS) goto end;
6230: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6231: PetscCall(ISGetLocalSize(cellIS, &numCells));
6232: if (cStart >= cEnd) goto end;
6233: PetscCall(DMHasBasisTransform(dm, &transform));
6234: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
6235: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
6236: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6237: PetscCall(PetscDSGetNumFields(prob, &Nf));
6238: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6239: PetscCall(PetscDSHasJacobian(prob, &hasJac));
6240: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
6241: /* user passed in the same matrix, avoid double contributions and
6242: only assemble the Jacobian */
6243: if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
6244: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6245: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6246: 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));
6247: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6248: for (c = cStart; c < cEnd; ++c) {
6249: const PetscInt cell = cells ? cells[c] : c;
6250: const PetscInt cind = c - cStart;
6251: PetscScalar *x = NULL, *x_t = NULL;
6253: PetscCall(DMPlexVecGetClosure(dm, section, locX, cell, NULL, &x));
6254: for (PetscInt i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6255: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cell, NULL, &x));
6256: if (locX_t) {
6257: PetscCall(DMPlexVecGetClosure(dm, section, locX_t, cell, NULL, &x_t));
6258: for (PetscInt i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6259: PetscCall(DMPlexVecRestoreClosure(dm, section, locX_t, cell, NULL, &x_t));
6260: }
6261: if (dmAux) {
6262: PetscInt subcell;
6263: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6264: PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
6265: for (PetscInt i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6266: PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
6267: }
6268: }
6269: if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6270: if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
6271: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6272: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6273: PetscClassId id;
6274: PetscFE fe;
6275: PetscQuadrature qGeom = NULL;
6276: PetscInt Nb;
6277: /* Conforming batches */
6278: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6279: /* Remainder */
6280: PetscInt Nr, offset, Nq;
6281: PetscInt maxDegree;
6282: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6284: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6285: PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
6286: if (id == PETSCFV_CLASSID) {
6287: hasFV = PETSC_TRUE;
6288: continue;
6289: }
6290: PetscCall(PetscFEGetDimension(fe, &Nb));
6291: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6292: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6293: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6294: if (!qGeom) {
6295: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6296: PetscCall(PetscObjectReference((PetscObject)qGeom));
6297: }
6298: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6299: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
6300: blockSize = Nb;
6301: batchSize = numBlocks * blockSize;
6302: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6303: numChunks = numCells / (numBatches * batchSize);
6304: Ne = numChunks * numBatches * batchSize;
6305: Nr = numCells % (numBatches * batchSize);
6306: offset = numCells - Nr;
6307: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6308: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6309: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6310: key.field = fieldI * Nf + fieldJ;
6311: if (hasJac) {
6312: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6313: 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]));
6314: }
6315: if (hasPrec) {
6316: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
6317: 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]));
6318: }
6319: if (hasDyn) {
6320: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6321: 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]));
6322: }
6323: }
6324: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6325: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6326: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6327: PetscCall(PetscQuadratureDestroy(&qGeom));
6328: }
6329: /* Add contribution from X_t */
6330: if (hasDyn) {
6331: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6332: }
6333: if (hasFV) {
6334: PetscClassId id;
6335: PetscFV fv;
6336: PetscInt offsetI, NcI, NbI = 1, fc, f;
6338: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6339: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
6340: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
6341: PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
6342: if (id != PETSCFV_CLASSID) continue;
6343: /* Put in the weighted identity */
6344: PetscCall(PetscFVGetNumComponents(fv, &NcI));
6345: for (c = cStart; c < cEnd; ++c) {
6346: const PetscInt cind = c - cStart;
6347: const PetscInt eOffset = cind * totDim * totDim;
6348: PetscReal vol;
6350: PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
6351: for (fc = 0; fc < NcI; ++fc) {
6352: for (f = 0; f < NbI; ++f) {
6353: const PetscInt i = offsetI + f * NcI + fc;
6354: if (hasPrec) {
6355: if (hasJac) elemMat[eOffset + i * totDim + i] = vol;
6356: elemMatP[eOffset + i * totDim + i] = vol;
6357: } else {
6358: elemMat[eOffset + i * totDim + i] = vol;
6359: }
6360: }
6361: }
6362: }
6363: }
6364: /* No allocated space for FV stuff, so ignore the zero entries */
6365: PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
6366: }
6367: /* Insert values into matrix */
6368: for (c = cStart; c < cEnd; ++c) {
6369: const PetscInt cell = cells ? cells[c] : c;
6370: const PetscInt cind = c - cStart;
6372: /* Transform to global basis before insertion in Jacobian */
6373: if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
6374: if (hasPrec) {
6375: if (hasJac) {
6376: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6377: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6378: }
6379: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
6380: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
6381: } else {
6382: if (hasJac) {
6383: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6384: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6385: }
6386: }
6387: }
6388: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6389: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
6390: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
6391: if (dmAux) PetscCall(PetscFree(a));
6392: /* Compute boundary integrals */
6393: PetscCall(DMPlexComputeBdJacobian_Internal(dm, locX, locX_t, t, X_tShift, Jac, JacP, ctx));
6394: /* Assemble matrix */
6395: end: {
6396: PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
6398: if (dmAux) PetscCall(DMDestroy(&plex));
6399: PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
6400: if (hasJac && hasPrec) {
6401: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6402: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6403: }
6404: }
6405: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6406: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6407: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6408: PetscFunctionReturn(PETSC_SUCCESS);
6409: }
6411: 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)
6412: {
6413: DM_Plex *mesh = (DM_Plex *)dmr->data;
6414: const char *name = "Jacobian";
6415: DM dmAux = NULL, plex, tdm;
6416: PetscInt printFEM = mesh->printFEM;
6417: PetscBool clPerm = mesh->useMatClPerm;
6418: DMEnclosureType encAux;
6419: Vec A, tv;
6420: DMField coordField;
6421: PetscDS rds, cds, dsAux = NULL;
6422: PetscSection rsection, rglobalSection, csection, cglobalSection, sectionAux;
6423: PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
6424: const PetscInt *cells;
6425: PetscInt Nf, cNf;
6426: PetscInt totDim, ctotDim, totDimAux = 0, cStart, cEnd, numCells;
6427: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
6428: MPI_Comm comm;
6430: PetscFunctionBegin;
6431: PetscCall(PetscObjectGetComm((PetscObject)dmr, &comm));
6432: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dmr, 0, 0, 0));
6433: PetscCall(DMGetLocalSection(dmr, &rsection));
6434: PetscCall(DMGetGlobalSection(dmr, &rglobalSection));
6435: PetscCall(DMGetLocalSection(dmc, &csection));
6436: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
6437: PetscCall(DMGetAuxiliaryVec(dmr, key.label, key.value, key.part, &A));
6438: if (A) {
6439: PetscCall(VecGetDM(A, &dmAux));
6440: PetscCall(DMGetEnclosureRelation(dmAux, dmr, &encAux));
6441: PetscCall(DMConvert(dmAux, DMPLEX, &plex));
6442: PetscCall(DMGetLocalSection(plex, §ionAux));
6443: PetscCall(DMGetDS(dmAux, &dsAux));
6444: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
6445: }
6446: PetscCall(DMGetCoordinateField(dmr, &coordField));
6447: if (!cellIS) goto end;
6448: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6449: PetscCall(ISGetLocalSize(cellIS, &numCells));
6450: if (cStart >= cEnd) goto end;
6451: PetscCall(DMHasBasisTransform(dmr, &transform));
6452: PetscCall(DMGetBasisTransformDM_Internal(dmr, &tdm));
6453: PetscCall(DMGetBasisTransformVec_Internal(dmr, &tv));
6454: PetscCall(DMGetCellDS(dmr, cells ? cells[cStart] : cStart, &rds, NULL));
6455: PetscCall(DMGetCellDS(dmc, cells ? cells[cStart] : cStart, &cds, NULL));
6456: PetscCall(PetscDSGetNumFields(rds, &Nf));
6457: PetscCall(PetscDSGetNumFields(cds, &cNf));
6458: PetscCheck(Nf == cNf, comm, PETSC_ERR_ARG_WRONG, "Number of row fields %" PetscInt_FMT " != %" PetscInt_FMT " number of columns field", Nf, cNf);
6459: PetscCall(PetscDSGetTotalDimension(rds, &totDim));
6460: PetscCall(PetscDSGetTotalDimension(cds, &ctotDim));
6461: PetscCall(PetscDSHasJacobian(rds, &hasJac));
6462: PetscCall(PetscDSHasJacobianPreconditioner(rds, &hasPrec));
6463: /* user passed in the same matrix, avoid double contributions and
6464: only assemble the Jacobian */
6465: if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
6466: PetscCall(PetscDSHasDynamicJacobian(rds, &hasDyn));
6467: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6468: 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));
6469: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6470: for (PetscInt c = cStart; c < cEnd; ++c) {
6471: const PetscInt cell = cells ? cells[c] : c;
6472: const PetscInt cind = c - cStart;
6473: PetscScalar *x = NULL, *x_t = NULL;
6475: PetscCall(DMPlexVecGetClosure(dmr, rsection, locX, cell, NULL, &x));
6476: for (PetscInt i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6477: PetscCall(DMPlexVecRestoreClosure(dmr, rsection, locX, cell, NULL, &x));
6478: if (locX_t) {
6479: PetscCall(DMPlexVecGetClosure(dmr, rsection, locX_t, cell, NULL, &x_t));
6480: for (PetscInt i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6481: PetscCall(DMPlexVecRestoreClosure(dmr, rsection, locX_t, cell, NULL, &x_t));
6482: }
6483: if (dmAux) {
6484: PetscInt subcell;
6485: PetscCall(DMGetEnclosurePoint(dmAux, dmr, encAux, cell, &subcell));
6486: PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
6487: for (PetscInt i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6488: PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
6489: }
6490: }
6491: if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * ctotDim));
6492: if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * ctotDim));
6493: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * ctotDim));
6494: for (PetscInt fieldI = 0; fieldI < Nf; ++fieldI) {
6495: PetscClassId id;
6496: PetscFE fe;
6497: PetscQuadrature qGeom = NULL;
6498: PetscInt Nb;
6499: /* Conforming batches */
6500: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6501: /* Remainder */
6502: PetscInt Nr, offset, Nq;
6503: PetscInt maxDegree;
6504: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6506: PetscCall(PetscDSGetDiscretization(rds, fieldI, (PetscObject *)&fe));
6507: PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
6508: if (id == PETSCFV_CLASSID) {
6509: hasFV = PETSC_TRUE;
6510: continue;
6511: }
6512: PetscCall(PetscFEGetDimension(fe, &Nb));
6513: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6514: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6515: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6516: if (!qGeom) {
6517: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6518: PetscCall(PetscObjectReference((PetscObject)qGeom));
6519: }
6520: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6521: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
6522: blockSize = Nb;
6523: batchSize = numBlocks * blockSize;
6524: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6525: numChunks = numCells / (numBatches * batchSize);
6526: Ne = numChunks * numBatches * batchSize;
6527: Nr = numCells % (numBatches * batchSize);
6528: offset = numCells - Nr;
6529: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6530: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6531: for (PetscInt fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6532: key.field = fieldI * Nf + fieldJ;
6533: if (hasJac) {
6534: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
6535: 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]));
6536: }
6537: if (hasPrec) {
6538: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
6539: 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]));
6540: }
6541: if (hasDyn) {
6542: PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatD));
6543: 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]));
6544: }
6545: }
6546: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6547: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6548: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6549: PetscCall(PetscQuadratureDestroy(&qGeom));
6550: }
6551: /* Add contribution from X_t */
6552: if (hasDyn) {
6553: for (PetscInt c = 0; c < numCells * totDim * ctotDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6554: }
6555: if (hasFV) {
6556: PetscClassId id;
6557: PetscFV fv;
6558: PetscInt offsetI, NcI, NbI = 1;
6560: for (PetscInt fieldI = 0; fieldI < Nf; ++fieldI) {
6561: PetscCall(PetscDSGetDiscretization(rds, fieldI, (PetscObject *)&fv));
6562: PetscCall(PetscDSGetFieldOffset(rds, fieldI, &offsetI));
6563: PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
6564: if (id != PETSCFV_CLASSID) continue;
6565: /* Put in the weighted identity */
6566: PetscCall(PetscFVGetNumComponents(fv, &NcI));
6567: for (PetscInt c = cStart; c < cEnd; ++c) {
6568: const PetscInt cind = c - cStart;
6569: const PetscInt eOffset = cind * totDim * ctotDim;
6570: PetscReal vol;
6572: PetscCall(DMPlexComputeCellGeometryFVM(dmr, c, &vol, NULL, NULL));
6573: for (PetscInt fc = 0; fc < NcI; ++fc) {
6574: for (PetscInt f = 0; f < NbI; ++f) {
6575: const PetscInt i = offsetI + f * NcI + fc;
6576: if (hasPrec) {
6577: if (hasJac) elemMat[eOffset + i * ctotDim + i] = vol;
6578: elemMatP[eOffset + i * ctotDim + i] = vol;
6579: } else {
6580: elemMat[eOffset + i * ctotDim + i] = vol;
6581: }
6582: }
6583: }
6584: }
6585: }
6586: /* No allocated space for FV stuff, so ignore the zero entries */
6587: PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
6588: }
6589: /* Insert values into matrix */
6590: for (PetscInt c = cStart; c < cEnd; ++c) {
6591: const PetscInt cell = cells ? cells[c] : c;
6592: const PetscInt cind = c - cStart;
6594: /* Transform to global basis before insertion in Jacobian */
6595: if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dmr, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * ctotDim]));
6596: if (hasPrec) {
6597: if (hasJac) {
6598: if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMat[cind * totDim * ctotDim]));
6599: PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, Jac, cell, &elemMat[cind * totDim * ctotDim], ADD_VALUES));
6600: }
6601: if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMatP[cind * totDim * ctotDim]));
6602: PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, JacP, cell, &elemMatP[cind * totDim * ctotDim], ADD_VALUES));
6603: } else {
6604: if (hasJac) {
6605: if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMat[cind * totDim * ctotDim]));
6606: PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, JacP, cell, &elemMat[cind * totDim * ctotDim], ADD_VALUES));
6607: }
6608: }
6609: }
6610: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6611: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
6612: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
6613: if (dmAux) PetscCall(PetscFree(a));
6614: /* Compute boundary integrals */
6615: PetscCall(DMPlexComputeBdJacobian_Internal(dmr, locX, locX_t, t, X_tShift, Jac, JacP, ctx));
6616: /* Assemble matrix */
6617: end: {
6618: PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
6620: if (dmAux) PetscCall(DMDestroy(&plex));
6621: PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPI_C_BOOL, MPI_LOR, comm));
6622: if (hasJac && hasPrec) {
6623: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6624: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6625: }
6626: }
6627: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6628: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6629: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dmr, 0, 0, 0));
6630: PetscFunctionReturn(PETSC_SUCCESS);
6631: }
6633: /*@
6634: DMPlexComputeJacobianHybridByKey - Compute the local Jacobian over hybrid cells for terms matching the input key
6636: Collective
6638: Input Parameters:
6639: + dm - The output `DM`
6640: . key - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
6641: . cellIS - The `IS` give a set of cells to integrate over
6642: . t - The time
6643: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
6644: . locX - The local solution
6645: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
6646: - ctx - An optional application context, passed to the pointwise functions
6648: Output Parameters:
6649: + Jac - The local Jacobian
6650: - JacP - The local Jacobian preconditioner
6652: Level: developer
6654: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `PetscFormKey`
6655: @*/
6656: PetscErrorCode DMPlexComputeJacobianHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
6657: {
6658: DM_Plex *mesh = (DM_Plex *)dm->data;
6659: const char *name = "Hybrid Jacobian";
6660: DM dmAux[3] = {NULL, NULL, NULL};
6661: DMLabel ghostLabel = NULL;
6662: DM plex = NULL;
6663: DM plexA = NULL;
6664: PetscDS ds = NULL;
6665: PetscDS dsIn = NULL;
6666: PetscDS dsAux[3] = {NULL, NULL, NULL};
6667: Vec locA[3] = {NULL, NULL, NULL};
6668: DM dmScale[3] = {NULL, NULL, NULL};
6669: PetscDS dsScale[3] = {NULL, NULL, NULL};
6670: Vec locS[3] = {NULL, NULL, NULL};
6671: PetscSection section = NULL;
6672: PetscSection sectionAux[3] = {NULL, NULL, NULL};
6673: DMField coordField = NULL;
6674: PetscScalar *a[3] = {NULL, NULL, NULL};
6675: PetscScalar *s[3] = {NULL, NULL, NULL};
6676: PetscScalar *u = NULL, *u_t;
6677: PetscScalar *elemMatNeg, *elemMatPos, *elemMatCoh;
6678: PetscScalar *elemMatNegP, *elemMatPosP, *elemMatCohP;
6679: PetscSection globalSection;
6680: IS chunkISF, chunkISN;
6681: const PetscInt *cells;
6682: PetscInt *faces, *neighbors;
6683: PetscInt cStart, cEnd, numCells;
6684: PetscInt Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
6685: PetscInt maxDegree = PETSC_INT_MAX;
6686: PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
6687: PetscFEGeom *affineGeomF = NULL, **geomsF = NULL;
6688: PetscQuadrature affineQuadN = NULL;
6689: PetscFEGeom *affineGeomN = NULL;
6690: PetscBool hasBdJac, hasBdPrec;
6692: PetscFunctionBegin;
6693: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6694: if (!cellIS) goto end;
6695: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6696: PetscCall(ISGetLocalSize(cellIS, &numCells));
6697: if (cStart >= cEnd) goto end;
6698: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
6699: const char *name;
6700: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
6701: 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);
6702: }
6703: PetscCall(DMConvert(dm, DMPLEX, &plex));
6704: PetscCall(DMGetLocalSection(dm, §ion));
6705: PetscCall(DMGetGlobalSection(dm, &globalSection));
6706: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
6707: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
6708: PetscCall(PetscDSGetNumFields(ds, &Nf));
6709: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
6710: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
6711: PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
6712: PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
6713: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
6714: if (locA[2]) {
6715: const PetscInt cellStart = cells ? cells[cStart] : cStart;
6717: PetscCall(VecGetDM(locA[2], &dmAux[2]));
6718: PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
6719: PetscCall(DMGetLocalSection(dmAux[2], §ionAux[2]));
6720: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
6721: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
6722: {
6723: const PetscInt *cone;
6724: PetscInt c;
6726: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6727: for (c = 0; c < 2; ++c) {
6728: const PetscInt *support;
6729: PetscInt ssize, s;
6731: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6732: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6733: 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);
6734: if (support[0] == cellStart) s = 1;
6735: else if (support[1] == cellStart) s = 0;
6736: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6737: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
6738: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
6739: else dmAux[c] = dmAux[2];
6740: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
6741: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
6742: }
6743: }
6744: }
6745: /* Handle mass matrix scaling
6746: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
6747: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
6748: if (locS[2]) {
6749: const PetscInt cellStart = cells ? cells[cStart] : cStart;
6750: PetscInt Nb, Nbs;
6752: PetscCall(VecGetDM(locS[2], &dmScale[2]));
6753: PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6754: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6755: // BRAD: This is not set correctly
6756: key[2].field = 2;
6757: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6758: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6759: 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);
6760: {
6761: const PetscInt *cone;
6763: locS[1] = locS[0] = locS[2];
6764: dmScale[1] = dmScale[0] = dmScale[2];
6765: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6766: for (PetscInt c = 0; c < 2; ++c) {
6767: const PetscInt *support;
6768: PetscInt ssize, s;
6770: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6771: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6772: 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);
6773: if (support[0] == cellStart) s = 1;
6774: else if (support[1] == cellStart) s = 0;
6775: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6776: PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
6777: PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
6778: }
6779: }
6780: }
6781: /* 2: Setup geometric data */
6782: PetscCall(DMGetCoordinateField(dm, &coordField));
6783: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6784: if (maxDegree > 1) {
6785: PetscCall(PetscCalloc2(Nf, &quadsF, Nf, &geomsF));
6786: for (PetscInt f = 0; f < Nf; ++f) {
6787: PetscFE fe;
6789: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
6790: if (fe) {
6791: PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
6792: PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
6793: }
6794: }
6795: }
6796: /* Loop over chunks */
6797: cellChunkSize = numCells;
6798: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
6799: PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
6800: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
6801: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
6802: /* Extract field coefficients */
6803: /* NOTE This needs the end cap faces to have identical orientations */
6804: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6805: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6806: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
6807: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6808: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6809: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6810: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6811: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6812: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6813: for (chunk = 0; chunk < numChunks; ++chunk) {
6814: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
6816: if (hasBdJac) {
6817: PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
6818: PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
6819: PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
6820: }
6821: if (hasBdPrec) {
6822: PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
6823: PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
6824: PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
6825: }
6826: /* Get faces */
6827: for (c = cS; c < cE; ++c) {
6828: const PetscInt cell = cells ? cells[c] : c;
6829: const PetscInt *cone, *support;
6830: PetscCall(DMPlexGetCone(plex, cell, &cone));
6831: faces[(c - cS) * 2 + 0] = cone[0];
6832: faces[(c - cS) * 2 + 1] = cone[1];
6833: PetscCall(DMPlexGetSupport(dm, cone[0], &support));
6834: neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
6835: PetscCall(DMPlexGetSupport(dm, cone[1], &support));
6836: neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
6837: }
6838: PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
6839: PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
6840: if (maxDegree <= 1) {
6841: if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
6842: if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
6843: if (!affineQuadN) {
6844: PetscInt dim;
6845: PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
6846: PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
6847: PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
6848: }
6849: if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
6850: } else {
6851: for (PetscInt f = 0; f < Nf; ++f) {
6852: if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
6853: }
6854: }
6856: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6857: PetscFE feI;
6858: PetscFEGeom *geomF = affineGeomF ? affineGeomF : geomsF[fieldI];
6859: PetscFEGeom *chunkGeomF = NULL, *remGeomF = NULL;
6860: PetscFEGeom *geomN = affineGeomN ? affineGeomN : geomsF[fieldI];
6861: PetscFEGeom *chunkGeomN = NULL, *remGeomN = NULL;
6862: PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[fieldI];
6863: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6864: PetscBool isCohesiveField;
6866: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6867: if (!feI) continue;
6868: PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6869: PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
6870: PetscCall(PetscFEGetDimension(feI, &Nb));
6871: blockSize = Nb;
6872: batchSize = numBlocks * blockSize;
6873: PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6874: numChunks = numCells / (numBatches * batchSize);
6875: Ne = numChunks * numBatches * batchSize;
6876: Nr = numCells % (numBatches * batchSize);
6877: offset = numCells - Nr;
6878: PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
6879: PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
6880: PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
6881: PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
6882: PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6883: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6884: PetscFE feJ;
6886: PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
6887: if (!feJ) continue;
6888: key[0].field = fieldI * Nf + fieldJ;
6889: key[1].field = fieldI * Nf + fieldJ;
6890: key[2].field = fieldI * Nf + fieldJ;
6891: if (hasBdJac) {
6892: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
6893: 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]));
6894: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
6895: 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]));
6896: }
6897: if (hasBdPrec) {
6898: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
6899: 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]));
6900: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
6901: 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]));
6902: }
6903: if (hasBdJac) {
6904: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
6905: 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]));
6906: }
6907: if (hasBdPrec) {
6908: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
6909: 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]));
6910: }
6911: }
6912: PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
6913: PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
6914: PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
6915: PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
6916: }
6917: /* Insert values into matrix */
6918: for (c = cS; c < cE; ++c) {
6919: const PetscInt cell = cells ? cells[c] : c;
6920: const PetscInt cind = c - cS, coff = cind * totDim * totDim;
6922: /* Scale element values */
6923: if (locS[0]) {
6924: PetscInt Nb, soff = cind * totDimScale[0], off = 0;
6925: PetscBool cohesive;
6927: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6928: PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6929: PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));
6931: if (fieldI == key[2].field) {
6932: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6933: for (PetscInt i = 0; i < Nb; ++i) {
6934: for (PetscInt 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];
6935: if (hasBdPrec)
6936: for (PetscInt 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];
6937: }
6938: off += Nb;
6939: } else {
6940: const PetscInt N = cohesive ? Nb : Nb * 2;
6942: for (PetscInt i = 0; i < N; ++i) {
6943: for (PetscInt j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
6944: if (hasBdPrec)
6945: for (PetscInt j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
6946: }
6947: off += N;
6948: }
6949: }
6950: } else {
6951: for (PetscInt i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
6952: if (hasBdPrec)
6953: for (PetscInt i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
6954: }
6955: if (hasBdPrec) {
6956: if (hasBdJac) {
6957: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6958: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6959: }
6960: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
6961: PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
6962: } else if (hasBdJac) {
6963: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6964: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6965: }
6966: }
6967: }
6968: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6969: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6970: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6971: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6972: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6973: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6974: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6975: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6976: PetscCall(PetscFree2(faces, neighbors));
6977: PetscCall(ISDestroy(&chunkISF));
6978: PetscCall(ISDestroy(&chunkISN));
6979: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6980: if (maxDegree <= 1) {
6981: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
6982: PetscCall(PetscQuadratureDestroy(&affineQuadF));
6983: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
6984: PetscCall(PetscQuadratureDestroy(&affineQuadN));
6985: } else {
6986: for (PetscInt f = 0; f < Nf; ++f) {
6987: if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
6988: if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
6989: }
6990: PetscCall(PetscFree2(quadsF, geomsF));
6991: }
6992: if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6993: PetscCall(DMDestroy(&plex));
6994: end:
6995: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6996: PetscFunctionReturn(PETSC_SUCCESS);
6997: }
6999: /*@
7000: DMPlexComputeJacobianActionByKey - Compute the local Jacobian for terms matching the input key
7002: Collective
7004: Input Parameters:
7005: + dm - The output `DM`
7006: . key - The `PetscFormKey` indicating what should be integrated
7007: . cellIS - The `IS` give a set of cells to integrate over
7008: . t - The time
7009: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
7010: . locX - The local solution
7011: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
7012: . locY - The local vector acted on by J
7013: - ctx - An optional application context, passed to the pointwise functions
7015: Output Parameter:
7016: . locF - The local residual F = J(X) Y
7018: Level: developer
7020: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
7021: @*/
7022: PetscErrorCode DMPlexComputeJacobianActionByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Vec locY, Vec locF, PetscCtx ctx)
7023: {
7024: DM_Plex *mesh = (DM_Plex *)dm->data;
7025: const char *name = "Jacobian";
7026: DM dmAux = NULL, plex, plexAux = NULL;
7027: DMEnclosureType encAux;
7028: Vec A;
7029: DMField coordField;
7030: PetscDS prob, probAux = NULL;
7031: PetscQuadrature quad;
7032: PetscSection section, globalSection, sectionAux;
7033: PetscScalar *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
7034: const PetscInt *cells;
7035: PetscInt Nf, fieldI, fieldJ;
7036: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
7037: PetscBool hasDyn;
7039: PetscFunctionBegin;
7040: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
7041: PetscCall(DMConvert(dm, DMPLEX, &plex));
7042: PetscCall(ISGetLocalSize(cellIS, &numCells));
7043: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
7044: PetscCall(DMGetLocalSection(dm, §ion));
7045: PetscCall(DMGetGlobalSection(dm, &globalSection));
7046: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
7047: PetscCall(PetscDSGetNumFields(prob, &Nf));
7048: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
7049: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
7050: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
7051: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
7052: if (A) {
7053: PetscCall(VecGetDM(A, &dmAux));
7054: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
7055: PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
7056: PetscCall(DMGetLocalSection(plexAux, §ionAux));
7057: PetscCall(DMGetDS(dmAux, &probAux));
7058: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
7059: }
7060: PetscCall(VecSet(locF, 0.0));
7061: 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));
7062: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
7063: PetscCall(DMGetCoordinateField(dm, &coordField));
7064: for (c = cStart; c < cEnd; ++c) {
7065: const PetscInt cell = cells ? cells[c] : c;
7066: const PetscInt cind = c - cStart;
7067: PetscScalar *x = NULL, *x_t = NULL;
7069: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
7070: for (PetscInt i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
7071: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
7072: if (locX_t) {
7073: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
7074: for (PetscInt i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
7075: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
7076: }
7077: if (dmAux) {
7078: PetscInt subcell;
7079: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
7080: PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
7081: for (PetscInt i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
7082: PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
7083: }
7084: PetscCall(DMPlexVecGetClosure(plex, section, locY, cell, NULL, &x));
7085: for (PetscInt i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
7086: PetscCall(DMPlexVecRestoreClosure(plex, section, locY, cell, NULL, &x));
7087: }
7088: PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
7089: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
7090: for (fieldI = 0; fieldI < Nf; ++fieldI) {
7091: PetscFE fe;
7092: PetscInt Nb;
7093: /* Conforming batches */
7094: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
7095: /* Remainder */
7096: PetscInt Nr, offset, Nq;
7097: PetscQuadrature qGeom = NULL;
7098: PetscInt maxDegree;
7099: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
7101: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
7102: PetscCall(PetscFEGetQuadrature(fe, &quad));
7103: PetscCall(PetscFEGetDimension(fe, &Nb));
7104: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
7105: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
7106: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
7107: if (!qGeom) {
7108: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
7109: PetscCall(PetscObjectReference((PetscObject)qGeom));
7110: }
7111: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
7112: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
7113: blockSize = Nb;
7114: batchSize = numBlocks * blockSize;
7115: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
7116: numChunks = numCells / (numBatches * batchSize);
7117: Ne = numChunks * numBatches * batchSize;
7118: Nr = numCells % (numBatches * batchSize);
7119: offset = numCells - Nr;
7120: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
7121: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
7122: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
7123: key.field = fieldI * Nf + fieldJ;
7124: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
7125: 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]));
7126: if (hasDyn) {
7127: PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
7128: 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]));
7129: }
7130: }
7131: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
7132: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
7133: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
7134: PetscCall(PetscQuadratureDestroy(&qGeom));
7135: }
7136: if (hasDyn) {
7137: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
7138: }
7139: for (c = cStart; c < cEnd; ++c) {
7140: const PetscInt cell = cells ? cells[c] : c;
7141: const PetscInt cind = c - cStart;
7142: const PetscBLASInt one = 1;
7143: PetscBLASInt M;
7144: const PetscScalar a = 1.0, b = 0.0;
7146: PetscCall(PetscBLASIntCast(totDim, &M));
7147: PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
7148: if (mesh->printFEM > 1) {
7149: PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
7150: PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
7151: PetscCall(DMPrintCellVector(c, "Z", totDim, z));
7152: }
7153: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, z, ADD_VALUES));
7154: }
7155: PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
7156: if (mesh->printFEM) {
7157: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)locF), "Z:\n"));
7158: PetscCall(VecView(locF, NULL));
7159: }
7160: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
7161: PetscCall(PetscFree(a));
7162: PetscCall(DMDestroy(&plexAux));
7163: PetscCall(DMDestroy(&plex));
7164: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
7165: PetscFunctionReturn(PETSC_SUCCESS);
7166: }
7168: 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[])
7169: {
7170: f0[0] = u[0];
7171: }
7173: 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[])
7174: {
7175: f0[0] = x[(int)PetscRealPart(constants[0])] * u[0];
7176: }
7178: 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[])
7179: {
7180: f0[0] = 0.0;
7181: for (PetscInt d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
7182: }
7184: /*@
7185: DMPlexComputeMoments - Compute the first three moments for a field
7187: Noncollective
7189: Input Parameters:
7190: + dm - the `DMPLEX`
7191: - u - the field
7193: Output Parameter:
7194: . moments - the field moments
7196: Level: intermediate
7198: Note:
7199: The `moments` array should be of length cdim + 2, where cdim is the number of components for the coordinate field.
7201: .seealso: `DM`, `DMPLEX`, `DMSwarmComputeMoments()`
7202: @*/
7203: PetscErrorCode DMPlexComputeMoments(DM dm, Vec u, PetscReal moments[])
7204: {
7205: PetscDS ds;
7206: PetscScalar mom, constants[1];
7207: const PetscScalar *oldConstants;
7208: PetscInt cdim, Nf, field = 0, Ncon;
7209: MPI_Comm comm;
7210: void *ctx;
7212: PetscFunctionBeginUser;
7213: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
7214: PetscCall(DMGetCoordinateDim(dm, &cdim));
7215: PetscCall(DMGetApplicationContext(dm, &ctx));
7216: PetscCall(DMGetDS(dm, &ds));
7217: PetscCall(PetscDSGetNumFields(ds, &Nf));
7218: PetscCall(PetscDSGetConstants(ds, &Ncon, &oldConstants));
7219: PetscCheck(Nf == 1, comm, PETSC_ERR_ARG_WRONG, "We currently only support 1 field, not %" PetscInt_FMT, Nf);
7220: PetscCall(PetscDSSetObjective(ds, field, &f0_1));
7221: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7222: moments[0] = PetscRealPart(mom);
7223: for (PetscInt c = 0; c < cdim; ++c) {
7224: constants[0] = c;
7225: PetscCall(PetscDSSetConstants(ds, 1, constants));
7226: PetscCall(PetscDSSetObjective(ds, field, &f0_x));
7227: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7228: moments[c + 1] = PetscRealPart(mom);
7229: }
7230: PetscCall(PetscDSSetObjective(ds, field, &f0_x2));
7231: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
7232: moments[cdim + 1] = PetscRealPart(mom);
7233: PetscCall(PetscDSSetConstants(ds, Ncon, (PetscScalar *)oldConstants));
7234: PetscFunctionReturn(PETSC_SUCCESS);
7235: }