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