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