Actual source code: plexfem.c
1: #include "petscdm.h"
2: #include "petscerror.h"
3: #include <petsc/private/dmpleximpl.h>
4: #include <petscsf.h>
6: #include <petscblaslapack.h>
7: #include <petsc/private/hashsetij.h>
8: #include <petsc/private/petscfeimpl.h>
9: #include <petsc/private/petscfvimpl.h>
11: PetscBool Clementcite = PETSC_FALSE;
12: const char ClementCitation[] = "@article{clement1975approximation,\n"
13: " title = {Approximation by finite element functions using local regularization},\n"
14: " author = {Philippe Cl{\\'e}ment},\n"
15: " journal = {Revue fran{\\c{c}}aise d'automatique, informatique, recherche op{\\'e}rationnelle. Analyse num{\\'e}rique},\n"
16: " volume = {9},\n"
17: " number = {R2},\n"
18: " pages = {77--84},\n"
19: " year = {1975}\n}\n";
21: static PetscErrorCode DMPlexConvertPlex(DM dm, DM *plex, PetscBool copy)
22: {
23: PetscBool isPlex;
25: PetscFunctionBegin;
26: PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
27: if (isPlex) {
28: *plex = dm;
29: PetscCall(PetscObjectReference((PetscObject)dm));
30: } else {
31: PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
32: if (!*plex) {
33: PetscCall(DMConvert(dm, DMPLEX, plex));
34: PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
35: } else {
36: PetscCall(PetscObjectReference((PetscObject)*plex));
37: }
38: if (copy) {
39: DMSubDomainHookLink link;
41: PetscCall(DMCopyDS(dm, PETSC_DETERMINE, PETSC_DETERMINE, *plex));
42: PetscCall(DMCopyAuxiliaryVec(dm, *plex));
43: /* Run the subdomain hook (this will copy the DMSNES/DMTS) */
44: for (link = dm->subdomainhook; link; link = link->next) {
45: if (link->ddhook) PetscCall((*link->ddhook)(dm, *plex, link->ctx));
46: }
47: }
48: }
49: PetscFunctionReturn(PETSC_SUCCESS);
50: }
52: static PetscErrorCode PetscContainerCtxDestroy_PetscFEGeom(void **ctx)
53: {
54: PetscFEGeom *geom = (PetscFEGeom *)*ctx;
56: PetscFunctionBegin;
57: PetscCall(PetscFEGeomDestroy(&geom));
58: PetscFunctionReturn(PETSC_SUCCESS);
59: }
61: static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
62: {
63: char composeStr[33] = {0};
64: PetscObjectId id;
65: PetscContainer container;
67: PetscFunctionBegin;
68: PetscCall(PetscObjectGetId((PetscObject)quad, &id));
69: PetscCall(PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%" PetscInt64_FMT "\n", id));
70: PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
71: if (container) {
72: PetscCall(PetscContainerGetPointer(container, (void **)geom));
73: } else {
74: PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
75: PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
76: PetscCall(PetscContainerSetPointer(container, (void *)*geom));
77: PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
78: PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
79: PetscCall(PetscContainerDestroy(&container));
80: }
81: PetscFunctionReturn(PETSC_SUCCESS);
82: }
84: static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
85: {
86: PetscFunctionBegin;
87: *geom = NULL;
88: PetscFunctionReturn(PETSC_SUCCESS);
89: }
91: /*@
92: DMPlexGetScale - Get the scale for the specified fundamental unit
94: Not Collective
96: Input Parameters:
97: + dm - the `DM`
98: - unit - The SI unit
100: Output Parameter:
101: . scale - The value used to scale all quantities with this unit
103: Level: advanced
105: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetScale()`, `PetscUnit`
106: @*/
107: PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
108: {
109: DM_Plex *mesh = (DM_Plex *)dm->data;
111: PetscFunctionBegin;
113: PetscAssertPointer(scale, 3);
114: *scale = mesh->scale[unit];
115: PetscFunctionReturn(PETSC_SUCCESS);
116: }
118: /*@
119: DMPlexSetScale - Set the scale for the specified fundamental unit
121: Not Collective
123: Input Parameters:
124: + dm - the `DM`
125: . unit - The SI unit
126: - scale - The value used to scale all quantities with this unit
128: Level: advanced
130: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetScale()`, `PetscUnit`
131: @*/
132: PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
133: {
134: DM_Plex *mesh = (DM_Plex *)dm->data;
136: PetscFunctionBegin;
138: mesh->scale[unit] = scale;
139: PetscFunctionReturn(PETSC_SUCCESS);
140: }
142: PetscErrorCode DMPlexGetUseCeed_Plex(DM dm, PetscBool *useCeed)
143: {
144: DM_Plex *mesh = (DM_Plex *)dm->data;
146: PetscFunctionBegin;
147: *useCeed = mesh->useCeed;
148: PetscFunctionReturn(PETSC_SUCCESS);
149: }
150: PetscErrorCode DMPlexSetUseCeed_Plex(DM dm, PetscBool useCeed)
151: {
152: DM_Plex *mesh = (DM_Plex *)dm->data;
154: PetscFunctionBegin;
155: mesh->useCeed = useCeed;
156: PetscFunctionReturn(PETSC_SUCCESS);
157: }
159: /*@
160: DMPlexGetUseCeed - Get flag for using the LibCEED backend
162: Not collective
164: Input Parameter:
165: . dm - The `DM`
167: Output Parameter:
168: . useCeed - The flag
170: Level: intermediate
172: .seealso: `DMPlexSetUseCeed()`
173: @*/
174: PetscErrorCode DMPlexGetUseCeed(DM dm, PetscBool *useCeed)
175: {
176: PetscFunctionBegin;
178: PetscAssertPointer(useCeed, 2);
179: *useCeed = PETSC_FALSE;
180: PetscTryMethod(dm, "DMPlexGetUseCeed_C", (DM, PetscBool *), (dm, useCeed));
181: PetscFunctionReturn(PETSC_SUCCESS);
182: }
184: /*@
185: DMPlexSetUseCeed - Set flag for using the LibCEED backend
187: Not collective
189: Input Parameters:
190: + dm - The `DM`
191: - useCeed - The flag
193: Level: intermediate
195: .seealso: `DMPlexGetUseCeed()`
196: @*/
197: PetscErrorCode DMPlexSetUseCeed(DM dm, PetscBool useCeed)
198: {
199: PetscFunctionBegin;
202: PetscUseMethod(dm, "DMPlexSetUseCeed_C", (DM, PetscBool), (dm, useCeed));
203: PetscFunctionReturn(PETSC_SUCCESS);
204: }
206: /*@
207: DMPlexGetUseMatClosurePermutation - Get flag for using a closure permutation for matrix insertion
209: Not collective
211: Input Parameter:
212: . dm - The `DM`
214: Output Parameter:
215: . useClPerm - The flag
217: Level: intermediate
219: .seealso: `DMPlexSetUseMatClosurePermutation()`
220: @*/
221: PetscErrorCode DMPlexGetUseMatClosurePermutation(DM dm, PetscBool *useClPerm)
222: {
223: DM_Plex *mesh = (DM_Plex *)dm->data;
225: PetscFunctionBegin;
227: PetscAssertPointer(useClPerm, 2);
228: *useClPerm = mesh->useMatClPerm;
229: PetscFunctionReturn(PETSC_SUCCESS);
230: }
232: /*@
233: DMPlexSetUseMatClosurePermutation - Set flag for using a closure permutation for matrix insertion
235: Not collective
237: Input Parameters:
238: + dm - The `DM`
239: - useClPerm - The flag
241: Level: intermediate
243: .seealso: `DMPlexGetUseMatClosurePermutation()`
244: @*/
245: PetscErrorCode DMPlexSetUseMatClosurePermutation(DM dm, PetscBool useClPerm)
246: {
247: DM_Plex *mesh = (DM_Plex *)dm->data;
249: PetscFunctionBegin;
252: mesh->useMatClPerm = useClPerm;
253: PetscFunctionReturn(PETSC_SUCCESS);
254: }
256: static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, void *ctx)
257: {
258: const PetscInt eps[3][3][3] = {
259: {{0, 0, 0}, {0, 0, 1}, {0, -1, 0}},
260: {{0, 0, -1}, {0, 0, 0}, {1, 0, 0} },
261: {{0, 1, 0}, {-1, 0, 0}, {0, 0, 0} }
262: };
263: PetscInt *ctxInt = (PetscInt *)ctx;
264: PetscInt dim2 = ctxInt[0];
265: PetscInt d = ctxInt[1];
266: PetscInt i, j, k = dim > 2 ? d - dim : d;
268: PetscFunctionBegin;
269: PetscCheck(dim == dim2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %" PetscInt_FMT " does not match context dimension %" PetscInt_FMT, dim, dim2);
270: for (i = 0; i < dim; i++) mode[i] = 0.;
271: if (d < dim) {
272: mode[d] = 1.; /* Translation along axis d */
273: } else {
274: for (i = 0; i < dim; i++) {
275: for (j = 0; j < dim; j++) { mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */ }
276: }
277: }
278: PetscFunctionReturn(PETSC_SUCCESS);
279: }
281: /*@
282: DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation
284: Collective
286: Input Parameters:
287: + dm - the `DM`
288: - field - The field number for the rigid body space, or 0 for the default
290: Output Parameter:
291: . sp - the null space
293: Level: advanced
295: Note:
296: This is necessary to provide a suitable coarse space for algebraic multigrid
298: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`, `PCGAMG`
299: @*/
300: PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
301: {
302: PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
303: MPI_Comm comm;
304: Vec mode[6];
305: PetscSection section, globalSection;
306: PetscInt dim, dimEmbed, Nf, n, m, mmin, d, i, j;
307: void **ctxs;
309: PetscFunctionBegin;
310: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
311: PetscCall(DMGetDimension(dm, &dim));
312: PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
313: PetscCall(DMGetNumFields(dm, &Nf));
314: PetscCheck(!Nf || !(field < 0 || field >= Nf), comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", field, Nf);
315: if (dim == 1 && Nf < 2) {
316: PetscCall(MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp));
317: PetscFunctionReturn(PETSC_SUCCESS);
318: }
319: PetscCall(DMGetLocalSection(dm, §ion));
320: PetscCall(DMGetGlobalSection(dm, &globalSection));
321: PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
322: PetscCall(PetscCalloc2(Nf, &func, Nf, &ctxs));
323: m = (dim * (dim + 1)) / 2;
324: PetscCall(VecCreate(comm, &mode[0]));
325: PetscCall(VecSetType(mode[0], dm->vectype));
326: PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
327: PetscCall(VecSetUp(mode[0]));
328: PetscCall(VecGetSize(mode[0], &n));
329: mmin = PetscMin(m, n);
330: func[field] = DMPlexProjectRigidBody_Private;
331: for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
332: for (d = 0; d < m; d++) {
333: PetscInt ctx[2];
335: ctxs[field] = (void *)(&ctx[0]);
336: ctx[0] = dimEmbed;
337: ctx[1] = d;
338: PetscCall(DMProjectFunction(dm, 0.0, func, ctxs, INSERT_VALUES, mode[d]));
339: }
340: /* Orthonormalize system */
341: for (i = 0; i < mmin; ++i) {
342: PetscScalar dots[6];
344: PetscCall(VecNormalize(mode[i], NULL));
345: PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1));
346: for (j = i + 1; j < mmin; ++j) {
347: dots[j] *= -1.0;
348: PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
349: }
350: }
351: PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp));
352: for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
353: PetscCall(PetscFree2(func, ctxs));
354: PetscFunctionReturn(PETSC_SUCCESS);
355: }
357: /*@
358: DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation
360: Collective
362: Input Parameters:
363: + dm - the `DM`
364: . nb - The number of bodies
365: . label - The `DMLabel` marking each domain
366: . nids - The number of ids per body
367: - ids - An array of the label ids in sequence for each domain
369: Output Parameter:
370: . sp - the null space
372: Level: advanced
374: Note:
375: This is necessary to provide a suitable coarse space for algebraic multigrid
377: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`
378: @*/
379: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
380: {
381: MPI_Comm comm;
382: PetscSection section, globalSection;
383: Vec *mode;
384: PetscScalar *dots;
385: PetscInt dim, dimEmbed, n, m, b, d, i, j, off;
387: PetscFunctionBegin;
388: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
389: PetscCall(DMGetDimension(dm, &dim));
390: PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
391: PetscCall(DMGetLocalSection(dm, §ion));
392: PetscCall(DMGetGlobalSection(dm, &globalSection));
393: PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
394: m = nb * (dim * (dim + 1)) / 2;
395: PetscCall(PetscMalloc2(m, &mode, m, &dots));
396: PetscCall(VecCreate(comm, &mode[0]));
397: PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
398: PetscCall(VecSetUp(mode[0]));
399: for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
400: for (b = 0, off = 0; b < nb; ++b) {
401: for (d = 0; d < m / nb; ++d) {
402: PetscInt ctx[2];
403: PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
404: void *voidctx = (void *)(&ctx[0]);
406: ctx[0] = dimEmbed;
407: ctx[1] = d;
408: PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]));
409: off += nids[b];
410: }
411: }
412: /* Orthonormalize system */
413: for (i = 0; i < m; ++i) {
414: PetscScalar dots[6];
416: PetscCall(VecNormalize(mode[i], NULL));
417: PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1));
418: for (j = i + 1; j < m; ++j) {
419: dots[j] *= -1.0;
420: PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
421: }
422: }
423: PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp));
424: for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
425: PetscCall(PetscFree2(mode, dots));
426: PetscFunctionReturn(PETSC_SUCCESS);
427: }
429: /*@
430: DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
431: are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
432: evaluating the dual space basis of that point.
434: Input Parameters:
435: + dm - the `DMPLEX` object
436: - height - the maximum projection height >= 0
438: Level: advanced
440: Notes:
441: A basis function is associated with the point in its transitively-closed support whose mesh
442: height is highest (w.r.t. DAG height), but not greater than the maximum projection height,
443: which is set with this function. By default, the maximum projection height is zero, which
444: means that only mesh cells are used to project basis functions. A height of one, for
445: example, evaluates a cell-interior basis functions using its cells dual space basis, but all
446: other basis functions with the dual space basis of a face.
448: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
449: @*/
450: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
451: {
452: DM_Plex *plex = (DM_Plex *)dm->data;
454: PetscFunctionBegin;
456: plex->maxProjectionHeight = height;
457: PetscFunctionReturn(PETSC_SUCCESS);
458: }
460: /*@
461: DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in
462: DMPlexProjectXXXLocal() functions.
464: Input Parameter:
465: . dm - the `DMPLEX` object
467: Output Parameter:
468: . height - the maximum projection height
470: Level: intermediate
472: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
473: @*/
474: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
475: {
476: DM_Plex *plex = (DM_Plex *)dm->data;
478: PetscFunctionBegin;
480: *height = plex->maxProjectionHeight;
481: PetscFunctionReturn(PETSC_SUCCESS);
482: }
484: typedef struct {
485: PetscReal alpha; /* The first Euler angle, and in 2D the only one */
486: PetscReal beta; /* The second Euler angle */
487: PetscReal gamma; /* The third Euler angle */
488: PetscInt dim; /* The dimension of R */
489: PetscScalar *R; /* The rotation matrix, transforming a vector in the local basis to the global basis */
490: PetscScalar *RT; /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
491: } RotCtx;
493: /*
494: Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
495: 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:
496: $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
497: $ 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.
498: $ The XYZ system rotates a third time about the z axis by gamma.
499: */
500: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
501: {
502: RotCtx *rc = (RotCtx *)ctx;
503: PetscInt dim = rc->dim;
504: PetscReal c1, s1, c2, s2, c3, s3;
506: PetscFunctionBegin;
507: PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT));
508: switch (dim) {
509: case 2:
510: c1 = PetscCosReal(rc->alpha);
511: s1 = PetscSinReal(rc->alpha);
512: rc->R[0] = c1;
513: rc->R[1] = s1;
514: rc->R[2] = -s1;
515: rc->R[3] = c1;
516: PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
517: DMPlex_Transpose2D_Internal(rc->RT);
518: break;
519: case 3:
520: c1 = PetscCosReal(rc->alpha);
521: s1 = PetscSinReal(rc->alpha);
522: c2 = PetscCosReal(rc->beta);
523: s2 = PetscSinReal(rc->beta);
524: c3 = PetscCosReal(rc->gamma);
525: s3 = PetscSinReal(rc->gamma);
526: rc->R[0] = c1 * c3 - c2 * s1 * s3;
527: rc->R[1] = c3 * s1 + c1 * c2 * s3;
528: rc->R[2] = s2 * s3;
529: rc->R[3] = -c1 * s3 - c2 * c3 * s1;
530: rc->R[4] = c1 * c2 * c3 - s1 * s3;
531: rc->R[5] = c3 * s2;
532: rc->R[6] = s1 * s2;
533: rc->R[7] = -c1 * s2;
534: rc->R[8] = c2;
535: PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
536: DMPlex_Transpose3D_Internal(rc->RT);
537: break;
538: default:
539: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
540: }
541: PetscFunctionReturn(PETSC_SUCCESS);
542: }
544: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
545: {
546: RotCtx *rc = (RotCtx *)ctx;
548: PetscFunctionBegin;
549: PetscCall(PetscFree2(rc->R, rc->RT));
550: PetscCall(PetscFree(rc));
551: PetscFunctionReturn(PETSC_SUCCESS);
552: }
554: static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, void *ctx)
555: {
556: RotCtx *rc = (RotCtx *)ctx;
558: PetscFunctionBeginHot;
559: PetscAssertPointer(ctx, 5);
560: if (l2g) {
561: *A = rc->R;
562: } else {
563: *A = rc->RT;
564: }
565: PetscFunctionReturn(PETSC_SUCCESS);
566: }
568: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
569: {
570: PetscFunctionBegin;
571: #if defined(PETSC_USE_COMPLEX)
572: switch (dim) {
573: case 2: {
574: PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};
576: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
577: z[0] = PetscRealPart(zt[0]);
578: z[1] = PetscRealPart(zt[1]);
579: } break;
580: case 3: {
581: PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};
583: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
584: z[0] = PetscRealPart(zt[0]);
585: z[1] = PetscRealPart(zt[1]);
586: z[2] = PetscRealPart(zt[2]);
587: } break;
588: }
589: #else
590: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx));
591: #endif
592: PetscFunctionReturn(PETSC_SUCCESS);
593: }
595: PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, void *ctx)
596: {
597: const PetscScalar *A;
599: PetscFunctionBeginHot;
600: PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx));
601: switch (dim) {
602: case 2:
603: DMPlex_Mult2D_Internal(A, 1, y, z);
604: break;
605: case 3:
606: DMPlex_Mult3D_Internal(A, 1, y, z);
607: break;
608: }
609: PetscFunctionReturn(PETSC_SUCCESS);
610: }
612: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
613: {
614: PetscSection ts;
615: const PetscScalar *ta, *tva;
616: PetscInt dof;
618: PetscFunctionBeginHot;
619: PetscCall(DMGetLocalSection(tdm, &ts));
620: PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
621: PetscCall(VecGetArrayRead(tv, &ta));
622: PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva));
623: if (l2g) {
624: switch (dof) {
625: case 4:
626: DMPlex_Mult2D_Internal(tva, 1, a, a);
627: break;
628: case 9:
629: DMPlex_Mult3D_Internal(tva, 1, a, a);
630: break;
631: }
632: } else {
633: switch (dof) {
634: case 4:
635: DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
636: break;
637: case 9:
638: DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
639: break;
640: }
641: }
642: PetscCall(VecRestoreArrayRead(tv, &ta));
643: PetscFunctionReturn(PETSC_SUCCESS);
644: }
646: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
647: {
648: PetscSection s, ts;
649: const PetscScalar *ta, *tvaf, *tvag;
650: PetscInt fdof, gdof, fpdof, gpdof;
652: PetscFunctionBeginHot;
653: PetscCall(DMGetLocalSection(dm, &s));
654: PetscCall(DMGetLocalSection(tdm, &ts));
655: PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof));
656: PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof));
657: PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof));
658: PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof));
659: PetscCall(VecGetArrayRead(tv, &ta));
660: PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf));
661: PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag));
662: if (l2g) {
663: switch (fdof) {
664: case 4:
665: DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
666: break;
667: case 9:
668: DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
669: break;
670: }
671: switch (gdof) {
672: case 4:
673: DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
674: break;
675: case 9:
676: DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
677: break;
678: }
679: } else {
680: switch (fdof) {
681: case 4:
682: DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
683: break;
684: case 9:
685: DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
686: break;
687: }
688: switch (gdof) {
689: case 4:
690: DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
691: break;
692: case 9:
693: DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
694: break;
695: }
696: }
697: PetscCall(VecRestoreArrayRead(tv, &ta));
698: PetscFunctionReturn(PETSC_SUCCESS);
699: }
701: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
702: {
703: PetscSection s;
704: PetscSection clSection;
705: IS clPoints;
706: const PetscInt *clp;
707: PetscInt *points = NULL;
708: PetscInt Nf, f, Np, cp, dof, d = 0;
710: PetscFunctionBegin;
711: PetscCall(DMGetLocalSection(dm, &s));
712: PetscCall(PetscSectionGetNumFields(s, &Nf));
713: PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
714: for (f = 0; f < Nf; ++f) {
715: for (cp = 0; cp < Np * 2; cp += 2) {
716: PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof));
717: if (!dof) continue;
718: if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]));
719: d += dof;
720: }
721: }
722: PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
723: PetscFunctionReturn(PETSC_SUCCESS);
724: }
726: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
727: {
728: PetscSection s;
729: PetscSection clSection;
730: IS clPoints;
731: const PetscInt *clp;
732: PetscInt *points = NULL;
733: PetscInt Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;
735: PetscFunctionBegin;
736: PetscCall(DMGetLocalSection(dm, &s));
737: PetscCall(PetscSectionGetNumFields(s, &Nf));
738: PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
739: for (f = 0, r = 0; f < Nf; ++f) {
740: for (cpf = 0; cpf < Np * 2; cpf += 2) {
741: PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof));
742: for (g = 0, c = 0; g < Nf; ++g) {
743: for (cpg = 0; cpg < Np * 2; cpg += 2) {
744: PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof));
745: PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]));
746: c += gdof;
747: }
748: }
749: PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
750: r += fdof;
751: }
752: }
753: PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
754: PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
755: PetscFunctionReturn(PETSC_SUCCESS);
756: }
758: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
759: {
760: DM tdm;
761: Vec tv;
762: PetscSection ts, s;
763: const PetscScalar *ta;
764: PetscScalar *a, *va;
765: PetscInt pStart, pEnd, p, Nf, f;
767: PetscFunctionBegin;
768: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
769: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
770: PetscCall(DMGetLocalSection(tdm, &ts));
771: PetscCall(DMGetLocalSection(dm, &s));
772: PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
773: PetscCall(PetscSectionGetNumFields(s, &Nf));
774: PetscCall(VecGetArray(lv, &a));
775: PetscCall(VecGetArrayRead(tv, &ta));
776: for (p = pStart; p < pEnd; ++p) {
777: for (f = 0; f < Nf; ++f) {
778: PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va));
779: PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va));
780: }
781: }
782: PetscCall(VecRestoreArray(lv, &a));
783: PetscCall(VecRestoreArrayRead(tv, &ta));
784: PetscFunctionReturn(PETSC_SUCCESS);
785: }
787: /*@
788: DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis
790: Input Parameters:
791: + dm - The `DM`
792: - lv - A local vector with values in the global basis
794: Output Parameter:
795: . lv - A local vector with values in the local basis
797: Level: developer
799: Note:
800: 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.
802: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
803: @*/
804: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
805: {
806: PetscFunctionBegin;
809: PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE));
810: PetscFunctionReturn(PETSC_SUCCESS);
811: }
813: /*@
814: DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis
816: Input Parameters:
817: + dm - The `DM`
818: - lv - A local vector with values in the local basis
820: Output Parameter:
821: . lv - A local vector with values in the global basis
823: Level: developer
825: Note:
826: 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.
828: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
829: @*/
830: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
831: {
832: PetscFunctionBegin;
835: PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE));
836: PetscFunctionReturn(PETSC_SUCCESS);
837: }
839: /*@
840: DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions
841: and global solutions, to a local basis, appropriate for discretization integrals and assembly.
843: Input Parameters:
844: + dm - The `DM`
845: . alpha - The first Euler angle, and in 2D the only one
846: . beta - The second Euler angle
847: - gamma - The third Euler angle
849: Level: developer
851: Note:
852: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
853: 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
854: .vb
855: The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
856: 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.
857: The XYZ system rotates a third time about the z axis by gamma.
858: .ve
860: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
861: @*/
862: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
863: {
864: RotCtx *rc;
865: PetscInt cdim;
867: PetscFunctionBegin;
868: PetscCall(DMGetCoordinateDim(dm, &cdim));
869: PetscCall(PetscMalloc1(1, &rc));
870: dm->transformCtx = rc;
871: dm->transformSetUp = DMPlexBasisTransformSetUp_Rotation_Internal;
872: dm->transformDestroy = DMPlexBasisTransformDestroy_Rotation_Internal;
873: dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
874: rc->dim = cdim;
875: rc->alpha = alpha;
876: rc->beta = beta;
877: rc->gamma = gamma;
878: PetscCall((*dm->transformSetUp)(dm, dm->transformCtx));
879: PetscCall(DMConstructBasisTransform_Internal(dm));
880: PetscFunctionReturn(PETSC_SUCCESS);
881: }
883: /*@C
884: DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector using a function of the coordinates
886: Input Parameters:
887: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
888: . time - The time
889: . field - The field to constrain
890: . Nc - The number of constrained field components, or 0 for all components
891: . comps - An array of constrained component numbers, or `NULL` for all components
892: . label - The `DMLabel` defining constrained points
893: . numids - The number of `DMLabel` ids for constrained points
894: . ids - An array of ids for constrained points
895: . func - A pointwise function giving boundary values
896: - ctx - An optional user context for bcFunc
898: Output Parameter:
899: . locX - A local vector to receives the boundary values
901: Level: developer
903: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
904: @*/
905: PetscErrorCode DMPlexInsertBoundaryValuesEssential(DM dm, PetscReal time, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, Vec locX)
906: {
907: PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
908: void **ctxs;
909: PetscInt numFields;
911: PetscFunctionBegin;
912: PetscCall(DMGetNumFields(dm, &numFields));
913: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
914: funcs[field] = func;
915: ctxs[field] = ctx;
916: PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX));
917: PetscCall(PetscFree2(funcs, ctxs));
918: PetscFunctionReturn(PETSC_SUCCESS);
919: }
921: /*@C
922: DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector using a function of the coordinates and field data
924: Input Parameters:
925: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
926: . time - The time
927: . locU - A local vector with the input solution values
928: . field - The field to constrain
929: . Nc - The number of constrained field components, or 0 for all components
930: . comps - An array of constrained component numbers, or `NULL` for all components
931: . label - The `DMLabel` defining constrained points
932: . numids - The number of `DMLabel` ids for constrained points
933: . ids - An array of ids for constrained points
934: . func - A pointwise function giving boundary values
935: - ctx - An optional user context for bcFunc
937: Output Parameter:
938: . locX - A local vector to receives the boundary values
940: Level: developer
942: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
943: @*/
944: 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[]), void *ctx, Vec locX)
945: {
946: 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[]);
947: void **ctxs;
948: PetscInt numFields;
950: PetscFunctionBegin;
951: PetscCall(DMGetNumFields(dm, &numFields));
952: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
953: funcs[field] = func;
954: ctxs[field] = ctx;
955: PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
956: PetscCall(PetscFree2(funcs, ctxs));
957: PetscFunctionReturn(PETSC_SUCCESS);
958: }
960: /*@C
961: DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coordinates and boundary field data
963: Collective
965: Input Parameters:
966: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
967: . time - The time
968: . locU - A local vector with the input solution values
969: . field - The field to constrain
970: . Nc - The number of constrained field components, or 0 for all components
971: . comps - An array of constrained component numbers, or `NULL` for all components
972: . label - The `DMLabel` defining constrained points
973: . numids - The number of `DMLabel` ids for constrained points
974: . ids - An array of ids for constrained points
975: . func - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()`
976: - ctx - An optional user context for `func`
978: Output Parameter:
979: . locX - A local vector to receive the boundary values
981: Level: developer
983: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
984: @*/
985: 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[]), void *ctx, Vec locX)
986: {
987: 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[]);
988: void **ctxs;
989: PetscInt numFields;
991: PetscFunctionBegin;
992: PetscCall(DMGetNumFields(dm, &numFields));
993: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
994: funcs[field] = func;
995: ctxs[field] = ctx;
996: PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
997: PetscCall(PetscFree2(funcs, ctxs));
998: PetscFunctionReturn(PETSC_SUCCESS);
999: }
1001: /*@C
1002: DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector
1004: Input Parameters:
1005: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
1006: . time - The time
1007: . faceGeometry - A vector with the FVM face geometry information
1008: . cellGeometry - A vector with the FVM cell geometry information
1009: . Grad - A vector with the FVM cell gradient information
1010: . field - The field to constrain
1011: . Nc - The number of constrained field components, or 0 for all components
1012: . comps - An array of constrained component numbers, or `NULL` for all components
1013: . label - The `DMLabel` defining constrained points
1014: . numids - The number of `DMLabel` ids for constrained points
1015: . ids - An array of ids for constrained points
1016: . func - A pointwise function giving boundary values
1017: - ctx - An optional user context for bcFunc
1019: Output Parameter:
1020: . locX - A local vector to receives the boundary values
1022: Level: developer
1024: Note:
1025: This implementation currently ignores the numcomps/comps argument from `DMAddBoundary()`
1027: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
1028: @*/
1029: 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 *), void *ctx, Vec locX)
1030: {
1031: PetscDS prob;
1032: PetscSF sf;
1033: DM dmFace, dmCell, dmGrad;
1034: const PetscScalar *facegeom, *cellgeom = NULL, *grad;
1035: const PetscInt *leaves;
1036: PetscScalar *x, *fx;
1037: PetscInt dim, nleaves, loc, fStart, fEnd, pdim, i;
1038: PetscErrorCode ierru = PETSC_SUCCESS;
1040: PetscFunctionBegin;
1041: PetscCall(DMGetPointSF(dm, &sf));
1042: PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
1043: nleaves = PetscMax(0, nleaves);
1044: PetscCall(DMGetDimension(dm, &dim));
1045: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1046: PetscCall(DMGetDS(dm, &prob));
1047: PetscCall(VecGetDM(faceGeometry, &dmFace));
1048: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
1049: if (cellGeometry) {
1050: PetscCall(VecGetDM(cellGeometry, &dmCell));
1051: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
1052: }
1053: if (Grad) {
1054: PetscFV fv;
1056: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv));
1057: PetscCall(VecGetDM(Grad, &dmGrad));
1058: PetscCall(VecGetArrayRead(Grad, &grad));
1059: PetscCall(PetscFVGetNumComponents(fv, &pdim));
1060: PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx));
1061: }
1062: PetscCall(VecGetArray(locX, &x));
1063: for (i = 0; i < numids; ++i) {
1064: IS faceIS;
1065: const PetscInt *faces;
1066: PetscInt numFaces, f;
1068: PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS));
1069: if (!faceIS) continue; /* No points with that id on this process */
1070: PetscCall(ISGetLocalSize(faceIS, &numFaces));
1071: PetscCall(ISGetIndices(faceIS, &faces));
1072: for (f = 0; f < numFaces; ++f) {
1073: const PetscInt face = faces[f], *cells;
1074: PetscFVFaceGeom *fg;
1076: if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
1077: PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc));
1078: if (loc >= 0) continue;
1079: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
1080: PetscCall(DMPlexGetSupport(dm, face, &cells));
1081: if (Grad) {
1082: PetscFVCellGeom *cg;
1083: PetscScalar *cx, *cgrad;
1084: PetscScalar *xG;
1085: PetscReal dx[3];
1086: PetscInt d;
1088: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
1089: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
1090: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
1091: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
1092: DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
1093: for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
1094: PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
1095: } else {
1096: PetscScalar *xI;
1097: PetscScalar *xG;
1099: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
1100: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
1101: ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
1102: if (ierru) {
1103: PetscCall(ISRestoreIndices(faceIS, &faces));
1104: PetscCall(ISDestroy(&faceIS));
1105: goto cleanup;
1106: }
1107: }
1108: }
1109: PetscCall(ISRestoreIndices(faceIS, &faces));
1110: PetscCall(ISDestroy(&faceIS));
1111: }
1112: cleanup:
1113: PetscCall(VecRestoreArray(locX, &x));
1114: if (Grad) {
1115: PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
1116: PetscCall(VecRestoreArrayRead(Grad, &grad));
1117: }
1118: if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
1119: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
1120: PetscCall(ierru);
1121: PetscFunctionReturn(PETSC_SUCCESS);
1122: }
1124: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
1125: {
1126: PetscInt c;
1127: for (c = 0; c < Nc; ++c) u[c] = 0.0;
1128: return PETSC_SUCCESS;
1129: }
1131: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1132: {
1133: PetscObject isZero;
1134: PetscDS prob;
1135: PetscInt numBd, b;
1137: PetscFunctionBegin;
1138: PetscCall(DMGetDS(dm, &prob));
1139: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1140: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1141: PetscCall(PetscDSUpdateBoundaryLabels(prob, dm));
1142: for (b = 0; b < numBd; ++b) {
1143: PetscWeakForm wf;
1144: DMBoundaryConditionType type;
1145: const char *name;
1146: DMLabel label;
1147: PetscInt field, Nc;
1148: const PetscInt *comps;
1149: PetscObject obj;
1150: PetscClassId id;
1151: void (*bvfunc)(void);
1152: PetscInt numids;
1153: const PetscInt *ids;
1154: void *ctx;
1156: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1157: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1158: PetscCall(DMGetField(dm, field, NULL, &obj));
1159: PetscCall(PetscObjectGetClassId(obj, &id));
1160: if (id == PETSCFE_CLASSID) {
1161: switch (type) {
1162: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1163: case DM_BC_ESSENTIAL: {
1164: PetscSimplePointFn *func = (PetscSimplePointFn *)bvfunc;
1166: if (isZero) func = zero;
1167: PetscCall(DMPlexLabelAddCells(dm, label));
1168: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
1169: PetscCall(DMPlexLabelClearCells(dm, label));
1170: } break;
1171: case DM_BC_ESSENTIAL_FIELD: {
1172: PetscPointFunc func = (PetscPointFunc)bvfunc;
1174: PetscCall(DMPlexLabelAddCells(dm, label));
1175: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
1176: PetscCall(DMPlexLabelClearCells(dm, label));
1177: } break;
1178: default:
1179: break;
1180: }
1181: } else if (id == PETSCFV_CLASSID) {
1182: {
1183: PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode (*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;
1185: if (!faceGeomFVM) continue;
1186: PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
1187: }
1188: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1189: }
1190: PetscFunctionReturn(PETSC_SUCCESS);
1191: }
1193: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1194: {
1195: PetscObject isZero;
1196: PetscDS prob;
1197: PetscInt numBd, b;
1199: PetscFunctionBegin;
1200: if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
1201: PetscCall(DMGetDS(dm, &prob));
1202: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1203: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1204: for (b = 0; b < numBd; ++b) {
1205: PetscWeakForm wf;
1206: DMBoundaryConditionType type;
1207: const char *name;
1208: DMLabel label;
1209: PetscInt field, Nc;
1210: const PetscInt *comps;
1211: PetscObject obj;
1212: PetscClassId id;
1213: PetscInt numids;
1214: const PetscInt *ids;
1215: void (*bvfunc)(void);
1216: void *ctx;
1218: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
1219: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1220: PetscCall(DMGetField(dm, field, NULL, &obj));
1221: PetscCall(PetscObjectGetClassId(obj, &id));
1222: if (id == PETSCFE_CLASSID) {
1223: switch (type) {
1224: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1225: case DM_BC_ESSENTIAL: {
1226: PetscSimplePointFn *func_t = (PetscSimplePointFn *)bvfunc;
1228: if (isZero) func_t = zero;
1229: PetscCall(DMPlexLabelAddCells(dm, label));
1230: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1231: PetscCall(DMPlexLabelClearCells(dm, label));
1232: } break;
1233: case DM_BC_ESSENTIAL_FIELD: {
1234: PetscPointFunc func_t = (PetscPointFunc)bvfunc;
1236: PetscCall(DMPlexLabelAddCells(dm, label));
1237: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1238: PetscCall(DMPlexLabelClearCells(dm, label));
1239: } break;
1240: default:
1241: break;
1242: }
1243: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1244: }
1245: PetscFunctionReturn(PETSC_SUCCESS);
1246: }
1248: /*@
1249: DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector
1251: Not Collective
1253: Input Parameters:
1254: + dm - The `DM`
1255: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1256: . time - The time
1257: . faceGeomFVM - Face geometry data for FV discretizations
1258: . cellGeomFVM - Cell geometry data for FV discretizations
1259: - gradFVM - Gradient reconstruction data for FV discretizations
1261: Output Parameter:
1262: . locX - Solution updated with boundary values
1264: Level: intermediate
1266: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1267: @*/
1268: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1269: {
1270: PetscFunctionBegin;
1276: PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1277: PetscFunctionReturn(PETSC_SUCCESS);
1278: }
1280: /*@
1281: DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector
1283: Input Parameters:
1284: + dm - The `DM`
1285: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1286: . time - The time
1287: . faceGeomFVM - Face geometry data for FV discretizations
1288: . cellGeomFVM - Cell geometry data for FV discretizations
1289: - gradFVM - Gradient reconstruction data for FV discretizations
1291: Output Parameter:
1292: . locX_t - Solution updated with boundary values
1294: Level: developer
1296: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1297: @*/
1298: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1299: {
1300: PetscFunctionBegin;
1306: PetscTryMethod(dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1307: PetscFunctionReturn(PETSC_SUCCESS);
1308: }
1310: // Handle non-essential (e.g. outflow) boundary values
1311: PetscErrorCode DMPlexInsertBoundaryValuesFVM(DM dm, PetscFV fv, Vec locX, PetscReal time, Vec *locGradient)
1312: {
1313: DM dmGrad;
1314: Vec cellGeometryFVM, faceGeometryFVM, locGrad = NULL;
1316: PetscFunctionBegin;
1320: if (locGradient) {
1321: PetscAssertPointer(locGradient, 5);
1322: *locGradient = NULL;
1323: }
1324: PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
1325: /* Reconstruct and limit cell gradients */
1326: PetscCall(DMPlexGetGradientDM(dm, fv, &dmGrad));
1327: if (dmGrad) {
1328: Vec grad;
1329: PetscInt fStart, fEnd;
1331: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1332: PetscCall(DMGetGlobalVector(dmGrad, &grad));
1333: PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
1334: /* Communicate gradient values */
1335: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
1336: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
1337: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
1338: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
1339: }
1340: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
1341: if (locGradient) *locGradient = locGrad;
1342: else if (locGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
1343: PetscFunctionReturn(PETSC_SUCCESS);
1344: }
1346: PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1347: {
1348: Vec localX;
1350: PetscFunctionBegin;
1351: PetscCall(DMGetLocalVector(dm, &localX));
1352: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1353: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1354: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1355: PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1356: PetscCall(DMRestoreLocalVector(dm, &localX));
1357: PetscFunctionReturn(PETSC_SUCCESS);
1358: }
1360: /*@C
1361: DMPlexComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
1363: Collective
1365: Input Parameters:
1366: + dm - The `DM`
1367: . time - The time
1368: . funcs - The functions to evaluate for each field component
1369: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1370: - localX - The coefficient vector u_h, a local vector
1372: Output Parameter:
1373: . diff - The diff ||u - u_h||_2
1375: Level: developer
1377: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1378: @*/
1379: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1380: {
1381: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1382: DM tdm;
1383: Vec tv;
1384: PetscSection section;
1385: PetscQuadrature quad;
1386: PetscFEGeom fegeom;
1387: PetscScalar *funcVal, *interpolant;
1388: PetscReal *coords, *gcoords;
1389: PetscReal localDiff = 0.0;
1390: const PetscReal *quadWeights;
1391: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1392: PetscBool transform;
1394: PetscFunctionBegin;
1395: PetscCall(DMGetDimension(dm, &dim));
1396: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1397: fegeom.dimEmbed = coordDim;
1398: PetscCall(DMGetLocalSection(dm, §ion));
1399: PetscCall(PetscSectionGetNumFields(section, &numFields));
1400: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1401: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1402: PetscCall(DMHasBasisTransform(dm, &transform));
1403: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1404: for (field = 0; field < numFields; ++field) {
1405: PetscObject obj;
1406: PetscClassId id;
1407: PetscInt Nc;
1409: PetscCall(DMGetField(dm, field, NULL, &obj));
1410: PetscCall(PetscObjectGetClassId(obj, &id));
1411: if (id == PETSCFE_CLASSID) {
1412: PetscFE fe = (PetscFE)obj;
1414: PetscCall(PetscFEGetQuadrature(fe, &quad));
1415: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1416: } else if (id == PETSCFV_CLASSID) {
1417: PetscFV fv = (PetscFV)obj;
1419: PetscCall(PetscFVGetQuadrature(fv, &quad));
1420: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1421: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1422: numComponents += Nc;
1423: }
1424: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1425: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1426: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1427: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1428: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1429: for (c = cStart; c < cEnd; ++c) {
1430: PetscScalar *x = NULL;
1431: PetscReal elemDiff = 0.0;
1432: PetscInt qc = 0;
1434: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1435: PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1437: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1438: PetscObject obj;
1439: PetscClassId id;
1440: void *const ctx = ctxs ? ctxs[field] : NULL;
1441: PetscInt Nb, Nc, q, fc;
1443: PetscCall(DMGetField(dm, field, NULL, &obj));
1444: PetscCall(PetscObjectGetClassId(obj, &id));
1445: if (id == PETSCFE_CLASSID) {
1446: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1447: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1448: } else if (id == PETSCFV_CLASSID) {
1449: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1450: Nb = 1;
1451: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1452: if (debug) {
1453: char title[1024];
1454: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1455: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1456: }
1457: for (q = 0; q < Nq; ++q) {
1458: PetscFEGeom qgeom;
1459: PetscErrorCode ierr;
1461: qgeom.dimEmbed = fegeom.dimEmbed;
1462: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1463: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1464: qgeom.detJ = &fegeom.detJ[q];
1465: 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);
1466: if (transform) {
1467: gcoords = &coords[coordDim * Nq];
1468: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1469: } else {
1470: gcoords = &coords[coordDim * q];
1471: }
1472: PetscCall(PetscArrayzero(funcVal, Nc));
1473: ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1474: if (ierr) {
1475: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1476: PetscCall(DMRestoreLocalVector(dm, &localX));
1477: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1478: }
1479: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1480: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1481: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1482: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1483: for (fc = 0; fc < Nc; ++fc) {
1484: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1485: if (debug)
1486: 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),
1487: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1488: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1489: }
1490: }
1491: fieldOffset += Nb;
1492: qc += Nc;
1493: }
1494: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1495: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1496: localDiff += elemDiff;
1497: }
1498: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1499: PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1500: *diff = PetscSqrtReal(*diff);
1501: PetscFunctionReturn(PETSC_SUCCESS);
1502: }
1504: 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)
1505: {
1506: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1507: DM tdm;
1508: PetscSection section;
1509: PetscQuadrature quad;
1510: Vec localX, tv;
1511: PetscScalar *funcVal, *interpolant;
1512: const PetscReal *quadWeights;
1513: PetscFEGeom fegeom;
1514: PetscReal *coords, *gcoords;
1515: PetscReal localDiff = 0.0;
1516: PetscInt dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1517: PetscBool transform;
1519: PetscFunctionBegin;
1520: PetscCall(DMGetDimension(dm, &dim));
1521: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1522: fegeom.dimEmbed = coordDim;
1523: PetscCall(DMGetLocalSection(dm, §ion));
1524: PetscCall(PetscSectionGetNumFields(section, &numFields));
1525: PetscCall(DMGetLocalVector(dm, &localX));
1526: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1527: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1528: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1529: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1530: PetscCall(DMHasBasisTransform(dm, &transform));
1531: for (field = 0; field < numFields; ++field) {
1532: PetscFE fe;
1533: PetscInt Nc;
1535: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1536: PetscCall(PetscFEGetQuadrature(fe, &quad));
1537: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1538: numComponents += Nc;
1539: }
1540: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1541: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1542: /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1543: PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1544: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1545: for (c = cStart; c < cEnd; ++c) {
1546: PetscScalar *x = NULL;
1547: PetscReal elemDiff = 0.0;
1548: PetscInt qc = 0;
1550: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1551: PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1553: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1554: PetscFE fe;
1555: void *const ctx = ctxs ? ctxs[field] : NULL;
1556: PetscInt Nb, Nc, q, fc;
1558: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1559: PetscCall(PetscFEGetDimension(fe, &Nb));
1560: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1561: if (debug) {
1562: char title[1024];
1563: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1564: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1565: }
1566: for (q = 0; q < Nq; ++q) {
1567: PetscFEGeom qgeom;
1568: PetscErrorCode ierr;
1570: qgeom.dimEmbed = fegeom.dimEmbed;
1571: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1572: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1573: qgeom.detJ = &fegeom.detJ[q];
1574: 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);
1575: if (transform) {
1576: gcoords = &coords[coordDim * Nq];
1577: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1578: } else {
1579: gcoords = &coords[coordDim * q];
1580: }
1581: PetscCall(PetscArrayzero(funcVal, Nc));
1582: ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1583: if (ierr) {
1584: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1585: PetscCall(DMRestoreLocalVector(dm, &localX));
1586: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1587: }
1588: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1589: PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1590: /* Overwrite with the dot product if the normal is given */
1591: if (n) {
1592: for (fc = 0; fc < Nc; ++fc) {
1593: PetscScalar sum = 0.0;
1594: PetscInt d;
1595: for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1596: interpolant[fc] = sum;
1597: }
1598: }
1599: for (fc = 0; fc < Nc; ++fc) {
1600: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1601: 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])));
1602: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1603: }
1604: }
1605: fieldOffset += Nb;
1606: qc += Nc;
1607: }
1608: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1609: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1610: localDiff += elemDiff;
1611: }
1612: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1613: PetscCall(DMRestoreLocalVector(dm, &localX));
1614: PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1615: *diff = PetscSqrtReal(*diff);
1616: PetscFunctionReturn(PETSC_SUCCESS);
1617: }
1619: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1620: {
1621: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1622: DM tdm;
1623: DMLabel depthLabel;
1624: PetscSection section;
1625: Vec localX, tv;
1626: PetscReal *localDiff;
1627: PetscInt dim, depth, dE, Nf, f, Nds, s;
1628: PetscBool transform;
1629: PetscMPIInt Nfi;
1631: PetscFunctionBegin;
1632: PetscCall(DMGetDimension(dm, &dim));
1633: PetscCall(DMGetCoordinateDim(dm, &dE));
1634: PetscCall(DMGetLocalSection(dm, §ion));
1635: PetscCall(DMGetLocalVector(dm, &localX));
1636: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1637: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1638: PetscCall(DMHasBasisTransform(dm, &transform));
1639: PetscCall(DMGetNumFields(dm, &Nf));
1640: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1641: PetscCall(DMLabelGetNumValues(depthLabel, &depth));
1643: PetscCall(VecSet(localX, 0.0));
1644: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1645: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1646: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1647: PetscCall(DMGetNumDS(dm, &Nds));
1648: PetscCall(PetscCalloc1(Nf, &localDiff));
1649: for (s = 0; s < Nds; ++s) {
1650: PetscDS ds;
1651: DMLabel label;
1652: IS fieldIS, pointIS;
1653: const PetscInt *fields, *points = NULL;
1654: PetscQuadrature quad;
1655: const PetscReal *quadPoints, *quadWeights;
1656: PetscFEGeom fegeom;
1657: PetscReal *coords, *gcoords;
1658: PetscScalar *funcVal, *interpolant;
1659: PetscBool isCohesive;
1660: PetscInt qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;
1662: PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1663: PetscCall(ISGetIndices(fieldIS, &fields));
1664: PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1665: PetscCall(PetscDSGetNumFields(ds, &dsNf));
1666: PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1667: PetscCall(PetscDSGetQuadrature(ds, &quad));
1668: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1669: PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1670: PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1671: if (!label) {
1672: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1673: } else {
1674: PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1675: PetscCall(ISGetLocalSize(pointIS, &cEnd));
1676: PetscCall(ISGetIndices(pointIS, &points));
1677: }
1678: for (c = cStart; c < cEnd; ++c) {
1679: const PetscInt cell = points ? points[c] : c;
1680: PetscScalar *x = NULL;
1681: const PetscInt *cone;
1682: PetscInt qc = 0, fOff = 0, dep;
1684: PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1685: if (dep != depth - 1) continue;
1686: if (isCohesive) {
1687: PetscCall(DMPlexGetCone(dm, cell, &cone));
1688: PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1689: } else {
1690: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1691: }
1692: PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x));
1693: for (f = 0; f < dsNf; ++f) {
1694: PetscObject obj;
1695: PetscClassId id;
1696: void *const ctx = ctxs ? ctxs[fields[f]] : NULL;
1697: PetscInt Nb, Nc, q, fc;
1698: PetscReal elemDiff = 0.0;
1699: PetscBool cohesive;
1701: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1702: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1703: PetscCall(PetscObjectGetClassId(obj, &id));
1704: if (id == PETSCFE_CLASSID) {
1705: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1706: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1707: } else if (id == PETSCFV_CLASSID) {
1708: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1709: Nb = 1;
1710: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1711: if (isCohesive && !cohesive) {
1712: fOff += Nb * 2;
1713: qc += Nc;
1714: continue;
1715: }
1716: if (debug) {
1717: char title[1024];
1718: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1719: PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1720: }
1721: for (q = 0; q < Nq; ++q) {
1722: PetscFEGeom qgeom;
1723: PetscErrorCode ierr;
1725: qgeom.dimEmbed = fegeom.dimEmbed;
1726: qgeom.J = &fegeom.J[q * dE * dE];
1727: qgeom.invJ = &fegeom.invJ[q * dE * dE];
1728: qgeom.detJ = &fegeom.detJ[q];
1729: 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);
1730: if (transform) {
1731: gcoords = &coords[dE * Nq];
1732: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1733: } else {
1734: gcoords = &coords[dE * q];
1735: }
1736: for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1737: ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1738: if (ierr) {
1739: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1740: PetscCall(DMRestoreLocalVector(dm, &localX));
1741: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1742: }
1743: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1744: /* Call once for each face, except for lagrange field */
1745: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1746: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1747: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1748: for (fc = 0; fc < Nc; ++fc) {
1749: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1750: if (debug)
1751: 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),
1752: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1753: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1754: }
1755: }
1756: fOff += Nb;
1757: qc += Nc;
1758: localDiff[fields[f]] += elemDiff;
1759: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1760: }
1761: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1762: }
1763: if (label) {
1764: PetscCall(ISRestoreIndices(pointIS, &points));
1765: PetscCall(ISDestroy(&pointIS));
1766: }
1767: PetscCall(ISRestoreIndices(fieldIS, &fields));
1768: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1769: }
1770: PetscCall(DMRestoreLocalVector(dm, &localX));
1771: PetscCall(PetscMPIIntCast(Nf, &Nfi));
1772: PetscCallMPI(MPIU_Allreduce(localDiff, diff, Nfi, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1773: PetscCall(PetscFree(localDiff));
1774: for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1775: PetscFunctionReturn(PETSC_SUCCESS);
1776: }
1778: /*@C
1779: 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.
1781: Collective
1783: Input Parameters:
1784: + dm - The `DM`
1785: . time - The time
1786: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1787: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1788: - X - The coefficient vector u_h
1790: Output Parameter:
1791: . D - A `Vec` which holds the difference ||u - u_h||_2 for each cell
1793: Level: developer
1795: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1796: @*/
1797: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1798: {
1799: PetscSection section;
1800: PetscQuadrature quad;
1801: Vec localX;
1802: PetscFEGeom fegeom;
1803: PetscScalar *funcVal, *interpolant;
1804: PetscReal *coords;
1805: const PetscReal *quadPoints, *quadWeights;
1806: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1808: PetscFunctionBegin;
1809: PetscCall(VecSet(D, 0.0));
1810: PetscCall(DMGetDimension(dm, &dim));
1811: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1812: PetscCall(DMGetLocalSection(dm, §ion));
1813: PetscCall(PetscSectionGetNumFields(section, &numFields));
1814: PetscCall(DMGetLocalVector(dm, &localX));
1815: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1816: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1817: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1818: for (field = 0; field < numFields; ++field) {
1819: PetscObject obj;
1820: PetscClassId id;
1821: PetscInt Nc;
1823: PetscCall(DMGetField(dm, field, NULL, &obj));
1824: PetscCall(PetscObjectGetClassId(obj, &id));
1825: if (id == PETSCFE_CLASSID) {
1826: PetscFE fe = (PetscFE)obj;
1828: PetscCall(PetscFEGetQuadrature(fe, &quad));
1829: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1830: } else if (id == PETSCFV_CLASSID) {
1831: PetscFV fv = (PetscFV)obj;
1833: PetscCall(PetscFVGetQuadrature(fv, &quad));
1834: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1835: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1836: numComponents += Nc;
1837: }
1838: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1839: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1840: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1841: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1842: for (c = cStart; c < cEnd; ++c) {
1843: PetscScalar *x = NULL;
1844: PetscScalar elemDiff = 0.0;
1845: PetscInt qc = 0;
1847: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1848: PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));
1850: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1851: PetscObject obj;
1852: PetscClassId id;
1853: void *const ctx = ctxs ? ctxs[field] : NULL;
1854: PetscInt Nb, Nc, q, fc;
1856: PetscCall(DMGetField(dm, field, NULL, &obj));
1857: PetscCall(PetscObjectGetClassId(obj, &id));
1858: if (id == PETSCFE_CLASSID) {
1859: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1860: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1861: } else if (id == PETSCFV_CLASSID) {
1862: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1863: Nb = 1;
1864: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1865: if (funcs[field]) {
1866: for (q = 0; q < Nq; ++q) {
1867: PetscFEGeom qgeom;
1869: qgeom.dimEmbed = fegeom.dimEmbed;
1870: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1871: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1872: qgeom.detJ = &fegeom.detJ[q];
1873: 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);
1874: PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1875: #if defined(needs_fix_with_return_code_argument)
1876: if (ierr) {
1877: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1878: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1879: PetscCall(DMRestoreLocalVector(dm, &localX));
1880: }
1881: #endif
1882: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1883: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1884: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1885: for (fc = 0; fc < Nc; ++fc) {
1886: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1887: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1888: }
1889: }
1890: }
1891: fieldOffset += Nb;
1892: qc += Nc;
1893: }
1894: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1895: PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1896: }
1897: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1898: PetscCall(DMRestoreLocalVector(dm, &localX));
1899: PetscCall(VecSqrtAbs(D));
1900: PetscFunctionReturn(PETSC_SUCCESS);
1901: }
1903: /*@
1904: DMPlexComputeL2FluxDiffVecLocal - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`
1906: Collective
1908: Input Parameters:
1909: + lu - The local `Vec` containing the primal solution
1910: . f - The field number for the potential
1911: . lmu - The local `Vec` containing the mixed solution
1912: - mf - The field number for the flux
1914: Output Parameter:
1915: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$
1917: Level: advanced
1919: Notes:
1920: We assume that the `DM` for each solution has the same topology, geometry, and quadrature.
1922: This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.
1924: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVec()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1925: @*/
1926: PetscErrorCode DMPlexComputeL2FluxDiffVecLocal(Vec lu, PetscInt f, Vec lmu, PetscInt mf, Vec eFlux)
1927: {
1928: DM dm, mdm, edm;
1929: PetscFE fe, mfe;
1930: PetscFEGeom fegeom;
1931: PetscQuadrature quad;
1932: const PetscReal *quadWeights;
1933: PetscReal *coords;
1934: PetscScalar *interpolant, *minterpolant, *earray;
1935: PetscInt cdim, mcdim, cStart, cEnd, Nc, mNc, qNc, Nq;
1936: MPI_Comm comm;
1938: PetscFunctionBegin;
1939: PetscCall(VecGetDM(lu, &dm));
1940: PetscCall(VecGetDM(lmu, &mdm));
1941: PetscCall(VecGetDM(eFlux, &edm));
1942: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1943: PetscCall(VecSet(eFlux, 0.0));
1945: // Check if the both problems are on the same mesh
1946: PetscCall(DMGetCoordinateDim(dm, &cdim));
1947: PetscCall(DMGetCoordinateDim(mdm, &mcdim));
1948: PetscCheck(cdim == mcdim, comm, PETSC_ERR_ARG_SIZ, "primal coordinate Dim %" PetscInt_FMT " != %" PetscInt_FMT " mixed coordinate Dim", cdim, mcdim);
1949: fegeom.dimEmbed = cdim;
1951: PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
1952: PetscCall(DMGetField(mdm, mf, NULL, (PetscObject *)&mfe));
1953: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1954: PetscCall(PetscFEGetNumComponents(mfe, &mNc));
1955: PetscCall(PetscFEGetQuadrature(fe, &quad));
1956: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1957: PetscCheck(qNc == 1 || qNc == mNc, comm, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, mNc);
1959: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1960: PetscCall(VecGetArrayWrite(eFlux, &earray));
1961: 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));
1962: for (PetscInt c = cStart; c < cEnd; ++c) {
1963: PetscScalar *x = NULL;
1964: PetscScalar *mx = NULL;
1965: PetscScalar *eval = NULL;
1966: PetscReal fluxElemDiff = 0.0;
1968: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1969: PetscCall(DMPlexVecGetClosure(dm, NULL, lu, c, NULL, &x));
1970: PetscCall(DMPlexVecGetClosure(mdm, NULL, lmu, c, NULL, &mx));
1972: for (PetscInt q = 0; q < Nq; ++q) {
1973: PetscFEGeom qgeom;
1975: qgeom.dimEmbed = fegeom.dimEmbed;
1976: qgeom.J = &fegeom.J[q * cdim * cdim];
1977: qgeom.invJ = &fegeom.invJ[q * cdim * cdim];
1978: qgeom.detJ = &fegeom.detJ[q];
1980: 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);
1982: PetscCall(PetscFEInterpolate_Static(mfe, &mx[0], &qgeom, q, minterpolant));
1983: PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[0], &qgeom, q, interpolant));
1985: /* Now take the elementwise difference and store that in a vector. */
1986: for (PetscInt fc = 0; fc < mNc; ++fc) {
1987: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : fc)];
1988: fluxElemDiff += PetscSqr(PetscRealPart(interpolant[fc] - minterpolant[fc])) * wt * fegeom.detJ[q];
1989: }
1990: }
1991: PetscCall(DMPlexVecRestoreClosure(dm, NULL, lu, c, NULL, &x));
1992: PetscCall(DMPlexVecRestoreClosure(mdm, NULL, lmu, c, NULL, &mx));
1993: PetscCall(DMPlexPointGlobalRef(edm, c, earray, (void *)&eval));
1994: if (eval) eval[0] = fluxElemDiff;
1995: }
1996: PetscCall(PetscFree6(interpolant, minterpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1997: PetscCall(VecRestoreArrayWrite(eFlux, &earray));
1999: PetscCall(VecAssemblyBegin(eFlux));
2000: PetscCall(VecAssemblyEnd(eFlux));
2001: PetscCall(VecSqrtAbs(eFlux));
2002: PetscFunctionReturn(PETSC_SUCCESS);
2003: }
2005: /*@
2006: DMPlexComputeL2FluxDiffVec - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`
2008: Collective
2010: Input Parameters:
2011: + u - The global `Vec` containing the primal solution
2012: . f - The field number for the potential
2013: . mu - The global `Vec` containing the mixed solution
2014: - mf - The field number for the flux
2016: Output Parameter:
2017: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$
2019: Level: advanced
2021: Notes:
2022: We assume that the `DM` for each solution has the same topology, geometry, and quadrature.
2024: This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.
2026: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2027: @*/
2028: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
2029: {
2030: DM dm, mdm;
2031: Vec lu, lmu;
2033: PetscFunctionBegin;
2034: PetscCall(VecGetDM(u, &dm));
2035: PetscCall(DMGetLocalVector(dm, &lu));
2036: PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
2037: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));
2039: PetscCall(VecGetDM(mu, &mdm));
2040: PetscCall(DMGetLocalVector(mdm, &lmu));
2041: PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
2042: PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));
2044: PetscCall(DMPlexComputeL2FluxDiffVecLocal(lu, f, lmu, mf, eFlux));
2046: PetscCall(DMRestoreLocalVector(dm, &lu));
2047: PetscCall(DMRestoreLocalVector(mdm, &lmu));
2048: PetscFunctionReturn(PETSC_SUCCESS);
2049: }
2051: /*@
2052: DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1
2054: Collective
2056: Input Parameters:
2057: + dm - The `DM`
2058: - locX - The coefficient vector u_h
2060: Output Parameter:
2061: . locC - A `Vec` which holds the Clement interpolant of the function
2063: Level: developer
2065: Note:
2066: $ 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
2068: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2069: @*/
2070: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
2071: {
2072: PetscInt debug = ((DM_Plex *)dm->data)->printFEM;
2073: DM dmc;
2074: PetscQuadrature quad;
2075: PetscScalar *interpolant, *valsum;
2076: PetscFEGeom fegeom;
2077: PetscReal *coords;
2078: const PetscReal *quadPoints, *quadWeights;
2079: PetscInt dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;
2081: PetscFunctionBegin;
2082: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2083: PetscCall(VecGetDM(locC, &dmc));
2084: PetscCall(VecSet(locC, 0.0));
2085: PetscCall(DMGetDimension(dm, &dim));
2086: PetscCall(DMGetCoordinateDim(dm, &cdim));
2087: fegeom.dimEmbed = cdim;
2088: PetscCall(DMGetNumFields(dm, &Nf));
2089: PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2090: for (f = 0; f < Nf; ++f) {
2091: PetscObject obj;
2092: PetscClassId id;
2093: PetscInt fNc;
2095: PetscCall(DMGetField(dm, f, NULL, &obj));
2096: PetscCall(PetscObjectGetClassId(obj, &id));
2097: if (id == PETSCFE_CLASSID) {
2098: PetscFE fe = (PetscFE)obj;
2100: PetscCall(PetscFEGetQuadrature(fe, &quad));
2101: PetscCall(PetscFEGetNumComponents(fe, &fNc));
2102: } else if (id == PETSCFV_CLASSID) {
2103: PetscFV fv = (PetscFV)obj;
2105: PetscCall(PetscFVGetQuadrature(fv, &quad));
2106: PetscCall(PetscFVGetNumComponents(fv, &fNc));
2107: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2108: Nc += fNc;
2109: }
2110: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2111: PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
2112: PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
2113: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2114: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2115: for (v = vStart; v < vEnd; ++v) {
2116: PetscScalar volsum = 0.0;
2117: PetscInt *star = NULL;
2118: PetscInt starSize, st, fc;
2120: PetscCall(PetscArrayzero(valsum, Nc));
2121: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2122: for (st = 0; st < starSize * 2; st += 2) {
2123: const PetscInt cell = star[st];
2124: PetscScalar *val = &valsum[Nc];
2125: PetscScalar *x = NULL;
2126: PetscReal vol = 0.0;
2127: PetscInt foff = 0;
2129: if ((cell < cStart) || (cell >= cEnd)) continue;
2130: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2131: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2132: for (f = 0; f < Nf; ++f) {
2133: PetscObject obj;
2134: PetscClassId id;
2135: PetscInt Nb, fNc, q;
2137: PetscCall(PetscArrayzero(val, Nc));
2138: PetscCall(DMGetField(dm, f, NULL, &obj));
2139: PetscCall(PetscObjectGetClassId(obj, &id));
2140: if (id == PETSCFE_CLASSID) {
2141: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
2142: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2143: } else if (id == PETSCFV_CLASSID) {
2144: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
2145: Nb = 1;
2146: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2147: for (q = 0; q < Nq; ++q) {
2148: const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
2149: PetscFEGeom qgeom;
2151: qgeom.dimEmbed = fegeom.dimEmbed;
2152: qgeom.J = &fegeom.J[q * cdim * cdim];
2153: qgeom.invJ = &fegeom.invJ[q * cdim * cdim];
2154: qgeom.detJ = &fegeom.detJ[q];
2155: 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);
2156: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
2157: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2158: for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
2159: vol += wt;
2160: }
2161: foff += Nb;
2162: }
2163: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2164: for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
2165: volsum += vol;
2166: if (debug) {
2167: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
2168: for (fc = 0; fc < Nc; ++fc) {
2169: if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2170: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
2171: }
2172: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2173: }
2174: }
2175: for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
2176: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2177: PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
2178: }
2179: PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2180: PetscFunctionReturn(PETSC_SUCCESS);
2181: }
2183: /*@
2184: DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1
2186: Collective
2188: Input Parameters:
2189: + dm - The `DM`
2190: - locX - The coefficient vector u_h
2192: Output Parameter:
2193: . locC - A `Vec` which holds the Clement interpolant of the gradient
2195: Level: developer
2197: Note:
2198: $\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
2200: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2201: @*/
2202: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
2203: {
2204: DM_Plex *mesh = (DM_Plex *)dm->data;
2205: PetscInt debug = mesh->printFEM;
2206: DM dmC;
2207: PetscQuadrature quad;
2208: PetscScalar *interpolant, *gradsum;
2209: PetscFEGeom fegeom;
2210: PetscReal *coords;
2211: const PetscReal *quadPoints, *quadWeights;
2212: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
2214: PetscFunctionBegin;
2215: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2216: PetscCall(VecGetDM(locC, &dmC));
2217: PetscCall(VecSet(locC, 0.0));
2218: PetscCall(DMGetDimension(dm, &dim));
2219: PetscCall(DMGetCoordinateDim(dm, &coordDim));
2220: fegeom.dimEmbed = coordDim;
2221: PetscCall(DMGetNumFields(dm, &numFields));
2222: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2223: for (field = 0; field < numFields; ++field) {
2224: PetscObject obj;
2225: PetscClassId id;
2226: PetscInt Nc;
2228: PetscCall(DMGetField(dm, field, NULL, &obj));
2229: PetscCall(PetscObjectGetClassId(obj, &id));
2230: if (id == PETSCFE_CLASSID) {
2231: PetscFE fe = (PetscFE)obj;
2233: PetscCall(PetscFEGetQuadrature(fe, &quad));
2234: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2235: } else if (id == PETSCFV_CLASSID) {
2236: PetscFV fv = (PetscFV)obj;
2238: PetscCall(PetscFVGetQuadrature(fv, &quad));
2239: PetscCall(PetscFVGetNumComponents(fv, &Nc));
2240: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2241: numComponents += Nc;
2242: }
2243: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2244: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
2245: 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));
2246: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2247: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2248: for (v = vStart; v < vEnd; ++v) {
2249: PetscScalar volsum = 0.0;
2250: PetscInt *star = NULL;
2251: PetscInt starSize, st, d, fc;
2253: PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
2254: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2255: for (st = 0; st < starSize * 2; st += 2) {
2256: const PetscInt cell = star[st];
2257: PetscScalar *grad = &gradsum[coordDim * numComponents];
2258: PetscScalar *x = NULL;
2259: PetscReal vol = 0.0;
2261: if ((cell < cStart) || (cell >= cEnd)) continue;
2262: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2263: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2264: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
2265: PetscObject obj;
2266: PetscClassId id;
2267: PetscInt Nb, Nc, q, qc = 0;
2269: PetscCall(PetscArrayzero(grad, coordDim * numComponents));
2270: PetscCall(DMGetField(dm, field, NULL, &obj));
2271: PetscCall(PetscObjectGetClassId(obj, &id));
2272: if (id == PETSCFE_CLASSID) {
2273: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
2274: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2275: } else if (id == PETSCFV_CLASSID) {
2276: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
2277: Nb = 1;
2278: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2279: for (q = 0; q < Nq; ++q) {
2280: PetscFEGeom qgeom;
2282: qgeom.dimEmbed = fegeom.dimEmbed;
2283: qgeom.J = &fegeom.J[q * coordDim * coordDim];
2284: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
2285: qgeom.detJ = &fegeom.detJ[q];
2286: 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);
2287: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
2288: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2289: for (fc = 0; fc < Nc; ++fc) {
2290: const PetscReal wt = quadWeights[q * qNc + qc];
2292: for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
2293: }
2294: vol += quadWeights[q * qNc] * fegeom.detJ[q];
2295: }
2296: fieldOffset += Nb;
2297: qc += Nc;
2298: }
2299: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2300: for (fc = 0; fc < numComponents; ++fc) {
2301: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
2302: }
2303: volsum += vol;
2304: if (debug) {
2305: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
2306: for (fc = 0; fc < numComponents; ++fc) {
2307: for (d = 0; d < coordDim; ++d) {
2308: if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2309: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
2310: }
2311: }
2312: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2313: }
2314: }
2315: for (fc = 0; fc < numComponents; ++fc) {
2316: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2317: }
2318: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2319: PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2320: }
2321: PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2322: PetscFunctionReturn(PETSC_SUCCESS);
2323: }
2325: PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec locX, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
2326: {
2327: DM dmAux = NULL, plexA = NULL;
2328: PetscDS prob, probAux = NULL;
2329: PetscSection section, sectionAux;
2330: Vec locA;
2331: PetscInt dim, numCells = cEnd - cStart, c, f;
2332: PetscBool useFVM = PETSC_FALSE;
2333: /* DS */
2334: PetscInt Nf, totDim, *uOff, *uOff_x, numConstants;
2335: PetscInt NfAux, totDimAux, *aOff;
2336: PetscScalar *u, *a = NULL;
2337: const PetscScalar *constants;
2338: /* Geometry */
2339: PetscFEGeom *cgeomFEM;
2340: DM dmGrad;
2341: PetscQuadrature affineQuad = NULL;
2342: Vec cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2343: PetscFVCellGeom *cgeomFVM;
2344: const PetscScalar *lgrad;
2345: PetscInt maxDegree;
2346: DMField coordField;
2347: IS cellIS;
2349: PetscFunctionBegin;
2350: PetscCall(DMGetDS(dm, &prob));
2351: PetscCall(DMGetDimension(dm, &dim));
2352: PetscCall(DMGetLocalSection(dm, §ion));
2353: PetscCall(DMGetNumFields(dm, &Nf));
2354: /* Determine which discretizations we have */
2355: for (f = 0; f < Nf; ++f) {
2356: PetscObject obj;
2357: PetscClassId id;
2359: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2360: PetscCall(PetscObjectGetClassId(obj, &id));
2361: if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2362: }
2363: /* Read DS information */
2364: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2365: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2366: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2367: PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2368: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2369: /* Read Auxiliary DS information */
2370: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2371: if (locA) {
2372: PetscCall(VecGetDM(locA, &dmAux));
2373: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2374: PetscCall(DMGetDS(dmAux, &probAux));
2375: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2376: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2377: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2378: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2379: }
2380: /* Allocate data arrays */
2381: PetscCall(PetscCalloc1(numCells * totDim, &u));
2382: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2383: /* Read out geometry */
2384: PetscCall(DMGetCoordinateField(dm, &coordField));
2385: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2386: if (maxDegree <= 1) {
2387: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2388: if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM));
2389: }
2390: if (useFVM) {
2391: PetscFV fv = NULL;
2392: Vec grad;
2393: PetscInt fStart, fEnd;
2394: PetscBool compGrad;
2396: for (f = 0; f < Nf; ++f) {
2397: PetscObject obj;
2398: PetscClassId id;
2400: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2401: PetscCall(PetscObjectGetClassId(obj, &id));
2402: if (id == PETSCFV_CLASSID) {
2403: fv = (PetscFV)obj;
2404: break;
2405: }
2406: }
2407: PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2408: PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2409: PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2410: PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2411: PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2412: PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2413: /* Reconstruct and limit cell gradients */
2414: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2415: PetscCall(DMGetGlobalVector(dmGrad, &grad));
2416: PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2417: /* Communicate gradient values */
2418: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2419: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2420: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2421: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2422: /* Handle non-essential (e.g. outflow) boundary values */
2423: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2424: PetscCall(VecGetArrayRead(locGrad, &lgrad));
2425: }
2426: /* Read out data from inputs */
2427: for (c = cStart; c < cEnd; ++c) {
2428: PetscScalar *x = NULL;
2429: PetscInt i;
2431: PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2432: for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2433: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2434: if (dmAux) {
2435: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, c, NULL, &x));
2436: for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2437: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, c, NULL, &x));
2438: }
2439: }
2440: /* Do integration for each field */
2441: for (f = 0; f < Nf; ++f) {
2442: PetscObject obj;
2443: PetscClassId id;
2444: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
2446: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2447: PetscCall(PetscObjectGetClassId(obj, &id));
2448: if (id == PETSCFE_CLASSID) {
2449: PetscFE fe = (PetscFE)obj;
2450: PetscQuadrature q;
2451: PetscFEGeom *chunkGeom = NULL;
2452: PetscInt Nq, Nb;
2454: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2455: PetscCall(PetscFEGetQuadrature(fe, &q));
2456: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2457: PetscCall(PetscFEGetDimension(fe, &Nb));
2458: blockSize = Nb * Nq;
2459: batchSize = numBlocks * blockSize;
2460: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2461: numChunks = numCells / (numBatches * batchSize);
2462: Ne = numChunks * numBatches * batchSize;
2463: Nr = numCells % (numBatches * batchSize);
2464: offset = numCells - Nr;
2465: if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM));
2466: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2467: PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2468: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2469: PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &cintegral[offset * Nf]));
2470: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2471: if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2472: } else if (id == PETSCFV_CLASSID) {
2473: PetscInt foff;
2474: PetscPointFunc obj_func;
2476: PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2477: PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2478: if (obj_func) {
2479: for (c = 0; c < numCells; ++c) {
2480: PetscScalar *u_x;
2481: PetscScalar lint = 0.;
2483: PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2484: 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);
2485: cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2486: }
2487: }
2488: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2489: }
2490: /* Cleanup data arrays */
2491: if (useFVM) {
2492: PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2493: PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2494: PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2495: PetscCall(VecDestroy(&faceGeometryFVM));
2496: PetscCall(VecDestroy(&cellGeometryFVM));
2497: PetscCall(DMDestroy(&dmGrad));
2498: }
2499: if (dmAux) PetscCall(PetscFree(a));
2500: PetscCall(DMDestroy(&plexA));
2501: PetscCall(PetscFree(u));
2502: /* Cleanup */
2503: if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2504: PetscCall(PetscQuadratureDestroy(&affineQuad));
2505: PetscCall(ISDestroy(&cellIS));
2506: PetscFunctionReturn(PETSC_SUCCESS);
2507: }
2509: /*@
2510: DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user
2512: Input Parameters:
2513: + dm - The mesh
2514: . X - Global input vector
2515: - user - The user context
2517: Output Parameter:
2518: . integral - Integral for each field
2520: Level: developer
2522: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2523: @*/
2524: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2525: {
2526: PetscInt printFEM;
2527: PetscScalar *cintegral, *lintegral;
2528: PetscInt Nf, f, cellHeight, cStart, cEnd, cell;
2529: Vec locX;
2530: PetscMPIInt Nfi;
2532: PetscFunctionBegin;
2535: PetscAssertPointer(integral, 3);
2536: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2537: PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2538: PetscCall(DMGetNumFields(dm, &Nf));
2539: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2540: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2541: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2542: PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2543: /* Get local solution with boundary values */
2544: PetscCall(DMGetLocalVector(dm, &locX));
2545: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2546: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2547: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2548: PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user));
2549: PetscCall(DMRestoreLocalVector(dm, &locX));
2550: printFEM = ((DM_Plex *)dm->data)->printFEM;
2551: /* Sum up values */
2552: for (cell = cStart; cell < cEnd; ++cell) {
2553: const PetscInt c = cell - cStart;
2555: if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2556: for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2557: }
2558: PetscCall(PetscMPIIntCast(Nf, &Nfi));
2559: PetscCallMPI(MPIU_Allreduce(lintegral, integral, Nfi, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2560: if (printFEM) {
2561: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2562: for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2563: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2564: }
2565: PetscCall(PetscFree2(lintegral, cintegral));
2566: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2567: PetscCall(DMDestroy(&dm));
2568: PetscFunctionReturn(PETSC_SUCCESS);
2569: }
2571: /*@
2572: DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user
2574: Input Parameters:
2575: + dm - The mesh
2576: . X - Global input vector
2577: - user - The user context
2579: Output Parameter:
2580: . F - Cellwise integrals for each field
2582: Level: developer
2584: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2585: @*/
2586: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2587: {
2588: PetscInt printFEM;
2589: DM dmF;
2590: PetscSection sectionF = NULL;
2591: PetscScalar *cintegral, *af;
2592: PetscInt Nf, f, cellHeight, cStart, cEnd, cell, n;
2593: Vec locX;
2595: PetscFunctionBegin;
2599: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2600: PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
2601: PetscCall(DMGetNumFields(dm, &Nf));
2602: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2603: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2604: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2605: PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2606: /* Get local solution with boundary values */
2607: PetscCall(DMGetLocalVector(dm, &locX));
2608: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2609: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2610: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2611: PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user));
2612: PetscCall(DMRestoreLocalVector(dm, &locX));
2613: /* Put values in F */
2614: PetscCall(VecGetArray(F, &af));
2615: PetscCall(VecGetDM(F, &dmF));
2616: if (dmF) PetscCall(DMGetLocalSection(dmF, §ionF));
2617: PetscCall(VecGetLocalSize(F, &n));
2618: PetscCheck(n >= (cEnd - cStart) * Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Vector size %" PetscInt_FMT " < %" PetscInt_FMT, n, (cEnd - cStart) * Nf);
2619: printFEM = ((DM_Plex *)dm->data)->printFEM;
2620: for (cell = cStart; cell < cEnd; ++cell) {
2621: const PetscInt c = cell - cStart;
2622: PetscInt dof = Nf, off = c * Nf;
2624: if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2625: if (sectionF) {
2626: PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2627: PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2628: }
2629: PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2630: for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2631: }
2632: PetscCall(VecRestoreArray(F, &af));
2633: PetscCall(PetscFree(cintegral));
2634: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2635: PetscCall(DMDestroy(&dm));
2636: PetscFunctionReturn(PETSC_SUCCESS);
2637: }
2639: 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, void *user)
2640: {
2641: DM plex = NULL, plexA = NULL;
2642: DMEnclosureType encAux;
2643: PetscDS prob, probAux = NULL;
2644: PetscSection section, sectionAux = NULL;
2645: Vec locA = NULL;
2646: DMField coordField;
2647: PetscInt Nf, totDim, *uOff, *uOff_x;
2648: PetscInt NfAux = 0, totDimAux = 0, *aOff = NULL;
2649: PetscScalar *u, *a = NULL;
2650: const PetscScalar *constants;
2651: PetscInt numConstants, f;
2653: PetscFunctionBegin;
2654: PetscCall(DMGetCoordinateField(dm, &coordField));
2655: PetscCall(DMConvert(dm, DMPLEX, &plex));
2656: PetscCall(DMGetDS(dm, &prob));
2657: PetscCall(DMGetLocalSection(dm, §ion));
2658: PetscCall(PetscSectionGetNumFields(section, &Nf));
2659: /* Determine which discretizations we have */
2660: for (f = 0; f < Nf; ++f) {
2661: PetscObject obj;
2662: PetscClassId id;
2664: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2665: PetscCall(PetscObjectGetClassId(obj, &id));
2666: PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2667: }
2668: /* Read DS information */
2669: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2670: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2671: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2672: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2673: /* Read Auxiliary DS information */
2674: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2675: if (locA) {
2676: DM dmAux;
2678: PetscCall(VecGetDM(locA, &dmAux));
2679: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2680: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2681: PetscCall(DMGetDS(dmAux, &probAux));
2682: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2683: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2684: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2685: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2686: }
2687: /* Integrate over points */
2688: {
2689: PetscFEGeom *fgeom, *chunkGeom = NULL;
2690: PetscInt maxDegree;
2691: PetscQuadrature qGeom = NULL;
2692: const PetscInt *points;
2693: PetscInt numFaces, face, Nq, field;
2694: PetscInt numChunks, chunkSize, chunk, Nr, offset;
2696: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2697: PetscCall(ISGetIndices(pointIS, &points));
2698: PetscCall(PetscCalloc2(numFaces * totDim, &u, (locA ? (size_t)numFaces * totDimAux : 0), &a));
2699: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2700: for (face = 0; face < numFaces; ++face) {
2701: const PetscInt point = points[face], *support;
2702: PetscScalar *x = NULL;
2704: PetscCall(DMPlexGetSupport(dm, point, &support));
2705: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2706: for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2707: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2708: if (locA) {
2709: PetscInt subp;
2710: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2711: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2712: for (PetscInt i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2713: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2714: }
2715: }
2716: for (field = 0; field < Nf; ++field) {
2717: PetscFE fe;
2719: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2720: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2721: if (!qGeom) {
2722: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2723: PetscCall(PetscObjectReference((PetscObject)qGeom));
2724: }
2725: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2726: PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2727: /* Get blocking */
2728: {
2729: PetscQuadrature q;
2730: PetscInt numBatches, batchSize, numBlocks, blockSize;
2731: PetscInt Nq, Nb;
2733: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2734: PetscCall(PetscFEGetQuadrature(fe, &q));
2735: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2736: PetscCall(PetscFEGetDimension(fe, &Nb));
2737: blockSize = Nb * Nq;
2738: batchSize = numBlocks * blockSize;
2739: chunkSize = numBatches * batchSize;
2740: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2741: numChunks = numFaces / chunkSize;
2742: Nr = numFaces % chunkSize;
2743: offset = numFaces - Nr;
2744: }
2745: /* Do integration for each field */
2746: for (chunk = 0; chunk < numChunks; ++chunk) {
2747: PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2748: PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], chunkSize, chunkGeom, &u[chunk * chunkSize * totDim], probAux, PetscSafePointerPlusOffset(a, chunk * chunkSize * totDimAux), &fintegral[chunk * chunkSize * Nf]));
2749: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2750: }
2751: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2752: PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf]));
2753: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2754: /* Cleanup data arrays */
2755: PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2756: PetscCall(PetscQuadratureDestroy(&qGeom));
2757: }
2758: PetscCall(PetscFree2(u, a));
2759: PetscCall(ISRestoreIndices(pointIS, &points));
2760: }
2761: if (plex) PetscCall(DMDestroy(&plex));
2762: if (plexA) PetscCall(DMDestroy(&plexA));
2763: PetscFunctionReturn(PETSC_SUCCESS);
2764: }
2766: /*@C
2767: DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2769: Input Parameters:
2770: + dm - The mesh
2771: . X - Global input vector
2772: . label - The boundary `DMLabel`
2773: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2774: . vals - The label values to use, or NULL for all values
2775: . funcs - The functions to integrate along the boundary for each field
2776: - user - The user context
2778: Output Parameter:
2779: . integral - Integral for each field
2781: Level: developer
2783: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2784: @*/
2785: 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, void *user)
2786: {
2787: Vec locX;
2788: PetscSection section;
2789: DMLabel depthLabel;
2790: IS facetIS;
2791: PetscInt dim, Nf, f, v;
2793: PetscFunctionBegin;
2797: if (vals) PetscAssertPointer(vals, 5);
2798: PetscAssertPointer(integral, 7);
2799: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2800: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2801: PetscCall(DMGetDimension(dm, &dim));
2802: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2803: PetscCall(DMGetLocalSection(dm, §ion));
2804: PetscCall(PetscSectionGetNumFields(section, &Nf));
2805: /* Get local solution with boundary values */
2806: PetscCall(DMGetLocalVector(dm, &locX));
2807: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2808: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2809: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2810: /* Loop over label values */
2811: PetscCall(PetscArrayzero(integral, Nf));
2812: for (v = 0; v < numVals; ++v) {
2813: IS pointIS;
2814: PetscInt numFaces, face;
2815: PetscScalar *fintegral;
2817: PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2818: if (!pointIS) continue; /* No points with that id on this process */
2819: {
2820: IS isectIS;
2822: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2823: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2824: PetscCall(ISDestroy(&pointIS));
2825: pointIS = isectIS;
2826: }
2827: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2828: PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2829: PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, funcs, fintegral, user));
2830: /* Sum point contributions into integral */
2831: for (f = 0; f < Nf; ++f)
2832: for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2833: PetscCall(PetscFree(fintegral));
2834: PetscCall(ISDestroy(&pointIS));
2835: }
2836: PetscCall(DMRestoreLocalVector(dm, &locX));
2837: PetscCall(ISDestroy(&facetIS));
2838: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2839: PetscFunctionReturn(PETSC_SUCCESS);
2840: }
2842: /*@
2843: DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix from the coarse `DM` to a uniformly refined `DM`.
2845: Input Parameters:
2846: + dmc - The coarse mesh
2847: . dmf - The fine mesh
2848: . isRefined - Flag indicating regular refinement, rather than the same topology
2849: - user - The user context
2851: Output Parameter:
2852: . In - The interpolation matrix
2854: Level: developer
2856: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`
2857: @*/
2858: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2859: {
2860: DM_Plex *mesh = (DM_Plex *)dmc->data;
2861: const char *name = "Interpolator";
2862: PetscFE *feRef;
2863: PetscFV *fvRef;
2864: PetscSection fsection, fglobalSection;
2865: PetscSection csection, cglobalSection;
2866: PetscScalar *elemMat;
2867: PetscInt dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2868: PetscInt cTotDim = 0, rTotDim = 0;
2870: PetscFunctionBegin;
2871: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2872: PetscCall(DMGetDimension(dmf, &dim));
2873: PetscCall(DMGetLocalSection(dmf, &fsection));
2874: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2875: PetscCall(DMGetLocalSection(dmc, &csection));
2876: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2877: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2878: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2879: PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2880: for (f = 0; f < Nf; ++f) {
2881: PetscObject obj, objc;
2882: PetscClassId id, idc;
2883: PetscInt rNb = 0, Nc = 0, cNb = 0;
2885: PetscCall(DMGetField(dmf, f, NULL, &obj));
2886: PetscCall(PetscObjectGetClassId(obj, &id));
2887: if (id == PETSCFE_CLASSID) {
2888: PetscFE fe = (PetscFE)obj;
2890: if (isRefined) {
2891: PetscCall(PetscFERefine(fe, &feRef[f]));
2892: } else {
2893: PetscCall(PetscObjectReference((PetscObject)fe));
2894: feRef[f] = fe;
2895: }
2896: PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2897: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2898: } else if (id == PETSCFV_CLASSID) {
2899: PetscFV fv = (PetscFV)obj;
2900: PetscDualSpace Q;
2902: if (isRefined) {
2903: PetscCall(PetscFVRefine(fv, &fvRef[f]));
2904: } else {
2905: PetscCall(PetscObjectReference((PetscObject)fv));
2906: fvRef[f] = fv;
2907: }
2908: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2909: PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2910: PetscCall(PetscFVGetDualSpace(fv, &Q));
2911: PetscCall(PetscFVGetNumComponents(fv, &Nc));
2912: }
2913: PetscCall(DMGetField(dmc, f, NULL, &objc));
2914: PetscCall(PetscObjectGetClassId(objc, &idc));
2915: if (idc == PETSCFE_CLASSID) {
2916: PetscFE fe = (PetscFE)objc;
2918: PetscCall(PetscFEGetDimension(fe, &cNb));
2919: } else if (id == PETSCFV_CLASSID) {
2920: PetscFV fv = (PetscFV)obj;
2921: PetscDualSpace Q;
2923: PetscCall(PetscFVGetDualSpace(fv, &Q));
2924: PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
2925: }
2926: rTotDim += rNb;
2927: cTotDim += cNb;
2928: }
2929: PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
2930: PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
2931: for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2932: PetscDualSpace Qref;
2933: PetscQuadrature f;
2934: const PetscReal *qpoints, *qweights;
2935: PetscReal *points;
2936: PetscInt npoints = 0, Nc, Np, fpdim, i, k, p, d;
2938: /* Compose points from all dual basis functionals */
2939: if (feRef[fieldI]) {
2940: PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
2941: PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
2942: } else {
2943: PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
2944: PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
2945: }
2946: PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
2947: for (i = 0; i < fpdim; ++i) {
2948: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2949: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
2950: npoints += Np;
2951: }
2952: PetscCall(PetscMalloc1(npoints * dim, &points));
2953: for (i = 0, k = 0; i < fpdim; ++i) {
2954: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2955: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
2956: for (p = 0; p < Np; ++p, ++k)
2957: for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2958: }
2960: for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2961: PetscObject obj;
2962: PetscClassId id;
2963: PetscInt NcJ = 0, cpdim = 0, j, qNc;
2965: PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
2966: PetscCall(PetscObjectGetClassId(obj, &id));
2967: if (id == PETSCFE_CLASSID) {
2968: PetscFE fe = (PetscFE)obj;
2969: PetscTabulation T = NULL;
2971: /* Evaluate basis at points */
2972: PetscCall(PetscFEGetNumComponents(fe, &NcJ));
2973: PetscCall(PetscFEGetDimension(fe, &cpdim));
2974: /* For now, fields only interpolate themselves */
2975: if (fieldI == fieldJ) {
2976: 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);
2977: PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
2978: for (i = 0, k = 0; i < fpdim; ++i) {
2979: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2980: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2981: 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);
2982: for (p = 0; p < Np; ++p, ++k) {
2983: for (j = 0; j < cpdim; ++j) {
2984: /*
2985: cTotDim: Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2986: offsetI, offsetJ: Offsets into the larger element interpolation matrix for different fields
2987: fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2988: qNC, Nc, Ncj, c: Number of components in this field
2989: Np, p: Number of quad points in the fine grid functional i
2990: k: i*Np + p, overall point number for the interpolation
2991: */
2992: 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];
2993: }
2994: }
2995: }
2996: PetscCall(PetscTabulationDestroy(&T));
2997: }
2998: } else if (id == PETSCFV_CLASSID) {
2999: PetscFV fv = (PetscFV)obj;
3001: /* Evaluate constant function at points */
3002: PetscCall(PetscFVGetNumComponents(fv, &NcJ));
3003: cpdim = 1;
3004: /* For now, fields only interpolate themselves */
3005: if (fieldI == fieldJ) {
3006: 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);
3007: for (i = 0, k = 0; i < fpdim; ++i) {
3008: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
3009: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
3010: 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);
3011: for (p = 0; p < Np; ++p, ++k) {
3012: for (j = 0; j < cpdim; ++j) {
3013: for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
3014: }
3015: }
3016: }
3017: }
3018: }
3019: offsetJ += cpdim;
3020: }
3021: offsetI += fpdim;
3022: PetscCall(PetscFree(points));
3023: }
3024: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
3025: /* Preallocate matrix */
3026: {
3027: Mat preallocator;
3028: PetscScalar *vals;
3029: PetscInt *cellCIndices, *cellFIndices;
3030: PetscInt locRows, locCols, cell;
3032: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3033: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
3034: PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
3035: PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3036: PetscCall(MatSetUp(preallocator));
3037: PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
3038: for (cell = cStart; cell < cEnd; ++cell) {
3039: if (isRefined) {
3040: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
3041: PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
3042: } else {
3043: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
3044: }
3045: }
3046: PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
3047: PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
3048: PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
3049: PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
3050: PetscCall(MatDestroy(&preallocator));
3051: }
3052: /* Fill matrix */
3053: PetscCall(MatZeroEntries(In));
3054: for (c = cStart; c < cEnd; ++c) {
3055: if (isRefined) {
3056: PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
3057: } else {
3058: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
3059: }
3060: }
3061: for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
3062: PetscCall(PetscFree2(feRef, fvRef));
3063: PetscCall(PetscFree(elemMat));
3064: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3065: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3066: if (mesh->printFEM > 1) {
3067: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
3068: PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
3069: PetscCall(MatView(In, NULL));
3070: }
3071: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3072: PetscFunctionReturn(PETSC_SUCCESS);
3073: }
3075: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
3076: {
3077: SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
3078: }
3080: /*@
3081: DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix from the coarse `DM` to a non-nested fine `DM`.
3083: Input Parameters:
3084: + dmf - The fine mesh
3085: . dmc - The coarse mesh
3086: - user - The user context
3088: Output Parameter:
3089: . In - The interpolation matrix
3091: Level: developer
3093: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3094: @*/
3095: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
3096: {
3097: DM_Plex *mesh = (DM_Plex *)dmf->data;
3098: const char *name = "Interpolator";
3099: PetscDS prob;
3100: Mat interp;
3101: PetscSection fsection, globalFSection;
3102: PetscSection csection, globalCSection;
3103: PetscInt locRows, locCols;
3104: PetscReal *x, *v0, *J, *invJ, detJ;
3105: PetscReal *v0c, *Jc, *invJc, detJc;
3106: PetscScalar *elemMat;
3107: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;
3109: PetscFunctionBegin;
3110: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3111: PetscCall(DMGetCoordinateDim(dmc, &dim));
3112: PetscCall(DMGetDS(dmc, &prob));
3113: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3114: PetscCall(PetscDSGetNumFields(prob, &Nf));
3115: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3116: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3117: PetscCall(DMGetLocalSection(dmf, &fsection));
3118: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3119: PetscCall(DMGetLocalSection(dmc, &csection));
3120: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3121: PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
3122: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3123: PetscCall(PetscMalloc1(totDim, &elemMat));
3125: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
3126: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
3127: PetscCall(MatSetType(interp, MATPREALLOCATOR));
3128: PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
3129: PetscCall(MatSetUp(interp));
3130: for (s = 0; s < 2; ++s) {
3131: for (field = 0; field < Nf; ++field) {
3132: PetscObject obj;
3133: PetscClassId id;
3134: PetscDualSpace Q = NULL;
3135: PetscTabulation T = NULL;
3136: PetscQuadrature f;
3137: const PetscReal *qpoints, *qweights;
3138: PetscInt Nc, qNc, Np, fpdim, off, i, d;
3140: PetscCall(PetscDSGetFieldOffset(prob, field, &off));
3141: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3142: PetscCall(PetscObjectGetClassId(obj, &id));
3143: if (id == PETSCFE_CLASSID) {
3144: PetscFE fe = (PetscFE)obj;
3146: PetscCall(PetscFEGetDualSpace(fe, &Q));
3147: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3148: if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
3149: } else if (id == PETSCFV_CLASSID) {
3150: PetscFV fv = (PetscFV)obj;
3152: PetscCall(PetscFVGetDualSpace(fv, &Q));
3153: Nc = 1;
3154: } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
3155: PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
3156: /* For each fine grid cell */
3157: for (cell = cStart; cell < cEnd; ++cell) {
3158: PetscInt *findices, *cindices;
3159: PetscInt numFIndices, numCIndices;
3161: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3162: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3163: PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
3164: for (i = 0; i < fpdim; ++i) {
3165: Vec pointVec;
3166: PetscScalar *pV;
3167: PetscSF coarseCellSF = NULL;
3168: const PetscSFNode *coarseCells;
3169: PetscInt numCoarseCells, cpdim, row = findices[i + off], q, c, j;
3171: /* Get points from the dual basis functional quadrature */
3172: PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
3173: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
3174: 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);
3175: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
3176: PetscCall(VecSetBlockSize(pointVec, dim));
3177: PetscCall(VecGetArray(pointVec, &pV));
3178: for (q = 0; q < Np; ++q) {
3179: const PetscReal xi0[3] = {-1., -1., -1.};
3181: /* Transform point to real space */
3182: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3183: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3184: }
3185: PetscCall(VecRestoreArray(pointVec, &pV));
3186: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3187: /* OPT: Read this out from preallocation information */
3188: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3189: /* Update preallocation info */
3190: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3191: PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3192: PetscCall(VecGetArray(pointVec, &pV));
3193: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3194: PetscReal pVReal[3];
3195: const PetscReal xi0[3] = {-1., -1., -1.};
3197: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3198: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim));
3199: else cpdim = 1;
3201: if (s) {
3202: /* Transform points from real space to coarse reference space */
3203: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3204: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3205: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3207: if (id == PETSCFE_CLASSID) {
3208: /* Evaluate coarse basis on contained point */
3209: PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
3210: PetscCall(PetscArrayzero(elemMat, cpdim));
3211: /* Get elemMat entries by multiplying by weight */
3212: for (j = 0; j < cpdim; ++j) {
3213: for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
3214: }
3215: } else {
3216: for (j = 0; j < cpdim; ++j) {
3217: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
3218: }
3219: }
3220: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3221: }
3222: /* Update interpolator */
3223: PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
3224: PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
3225: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3226: }
3227: PetscCall(VecRestoreArray(pointVec, &pV));
3228: PetscCall(PetscSFDestroy(&coarseCellSF));
3229: PetscCall(VecDestroy(&pointVec));
3230: }
3231: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3232: }
3233: if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3234: }
3235: if (!s) {
3236: PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
3237: PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
3238: PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
3239: PetscCall(MatDestroy(&interp));
3240: interp = In;
3241: }
3242: }
3243: PetscCall(PetscFree3(v0, J, invJ));
3244: PetscCall(PetscFree3(v0c, Jc, invJc));
3245: PetscCall(PetscFree(elemMat));
3246: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3247: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3248: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3249: PetscFunctionReturn(PETSC_SUCCESS);
3250: }
3252: /*@
3253: DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix from the coarse `DM` to a non-nested fine `DM`.
3255: Input Parameters:
3256: + dmf - The fine mesh
3257: . dmc - The coarse mesh
3258: - user - The user context
3260: Output Parameter:
3261: . mass - The mass matrix
3263: Level: developer
3265: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`
3266: @*/
3267: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
3268: {
3269: DM_Plex *mesh = (DM_Plex *)dmf->data;
3270: const char *name = "Mass Matrix";
3271: PetscDS prob;
3272: PetscSection fsection, csection, globalFSection, globalCSection;
3273: PetscHSetIJ ht;
3274: PetscLayout rLayout;
3275: PetscInt *dnz, *onz;
3276: PetscInt locRows, rStart, rEnd;
3277: PetscReal *x, *v0, *J, *invJ, detJ;
3278: PetscReal *v0c, *Jc, *invJc, detJc;
3279: PetscScalar *elemMat;
3280: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
3282: PetscFunctionBegin;
3283: PetscCall(DMGetCoordinateDim(dmc, &dim));
3284: PetscCall(DMGetDS(dmc, &prob));
3285: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3286: PetscCall(PetscDSGetNumFields(prob, &Nf));
3287: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3288: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3289: PetscCall(DMGetLocalSection(dmf, &fsection));
3290: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3291: PetscCall(DMGetLocalSection(dmc, &csection));
3292: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3293: PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
3294: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3295: PetscCall(PetscMalloc1(totDim, &elemMat));
3297: PetscCall(MatGetLocalSize(mass, &locRows, NULL));
3298: PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
3299: PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
3300: PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
3301: PetscCall(PetscLayoutSetUp(rLayout));
3302: PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
3303: PetscCall(PetscLayoutDestroy(&rLayout));
3304: PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
3305: PetscCall(PetscHSetIJCreate(&ht));
3306: for (field = 0; field < Nf; ++field) {
3307: PetscObject obj;
3308: PetscClassId id;
3309: PetscQuadrature quad;
3310: const PetscReal *qpoints;
3311: PetscInt Nq, Nc, i, d;
3313: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3314: PetscCall(PetscObjectGetClassId(obj, &id));
3315: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3316: else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3317: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
3318: /* For each fine grid cell */
3319: for (cell = cStart; cell < cEnd; ++cell) {
3320: Vec pointVec;
3321: PetscScalar *pV;
3322: PetscSF coarseCellSF = NULL;
3323: const PetscSFNode *coarseCells;
3324: PetscInt numCoarseCells, q, c;
3325: PetscInt *findices, *cindices;
3326: PetscInt numFIndices, numCIndices;
3328: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3329: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3330: /* Get points from the quadrature */
3331: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3332: PetscCall(VecSetBlockSize(pointVec, dim));
3333: PetscCall(VecGetArray(pointVec, &pV));
3334: for (q = 0; q < Nq; ++q) {
3335: const PetscReal xi0[3] = {-1., -1., -1.};
3337: /* Transform point to real space */
3338: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3339: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3340: }
3341: PetscCall(VecRestoreArray(pointVec, &pV));
3342: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3343: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3344: PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3345: /* Update preallocation info */
3346: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3347: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3348: {
3349: PetscHashIJKey key;
3350: PetscBool missing;
3352: for (i = 0; i < numFIndices; ++i) {
3353: key.i = findices[i];
3354: if (key.i >= 0) {
3355: /* Get indices for coarse elements */
3356: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3357: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3358: for (c = 0; c < numCIndices; ++c) {
3359: key.j = cindices[c];
3360: if (key.j < 0) continue;
3361: PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3362: if (missing) {
3363: if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3364: else ++onz[key.i - rStart];
3365: }
3366: }
3367: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3368: }
3369: }
3370: }
3371: }
3372: PetscCall(PetscSFDestroy(&coarseCellSF));
3373: PetscCall(VecDestroy(&pointVec));
3374: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3375: }
3376: }
3377: PetscCall(PetscHSetIJDestroy(&ht));
3378: PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3379: PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3380: PetscCall(PetscFree2(dnz, onz));
3381: for (field = 0; field < Nf; ++field) {
3382: PetscObject obj;
3383: PetscClassId id;
3384: PetscTabulation T, Tfine;
3385: PetscQuadrature quad;
3386: const PetscReal *qpoints, *qweights;
3387: PetscInt Nq, Nc, i, d;
3389: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3390: PetscCall(PetscObjectGetClassId(obj, &id));
3391: if (id == PETSCFE_CLASSID) {
3392: PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3393: PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3394: PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3395: } else {
3396: PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3397: }
3398: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3399: /* For each fine grid cell */
3400: for (cell = cStart; cell < cEnd; ++cell) {
3401: Vec pointVec;
3402: PetscScalar *pV;
3403: PetscSF coarseCellSF = NULL;
3404: const PetscSFNode *coarseCells;
3405: PetscInt numCoarseCells, cpdim, q, c, j;
3406: PetscInt *findices, *cindices;
3407: PetscInt numFIndices, numCIndices;
3409: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3410: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3411: /* Get points from the quadrature */
3412: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3413: PetscCall(VecSetBlockSize(pointVec, dim));
3414: PetscCall(VecGetArray(pointVec, &pV));
3415: for (q = 0; q < Nq; ++q) {
3416: const PetscReal xi0[3] = {-1., -1., -1.};
3418: /* Transform point to real space */
3419: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3420: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3421: }
3422: PetscCall(VecRestoreArray(pointVec, &pV));
3423: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3424: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3425: /* Update matrix */
3426: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3427: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3428: PetscCall(VecGetArray(pointVec, &pV));
3429: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3430: PetscReal pVReal[3];
3431: const PetscReal xi0[3] = {-1., -1., -1.};
3433: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3434: /* Transform points from real space to coarse reference space */
3435: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3436: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3437: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3439: if (id == PETSCFE_CLASSID) {
3440: PetscFE fe = (PetscFE)obj;
3442: /* Evaluate coarse basis on contained point */
3443: PetscCall(PetscFEGetDimension(fe, &cpdim));
3444: PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3445: /* Get elemMat entries by multiplying by weight */
3446: for (i = 0; i < numFIndices; ++i) {
3447: PetscCall(PetscArrayzero(elemMat, cpdim));
3448: for (j = 0; j < cpdim; ++j) {
3449: 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;
3450: }
3451: /* Update interpolator */
3452: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3453: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3454: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3455: }
3456: } else {
3457: cpdim = 1;
3458: for (i = 0; i < numFIndices; ++i) {
3459: PetscCall(PetscArrayzero(elemMat, cpdim));
3460: for (j = 0; j < cpdim; ++j) {
3461: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3462: }
3463: /* Update interpolator */
3464: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3465: 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));
3466: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3467: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3468: }
3469: }
3470: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3471: }
3472: PetscCall(VecRestoreArray(pointVec, &pV));
3473: PetscCall(PetscSFDestroy(&coarseCellSF));
3474: PetscCall(VecDestroy(&pointVec));
3475: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3476: }
3477: if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3478: }
3479: PetscCall(PetscFree3(v0, J, invJ));
3480: PetscCall(PetscFree3(v0c, Jc, invJc));
3481: PetscCall(PetscFree(elemMat));
3482: PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3483: PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3484: PetscFunctionReturn(PETSC_SUCCESS);
3485: }
3487: /*@
3488: DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
3490: Input Parameters:
3491: + dmc - The coarse mesh
3492: . dmf - The fine mesh
3493: - user - The user context
3495: Output Parameter:
3496: . sc - The mapping
3498: Level: developer
3500: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
3501: @*/
3502: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3503: {
3504: PetscDS prob;
3505: PetscFE *feRef;
3506: PetscFV *fvRef;
3507: Vec fv, cv;
3508: IS fis, cis;
3509: PetscSection fsection, fglobalSection, csection, cglobalSection;
3510: PetscInt *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3511: PetscInt cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3512: PetscBool *needAvg;
3514: PetscFunctionBegin;
3515: PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3516: PetscCall(DMGetDimension(dmf, &dim));
3517: PetscCall(DMGetLocalSection(dmf, &fsection));
3518: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3519: PetscCall(DMGetLocalSection(dmc, &csection));
3520: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3521: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3522: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3523: PetscCall(DMGetDS(dmc, &prob));
3524: PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3525: for (f = 0; f < Nf; ++f) {
3526: PetscObject obj;
3527: PetscClassId id;
3528: PetscInt fNb = 0, Nc = 0;
3530: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3531: PetscCall(PetscObjectGetClassId(obj, &id));
3532: if (id == PETSCFE_CLASSID) {
3533: PetscFE fe = (PetscFE)obj;
3534: PetscSpace sp;
3535: PetscInt maxDegree;
3537: PetscCall(PetscFERefine(fe, &feRef[f]));
3538: PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3539: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3540: PetscCall(PetscFEGetBasisSpace(fe, &sp));
3541: PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3542: if (!maxDegree) needAvg[f] = PETSC_TRUE;
3543: } else if (id == PETSCFV_CLASSID) {
3544: PetscFV fv = (PetscFV)obj;
3545: PetscDualSpace Q;
3547: PetscCall(PetscFVRefine(fv, &fvRef[f]));
3548: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3549: PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3550: PetscCall(PetscFVGetNumComponents(fv, &Nc));
3551: needAvg[f] = PETSC_TRUE;
3552: }
3553: fTotDim += fNb;
3554: }
3555: PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3556: PetscCall(PetscMalloc1(cTotDim, &cmap));
3557: for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3558: PetscFE feC;
3559: PetscFV fvC;
3560: PetscDualSpace QF, QC;
3561: PetscInt order = -1, NcF, NcC, fpdim, cpdim;
3563: if (feRef[field]) {
3564: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3565: PetscCall(PetscFEGetNumComponents(feC, &NcC));
3566: PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3567: PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3568: PetscCall(PetscDualSpaceGetOrder(QF, &order));
3569: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3570: PetscCall(PetscFEGetDualSpace(feC, &QC));
3571: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3572: } else {
3573: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3574: PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3575: PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3576: PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3577: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3578: PetscCall(PetscFVGetDualSpace(fvC, &QC));
3579: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3580: }
3581: 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);
3582: for (c = 0; c < cpdim; ++c) {
3583: PetscQuadrature cfunc;
3584: const PetscReal *cqpoints, *cqweights;
3585: PetscInt NqcC, NpC;
3586: PetscBool found = PETSC_FALSE;
3588: PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3589: PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3590: 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);
3591: PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3592: for (f = 0; f < fpdim; ++f) {
3593: PetscQuadrature ffunc;
3594: const PetscReal *fqpoints, *fqweights;
3595: PetscReal sum = 0.0;
3596: PetscInt NqcF, NpF;
3598: PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3599: PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3600: 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);
3601: if (NpC != NpF) continue;
3602: for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3603: if (sum > 1.0e-9) continue;
3604: for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3605: if (sum < 1.0e-9) continue;
3606: cmap[offsetC + c] = offsetF + f;
3607: found = PETSC_TRUE;
3608: break;
3609: }
3610: if (!found) {
3611: /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3612: if (fvRef[field] || (feRef[field] && order == 0)) {
3613: cmap[offsetC + c] = offsetF + 0;
3614: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3615: }
3616: }
3617: offsetC += cpdim;
3618: offsetF += fpdim;
3619: }
3620: for (f = 0; f < Nf; ++f) {
3621: PetscCall(PetscFEDestroy(&feRef[f]));
3622: PetscCall(PetscFVDestroy(&fvRef[f]));
3623: }
3624: PetscCall(PetscFree3(feRef, fvRef, needAvg));
3626: PetscCall(DMGetGlobalVector(dmf, &fv));
3627: PetscCall(DMGetGlobalVector(dmc, &cv));
3628: PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3629: PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3630: PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3631: PetscCall(PetscMalloc1(m, &cindices));
3632: PetscCall(PetscMalloc1(m, &findices));
3633: for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3634: for (c = cStart; c < cEnd; ++c) {
3635: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3636: for (d = 0; d < cTotDim; ++d) {
3637: if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3638: 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]]);
3639: cindices[cellCIndices[d] - startC] = cellCIndices[d];
3640: findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3641: }
3642: }
3643: PetscCall(PetscFree(cmap));
3644: PetscCall(PetscFree2(cellCIndices, cellFIndices));
3646: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3647: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3648: PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3649: PetscCall(ISDestroy(&cis));
3650: PetscCall(ISDestroy(&fis));
3651: PetscCall(DMRestoreGlobalVector(dmf, &fv));
3652: PetscCall(DMRestoreGlobalVector(dmc, &cv));
3653: PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3654: PetscFunctionReturn(PETSC_SUCCESS);
3655: }
3657: /*@C
3658: DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3660: Input Parameters:
3661: + dm - The `DM`
3662: . cellIS - The cells to include
3663: . locX - A local vector with the solution fields
3664: . locX_t - A local vector with solution field time derivatives, or NULL
3665: - locA - A local vector with auxiliary fields, or NULL
3667: Output Parameters:
3668: + u - The field coefficients
3669: . u_t - The fields derivative coefficients
3670: - a - The auxiliary field coefficients
3672: Level: developer
3674: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3675: @*/
3676: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3677: {
3678: DM plex, plexA = NULL;
3679: DMEnclosureType encAux;
3680: PetscSection section, sectionAux;
3681: PetscDS prob;
3682: const PetscInt *cells;
3683: PetscInt cStart, cEnd, numCells, totDim, totDimAux, c;
3685: PetscFunctionBegin;
3690: PetscAssertPointer(u, 6);
3691: PetscAssertPointer(u_t, 7);
3692: PetscAssertPointer(a, 8);
3693: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3694: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3695: PetscCall(DMGetLocalSection(dm, §ion));
3696: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3697: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3698: if (locA) {
3699: DM dmAux;
3700: PetscDS probAux;
3702: PetscCall(VecGetDM(locA, &dmAux));
3703: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3704: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3705: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3706: PetscCall(DMGetDS(dmAux, &probAux));
3707: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3708: }
3709: numCells = cEnd - cStart;
3710: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3711: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3712: else *u_t = NULL;
3713: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3714: else *a = NULL;
3715: for (c = cStart; c < cEnd; ++c) {
3716: const PetscInt cell = cells ? cells[c] : c;
3717: const PetscInt cind = c - cStart;
3718: PetscScalar *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3719: PetscInt i;
3721: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3722: for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3723: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3724: if (locX_t) {
3725: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3726: for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3727: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3728: }
3729: if (locA) {
3730: PetscInt subcell;
3731: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3732: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3733: for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3734: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3735: }
3736: }
3737: PetscCall(DMDestroy(&plex));
3738: if (locA) PetscCall(DMDestroy(&plexA));
3739: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3740: PetscFunctionReturn(PETSC_SUCCESS);
3741: }
3743: /*@C
3744: DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3746: Input Parameters:
3747: + dm - The `DM`
3748: . cellIS - The cells to include
3749: . locX - A local vector with the solution fields
3750: . locX_t - A local vector with solution field time derivatives, or NULL
3751: - locA - A local vector with auxiliary fields, or NULL
3753: Output Parameters:
3754: + u - The field coefficients
3755: . u_t - The fields derivative coefficients
3756: - a - The auxiliary field coefficients
3758: Level: developer
3760: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3761: @*/
3762: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3763: {
3764: PetscFunctionBegin;
3765: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3766: if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3767: if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3768: PetscFunctionReturn(PETSC_SUCCESS);
3769: }
3771: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3772: {
3773: DM plex, plexA = NULL;
3774: DMEnclosureType encAux;
3775: PetscSection section, sectionAux;
3776: PetscDS ds, dsIn;
3777: const PetscInt *cells;
3778: PetscInt cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;
3780: PetscFunctionBegin;
3786: PetscAssertPointer(u, 6);
3787: PetscAssertPointer(u_t, 7);
3788: PetscAssertPointer(a, 8);
3789: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3790: numCells = cEnd - cStart;
3791: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3792: PetscCall(DMGetLocalSection(dm, §ion));
3793: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3794: PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3795: PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3796: if (locA) {
3797: DM dmAux;
3798: PetscDS probAux;
3800: PetscCall(VecGetDM(locA, &dmAux));
3801: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3802: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3803: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3804: PetscCall(DMGetDS(dmAux, &probAux));
3805: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3806: }
3807: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3808: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3809: else {
3810: *u_t = NULL;
3811: }
3812: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3813: else {
3814: *a = NULL;
3815: }
3816: // Loop over cohesive cells
3817: for (c = cStart; c < cEnd; ++c) {
3818: const PetscInt cell = cells ? cells[c] : c;
3819: const PetscInt cind = c - cStart;
3820: PetscScalar *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
3821: PetscScalar *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim);
3822: const PetscInt *cone, *ornt;
3823: PetscInt Nx = 0, Nxf, s;
3825: PetscCall(DMPlexGetCone(dm, cell, &cone));
3826: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3827: // Put in cohesive unknowns
3828: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3829: if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3830: for (f = 0; f < Nf; ++f) {
3831: PetscInt fdofIn, foff, foffIn;
3832: PetscBool cohesive;
3834: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3835: if (!cohesive) continue;
3836: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3837: PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3838: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3839: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3840: if (locX_t)
3841: for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3842: Nx += fdofIn;
3843: }
3844: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3845: if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3846: // Loop over sides of surface
3847: for (s = 0; s < 2; ++s) {
3848: const PetscInt *support;
3849: const PetscInt face = cone[s];
3850: PetscInt ssize, ncell, Nxc;
3852: // I don't think I need the face to have 0 orientation in the hybrid cell
3853: //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]);
3854: PetscCall(DMPlexGetSupport(dm, face, &support));
3855: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3856: if (support[0] == cell) ncell = support[1];
3857: else if (support[1] == cell) ncell = support[0];
3858: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3859: // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3860: PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3861: if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3862: for (f = 0; f < Nf; ++f) {
3863: PetscInt fdofIn, foffIn;
3864: PetscBool cohesive;
3866: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3867: if (cohesive) continue;
3868: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3869: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3870: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i];
3871: if (locX_t)
3872: for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foffIn + i];
3873: Nx += fdofIn;
3874: }
3875: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3876: if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3877: }
3878: 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);
3880: if (locA) {
3881: PetscScalar *al = &(*a)[cind * totDimAux];
3882: PetscInt subcell;
3884: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3885: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3886: 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);
3887: for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3888: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3889: }
3890: }
3891: PetscCall(DMDestroy(&plex));
3892: PetscCall(DMDestroy(&plexA));
3893: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3894: PetscFunctionReturn(PETSC_SUCCESS);
3895: }
3897: /*
3898: DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface
3900: Input Parameters:
3901: + dm - The full domain DM
3902: . dmX - An array of DM for the field, say an auxiliary DM, indexed by s
3903: . dsX - An array of PetscDS for the field, indexed by s
3904: . cellIS - The interface cells for which we want values
3905: . locX - An array of local vectors with the field values, indexed by s
3906: - useCell - Flag to have values come from neighboring cell rather than endcap face
3908: Output Parameter:
3909: . x - An array of field values, indexed by s
3911: Note:
3912: The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`.
3914: Level: advanced
3916: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
3917: */
3918: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3919: {
3920: DM plexX[2];
3921: DMEnclosureType encX[2];
3922: PetscSection sectionX[2];
3923: const PetscInt *cells;
3924: PetscInt cStart, cEnd, numCells, c, s, totDimX[2];
3926: PetscFunctionBegin;
3927: PetscAssertPointer(locX, 5);
3928: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3929: PetscAssertPointer(dmX, 2);
3930: PetscAssertPointer(dsX, 3);
3932: PetscAssertPointer(x, 7);
3933: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3934: numCells = cEnd - cStart;
3935: for (s = 0; s < 2; ++s) {
3939: PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
3940: PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
3941: PetscCall(DMGetLocalSection(dmX[s], §ionX[s]));
3942: PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
3943: PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
3944: }
3945: for (c = cStart; c < cEnd; ++c) {
3946: const PetscInt cell = cells ? cells[c] : c;
3947: const PetscInt cind = c - cStart;
3948: const PetscInt *cone, *ornt;
3950: PetscCall(DMPlexGetCone(dm, cell, &cone));
3951: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3952: //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]);
3953: for (s = 0; s < 2; ++s) {
3954: const PetscInt tdX = totDimX[s];
3955: PetscScalar *closure = NULL, *xl = &x[s][cind * tdX];
3956: PetscInt face = cone[s], point = face, subpoint, Nx, i;
3958: if (useCell) {
3959: const PetscInt *support;
3960: PetscInt ssize;
3962: PetscCall(DMPlexGetSupport(dm, face, &support));
3963: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3964: 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);
3965: if (support[0] == cell) point = support[1];
3966: else if (support[1] == cell) point = support[0];
3967: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3968: }
3969: PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
3970: PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
3971: 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);
3972: for (i = 0; i < Nx; ++i) xl[i] = closure[i];
3973: PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
3974: }
3975: }
3976: for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
3977: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3978: PetscFunctionReturn(PETSC_SUCCESS);
3979: }
3981: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3982: {
3983: PetscFunctionBegin;
3984: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3985: PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
3986: PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
3987: PetscFunctionReturn(PETSC_SUCCESS);
3988: }
3990: /*@C
3991: DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3993: Input Parameters:
3994: + dm - The `DM`
3995: . fStart - The first face to include
3996: . fEnd - The first face to exclude
3997: . locX - A local vector with the solution fields
3998: . locX_t - A local vector with solution field time derivatives, or NULL
3999: . faceGeometry - A local vector with face geometry
4000: . cellGeometry - A local vector with cell geometry
4001: - locGrad - A local vector with field gradients, or NULL
4003: Output Parameters:
4004: + Nface - The number of faces with field values
4005: . uL - The field values at the left side of the face
4006: - uR - The field values at the right side of the face
4008: Level: developer
4010: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4011: @*/
4012: PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
4013: {
4014: DM dmFace, dmCell, dmGrad = NULL;
4015: PetscSection section;
4016: PetscDS prob;
4017: DMLabel ghostLabel;
4018: const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
4019: PetscBool *isFE;
4020: PetscInt dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
4022: PetscFunctionBegin;
4029: PetscAssertPointer(uL, 10);
4030: PetscAssertPointer(uR, 11);
4031: PetscCall(DMGetDimension(dm, &dim));
4032: PetscCall(DMGetDS(dm, &prob));
4033: PetscCall(DMGetLocalSection(dm, §ion));
4034: PetscCall(PetscDSGetNumFields(prob, &Nf));
4035: PetscCall(PetscDSGetTotalComponents(prob, &Nc));
4036: PetscCall(PetscMalloc1(Nf, &isFE));
4037: for (f = 0; f < Nf; ++f) {
4038: PetscObject obj;
4039: PetscClassId id;
4041: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4042: PetscCall(PetscObjectGetClassId(obj, &id));
4043: if (id == PETSCFE_CLASSID) {
4044: isFE[f] = PETSC_TRUE;
4045: } else if (id == PETSCFV_CLASSID) {
4046: isFE[f] = PETSC_FALSE;
4047: } else {
4048: isFE[f] = PETSC_FALSE;
4049: }
4050: }
4051: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4052: PetscCall(VecGetArrayRead(locX, &x));
4053: PetscCall(VecGetDM(faceGeometry, &dmFace));
4054: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4055: PetscCall(VecGetDM(cellGeometry, &dmCell));
4056: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4057: if (locGrad) {
4058: PetscCall(VecGetDM(locGrad, &dmGrad));
4059: PetscCall(VecGetArrayRead(locGrad, &lgrad));
4060: }
4061: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
4062: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
4063: /* Right now just eat the extra work for FE (could make a cell loop) */
4064: for (face = fStart, iface = 0; face < fEnd; ++face) {
4065: const PetscInt *cells;
4066: PetscFVFaceGeom *fg;
4067: PetscFVCellGeom *cgL, *cgR;
4068: PetscScalar *xL, *xR, *gL, *gR;
4069: PetscScalar *uLl = *uL, *uRl = *uR;
4070: PetscInt ghost, nsupp, nchild;
4072: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4073: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4074: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4075: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4076: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4077: PetscCall(DMPlexGetSupport(dm, face, &cells));
4078: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4079: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4080: for (f = 0; f < Nf; ++f) {
4081: PetscInt off;
4083: PetscCall(PetscDSGetComponentOffset(prob, f, &off));
4084: if (isFE[f]) {
4085: const PetscInt *cone;
4086: PetscInt comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
4088: xL = xR = NULL;
4089: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4090: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, &xL));
4091: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, &xR));
4092: PetscCall(DMPlexGetCone(dm, cells[0], &cone));
4093: PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
4094: for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
4095: if (cone[faceLocL] == face) break;
4096: PetscCall(DMPlexGetCone(dm, cells[1], &cone));
4097: PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
4098: for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
4099: if (cone[faceLocR] == face) break;
4100: 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]);
4101: /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
4102: /* TODO: this is a hack that might not be right for nonconforming */
4103: if (faceLocL < coneSizeL) {
4104: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
4105: if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4106: else {
4107: for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
4108: }
4109: } else {
4110: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
4111: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
4112: for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
4113: }
4114: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, &xL));
4115: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, &xR));
4116: } else {
4117: PetscFV fv;
4118: PetscInt numComp, c;
4120: PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
4121: PetscCall(PetscFVGetNumComponents(fv, &numComp));
4122: PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
4123: PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
4124: if (dmGrad) {
4125: PetscReal dxL[3], dxR[3];
4127: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
4128: PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
4129: DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
4130: DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
4131: for (c = 0; c < numComp; ++c) {
4132: uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
4133: uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
4134: }
4135: } else {
4136: for (c = 0; c < numComp; ++c) {
4137: uLl[iface * Nc + off + c] = xL[c];
4138: uRl[iface * Nc + off + c] = xR[c];
4139: }
4140: }
4141: }
4142: }
4143: ++iface;
4144: }
4145: *Nface = iface;
4146: PetscCall(VecRestoreArrayRead(locX, &x));
4147: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4148: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4149: if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
4150: PetscCall(PetscFree(isFE));
4151: PetscFunctionReturn(PETSC_SUCCESS);
4152: }
4154: /*@C
4155: DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
4157: Input Parameters:
4158: + dm - The `DM`
4159: . fStart - The first face to include
4160: . fEnd - The first face to exclude
4161: . locX - A local vector with the solution fields
4162: . locX_t - A local vector with solution field time derivatives, or NULL
4163: . faceGeometry - A local vector with face geometry
4164: . cellGeometry - A local vector with cell geometry
4165: - locGrad - A local vector with field gradients, or NULL
4167: Output Parameters:
4168: + Nface - The number of faces with field values
4169: . uL - The field values at the left side of the face
4170: - uR - The field values at the right side of the face
4172: Level: developer
4174: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4175: @*/
4176: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
4177: {
4178: PetscFunctionBegin;
4179: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
4180: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
4181: PetscFunctionReturn(PETSC_SUCCESS);
4182: }
4184: /*@C
4185: DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
4187: Input Parameters:
4188: + dm - The `DM`
4189: . fStart - The first face to include
4190: . fEnd - The first face to exclude
4191: . faceGeometry - A local vector with face geometry
4192: - cellGeometry - A local vector with cell geometry
4194: Output Parameters:
4195: + Nface - The number of faces with field values
4196: . fgeom - The extract the face centroid and normal
4197: - vol - The cell volume
4199: Level: developer
4201: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
4202: @*/
4203: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
4204: {
4205: DM dmFace, dmCell;
4206: DMLabel ghostLabel;
4207: const PetscScalar *facegeom, *cellgeom;
4208: PetscInt dim, numFaces = fEnd - fStart, iface, face;
4210: PetscFunctionBegin;
4214: PetscAssertPointer(fgeom, 7);
4215: PetscAssertPointer(vol, 8);
4216: PetscCall(DMGetDimension(dm, &dim));
4217: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4218: PetscCall(VecGetDM(faceGeometry, &dmFace));
4219: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4220: PetscCall(VecGetDM(cellGeometry, &dmCell));
4221: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4222: PetscCall(PetscMalloc1(numFaces, fgeom));
4223: PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
4224: for (face = fStart, iface = 0; face < fEnd; ++face) {
4225: const PetscInt *cells;
4226: PetscFVFaceGeom *fg;
4227: PetscFVCellGeom *cgL, *cgR;
4228: PetscFVFaceGeom *fgeoml = *fgeom;
4229: PetscReal *voll = *vol;
4230: PetscInt ghost, d, nchild, nsupp;
4232: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4233: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4234: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4235: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4236: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4237: PetscCall(DMPlexGetSupport(dm, face, &cells));
4238: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4239: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4240: for (d = 0; d < dim; ++d) {
4241: fgeoml[iface].centroid[d] = fg->centroid[d];
4242: fgeoml[iface].normal[d] = fg->normal[d];
4243: }
4244: voll[iface * 2 + 0] = cgL->volume;
4245: voll[iface * 2 + 1] = cgR->volume;
4246: ++iface;
4247: }
4248: *Nface = iface;
4249: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4250: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4251: PetscFunctionReturn(PETSC_SUCCESS);
4252: }
4254: /*@C
4255: DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
4257: Input Parameters:
4258: + dm - The `DM`
4259: . fStart - The first face to include
4260: . fEnd - The first face to exclude
4261: . faceGeometry - A local vector with face geometry
4262: - cellGeometry - A local vector with cell geometry
4264: Output Parameters:
4265: + Nface - The number of faces with field values
4266: . fgeom - The extract the face centroid and normal
4267: - vol - The cell volume
4269: Level: developer
4271: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4272: @*/
4273: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
4274: {
4275: PetscFunctionBegin;
4276: PetscCall(PetscFree(*fgeom));
4277: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
4278: PetscFunctionReturn(PETSC_SUCCESS);
4279: }
4281: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4282: {
4283: char composeStr[33] = {0};
4284: PetscObjectId id;
4285: PetscContainer container;
4287: PetscFunctionBegin;
4288: PetscCall(PetscObjectGetId((PetscObject)quad, &id));
4289: PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
4290: PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
4291: if (container) {
4292: PetscCall(PetscContainerGetPointer(container, (void **)geom));
4293: } else {
4294: PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
4295: PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
4296: PetscCall(PetscContainerSetPointer(container, (void *)*geom));
4297: PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
4298: PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
4299: PetscCall(PetscContainerDestroy(&container));
4300: }
4301: PetscFunctionReturn(PETSC_SUCCESS);
4302: }
4304: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4305: {
4306: PetscFunctionBegin;
4307: *geom = NULL;
4308: PetscFunctionReturn(PETSC_SUCCESS);
4309: }
4311: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
4312: {
4313: DM_Plex *mesh = (DM_Plex *)dm->data;
4314: const char *name = "Residual";
4315: DM dmAux = NULL;
4316: DMLabel ghostLabel = NULL;
4317: PetscDS prob = NULL;
4318: PetscDS probAux = NULL;
4319: PetscBool useFEM = PETSC_FALSE;
4320: PetscBool isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4321: DMField coordField = NULL;
4322: Vec locA;
4323: PetscScalar *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
4324: IS chunkIS;
4325: const PetscInt *cells;
4326: PetscInt cStart, cEnd, numCells;
4327: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
4328: PetscInt maxDegree = PETSC_INT_MAX;
4329: PetscFormKey key;
4330: PetscQuadrature affineQuad = NULL, *quads = NULL;
4331: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
4333: PetscFunctionBegin;
4334: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4335: /* FEM+FVM */
4336: /* 1: Get sizes from dm and dmAux */
4337: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4338: PetscCall(DMGetDS(dm, &prob));
4339: PetscCall(PetscDSGetNumFields(prob, &Nf));
4340: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4341: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4342: if (locA) {
4343: PetscCall(VecGetDM(locA, &dmAux));
4344: PetscCall(DMGetDS(dmAux, &probAux));
4345: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4346: }
4347: /* 2: Get geometric data */
4348: for (f = 0; f < Nf; ++f) {
4349: PetscObject obj;
4350: PetscClassId id;
4351: PetscBool fimp;
4353: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4354: if (isImplicit != fimp) continue;
4355: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4356: PetscCall(PetscObjectGetClassId(obj, &id));
4357: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4358: PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4359: }
4360: if (useFEM) {
4361: PetscCall(DMGetCoordinateField(dm, &coordField));
4362: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4363: if (maxDegree <= 1) {
4364: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4365: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4366: } else {
4367: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4368: for (f = 0; f < Nf; ++f) {
4369: PetscObject obj;
4370: PetscClassId id;
4371: PetscBool fimp;
4373: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4374: if (isImplicit != fimp) continue;
4375: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4376: PetscCall(PetscObjectGetClassId(obj, &id));
4377: if (id == PETSCFE_CLASSID) {
4378: PetscFE fe = (PetscFE)obj;
4380: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4381: PetscCall(PetscObjectReference((PetscObject)quads[f]));
4382: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4383: }
4384: }
4385: }
4386: }
4387: /* Loop over chunks */
4388: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4389: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4390: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4391: numCells = cEnd - cStart;
4392: numChunks = 1;
4393: cellChunkSize = numCells / numChunks;
4394: numChunks = PetscMin(1, numCells);
4395: key.label = NULL;
4396: key.value = 0;
4397: key.part = 0;
4398: for (chunk = 0; chunk < numChunks; ++chunk) {
4399: PetscScalar *elemVec, *fluxL = NULL, *fluxR = NULL;
4400: PetscReal *vol = NULL;
4401: PetscFVFaceGeom *fgeom = NULL;
4402: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4403: PetscInt numFaces = 0;
4405: /* Extract field coefficients */
4406: if (useFEM) {
4407: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4408: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4409: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4410: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4411: }
4412: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4413: /* Loop over fields */
4414: for (f = 0; f < Nf; ++f) {
4415: PetscObject obj;
4416: PetscClassId id;
4417: PetscBool fimp;
4418: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4420: key.field = f;
4421: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4422: if (isImplicit != fimp) continue;
4423: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4424: PetscCall(PetscObjectGetClassId(obj, &id));
4425: if (id == PETSCFE_CLASSID) {
4426: PetscFE fe = (PetscFE)obj;
4427: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
4428: PetscFEGeom *chunkGeom = NULL;
4429: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4430: PetscInt Nq, Nb;
4432: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4433: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4434: PetscCall(PetscFEGetDimension(fe, &Nb));
4435: blockSize = Nb;
4436: batchSize = numBlocks * blockSize;
4437: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4438: numChunks = numCells / (numBatches * batchSize);
4439: Ne = numChunks * numBatches * batchSize;
4440: Nr = numCells % (numBatches * batchSize);
4441: offset = numCells - Nr;
4442: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4443: /* 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) */
4444: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4445: PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4446: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4447: PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4448: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4449: } else if (id == PETSCFV_CLASSID) {
4450: PetscFV fv = (PetscFV)obj;
4452: Ne = numFaces;
4453: /* Riemann solve over faces (need fields at face centroids) */
4454: /* We need to evaluate FE fields at those coordinates */
4455: PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4456: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4457: }
4458: /* Loop over domain */
4459: if (useFEM) {
4460: /* Add elemVec to locX */
4461: for (c = cS; c < cE; ++c) {
4462: const PetscInt cell = cells ? cells[c] : c;
4463: const PetscInt cind = c - cStart;
4465: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4466: if (ghostLabel) {
4467: PetscInt ghostVal;
4469: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4470: if (ghostVal > 0) continue;
4471: }
4472: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4473: }
4474: }
4475: /* Handle time derivative */
4476: if (locX_t) {
4477: PetscScalar *x_t, *fa;
4479: PetscCall(VecGetArray(locF, &fa));
4480: PetscCall(VecGetArray(locX_t, &x_t));
4481: for (f = 0; f < Nf; ++f) {
4482: PetscFV fv;
4483: PetscObject obj;
4484: PetscClassId id;
4485: PetscInt pdim, d;
4487: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4488: PetscCall(PetscObjectGetClassId(obj, &id));
4489: if (id != PETSCFV_CLASSID) continue;
4490: fv = (PetscFV)obj;
4491: PetscCall(PetscFVGetNumComponents(fv, &pdim));
4492: for (c = cS; c < cE; ++c) {
4493: const PetscInt cell = cells ? cells[c] : c;
4494: PetscScalar *u_t, *r;
4496: if (ghostLabel) {
4497: PetscInt ghostVal;
4499: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4500: if (ghostVal > 0) continue;
4501: }
4502: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4503: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4504: for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4505: }
4506: }
4507: PetscCall(VecRestoreArray(locX_t, &x_t));
4508: PetscCall(VecRestoreArray(locF, &fa));
4509: }
4510: if (useFEM) {
4511: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4512: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4513: }
4514: }
4515: if (useFEM) PetscCall(ISDestroy(&chunkIS));
4516: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4517: /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
4518: if (useFEM) {
4519: if (maxDegree <= 1) {
4520: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4521: PetscCall(PetscQuadratureDestroy(&affineQuad));
4522: } else {
4523: for (f = 0; f < Nf; ++f) {
4524: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4525: PetscCall(PetscQuadratureDestroy(&quads[f]));
4526: }
4527: PetscCall(PetscFree2(quads, geoms));
4528: }
4529: }
4530: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4531: PetscFunctionReturn(PETSC_SUCCESS);
4532: }
4534: /*
4535: 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
4537: X - The local solution vector
4538: X_t - The local solution time derivative vector, or NULL
4539: */
4540: 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, void *ctx)
4541: {
4542: DM_Plex *mesh = (DM_Plex *)dm->data;
4543: const char *name = "Jacobian", *nameP = "JacobianPre";
4544: DM dmAux = NULL;
4545: PetscDS prob, probAux = NULL;
4546: PetscSection sectionAux = NULL;
4547: Vec A;
4548: DMField coordField;
4549: PetscFEGeom *cgeomFEM;
4550: PetscQuadrature qGeom = NULL;
4551: Mat J = Jac, JP = JacP;
4552: PetscScalar *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4553: PetscBool hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4554: const PetscInt *cells;
4555: PetscFormKey key;
4556: PetscInt Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
4558: PetscFunctionBegin;
4559: PetscCall(ISGetLocalSize(cellIS, &numCells));
4560: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4561: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4562: PetscCall(DMGetDS(dm, &prob));
4563: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4564: if (A) {
4565: PetscCall(VecGetDM(A, &dmAux));
4566: PetscCall(DMGetLocalSection(dmAux, §ionAux));
4567: PetscCall(DMGetDS(dmAux, &probAux));
4568: }
4569: /* Get flags */
4570: PetscCall(PetscDSGetNumFields(prob, &Nf));
4571: PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4572: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4573: PetscObject disc;
4574: PetscClassId id;
4575: PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4576: PetscCall(PetscObjectGetClassId(disc, &id));
4577: if (id == PETSCFE_CLASSID) {
4578: isFE[fieldI] = PETSC_TRUE;
4579: } else if (id == PETSCFV_CLASSID) {
4580: hasFV = PETSC_TRUE;
4581: isFE[fieldI] = PETSC_FALSE;
4582: }
4583: }
4584: PetscCall(PetscDSHasJacobian(prob, &hasJac));
4585: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4586: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4587: assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4588: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4589: if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4590: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4591: if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4592: /* Compute batch sizes */
4593: if (isFE[0]) {
4594: PetscFE fe;
4595: PetscQuadrature q;
4596: PetscInt numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
4598: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4599: PetscCall(PetscFEGetQuadrature(fe, &q));
4600: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4601: PetscCall(PetscFEGetDimension(fe, &Nb));
4602: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4603: blockSize = Nb * numQuadPoints;
4604: batchSize = numBlocks * blockSize;
4605: chunkSize = numBatches * batchSize;
4606: numChunks = numCells / chunkSize + numCells % chunkSize;
4607: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4608: } else {
4609: chunkSize = numCells;
4610: numChunks = 1;
4611: }
4612: /* Get work space */
4613: wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4614: PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4615: PetscCall(PetscArrayzero(work, wsz));
4616: off = 0;
4617: u = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4618: u_t = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4619: a = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4620: elemMat = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4621: elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4622: elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4623: PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4624: /* Setup geometry */
4625: PetscCall(DMGetCoordinateField(dm, &coordField));
4626: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4627: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4628: if (!qGeom) {
4629: PetscFE fe;
4631: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4632: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4633: PetscCall(PetscObjectReference((PetscObject)qGeom));
4634: }
4635: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4636: /* Compute volume integrals */
4637: if (assembleJac) PetscCall(MatZeroEntries(J));
4638: PetscCall(MatZeroEntries(JP));
4639: key.label = NULL;
4640: key.value = 0;
4641: key.part = 0;
4642: for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4643: const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4644: PetscInt c;
4646: /* Extract values */
4647: for (c = 0; c < Ncell; ++c) {
4648: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4649: PetscScalar *x = NULL, *x_t = NULL;
4650: PetscInt i;
4652: if (X) {
4653: PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4654: for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4655: PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4656: }
4657: if (X_t) {
4658: PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4659: for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4660: PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4661: }
4662: if (dmAux) {
4663: PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4664: for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4665: PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4666: }
4667: }
4668: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4669: PetscFE fe;
4670: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4671: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4672: key.field = fieldI * Nf + fieldJ;
4673: if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4674: if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4675: if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4676: }
4677: /* For finite volume, add the identity */
4678: if (!isFE[fieldI]) {
4679: PetscFV fv;
4680: PetscInt eOffset = 0, Nc, fc, foff;
4682: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4683: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4684: PetscCall(PetscFVGetNumComponents(fv, &Nc));
4685: for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4686: for (fc = 0; fc < Nc; ++fc) {
4687: const PetscInt i = foff + fc;
4688: if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4689: if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4690: }
4691: }
4692: }
4693: }
4694: /* Add contribution from X_t */
4695: if (hasDyn) {
4696: for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4697: }
4698: /* Insert values into matrix */
4699: for (c = 0; c < Ncell; ++c) {
4700: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4701: if (mesh->printFEM > 1) {
4702: if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4703: if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4704: }
4705: if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4706: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4707: }
4708: }
4709: /* Cleanup */
4710: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4711: PetscCall(PetscQuadratureDestroy(&qGeom));
4712: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4713: PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4714: 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));
4715: /* Compute boundary integrals */
4716: /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4717: /* Assemble matrix */
4718: if (assembleJac) {
4719: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4720: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4721: }
4722: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4723: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4724: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4725: PetscFunctionReturn(PETSC_SUCCESS);
4726: }
4728: /* FEM Assembly Function */
4730: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4731: {
4732: PetscBool isPlex;
4734: PetscFunctionBegin;
4735: PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4736: if (isPlex) {
4737: *plex = dm;
4738: PetscCall(PetscObjectReference((PetscObject)dm));
4739: } else {
4740: PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4741: if (!*plex) {
4742: PetscCall(DMConvert(dm, DMPLEX, plex));
4743: PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4744: } else {
4745: PetscCall(PetscObjectReference((PetscObject)*plex));
4746: }
4747: if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4748: }
4749: PetscFunctionReturn(PETSC_SUCCESS);
4750: }
4752: /*@
4753: DMPlexGetGeometryFVM - Return precomputed geometric data
4755: Collective
4757: Input Parameter:
4758: . dm - The `DM`
4760: Output Parameters:
4761: + facegeom - The values precomputed from face geometry
4762: . cellgeom - The values precomputed from cell geometry
4763: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell
4765: Level: developer
4767: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4768: @*/
4769: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4770: {
4771: DM plex;
4773: PetscFunctionBegin;
4775: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4776: PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4777: if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4778: PetscCall(DMDestroy(&plex));
4779: PetscFunctionReturn(PETSC_SUCCESS);
4780: }
4782: /*@
4783: DMPlexGetGradientDM - Return gradient data layout
4785: Collective
4787: Input Parameters:
4788: + dm - The `DM`
4789: - fv - The `PetscFV`
4791: Output Parameter:
4792: . dmGrad - The layout for gradient values
4794: Level: developer
4796: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4797: @*/
4798: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4799: {
4800: DM plex;
4801: PetscBool computeGradients;
4803: PetscFunctionBegin;
4806: PetscAssertPointer(dmGrad, 3);
4807: PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4808: if (!computeGradients) {
4809: *dmGrad = NULL;
4810: PetscFunctionReturn(PETSC_SUCCESS);
4811: }
4812: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4813: PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4814: PetscCall(DMDestroy(&plex));
4815: PetscFunctionReturn(PETSC_SUCCESS);
4816: }
4818: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4819: {
4820: DM_Plex *mesh = (DM_Plex *)dm->data;
4821: DM plex = NULL, plexA = NULL;
4822: const char *name = "BdResidual";
4823: DMEnclosureType encAux;
4824: PetscDS prob, probAux = NULL;
4825: PetscSection section, sectionAux = NULL;
4826: Vec locA = NULL;
4827: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4828: PetscInt totDim, totDimAux = 0;
4830: PetscFunctionBegin;
4831: PetscCall(DMConvert(dm, DMPLEX, &plex));
4832: PetscCall(DMGetLocalSection(dm, §ion));
4833: PetscCall(DMGetDS(dm, &prob));
4834: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4835: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4836: if (locA) {
4837: DM dmAux;
4839: PetscCall(VecGetDM(locA, &dmAux));
4840: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4841: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4842: PetscCall(DMGetDS(plexA, &probAux));
4843: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4844: PetscCall(DMGetLocalSection(plexA, §ionAux));
4845: }
4846: {
4847: PetscFEGeom *fgeom;
4848: PetscInt maxDegree;
4849: PetscQuadrature qGeom = NULL;
4850: IS pointIS;
4851: const PetscInt *points;
4852: PetscInt numFaces, face, Nq;
4854: PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4855: if (!pointIS) goto end; /* No points with that id on this process */
4856: {
4857: IS isectIS;
4859: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4860: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4861: PetscCall(ISDestroy(&pointIS));
4862: pointIS = isectIS;
4863: }
4864: PetscCall(ISGetLocalSize(pointIS, &numFaces));
4865: PetscCall(ISGetIndices(pointIS, &points));
4866: PetscCall(PetscMalloc4(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, numFaces * totDim, &elemVec, (locA ? (size_t)numFaces * totDimAux : 0), &a));
4867: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4868: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4869: if (!qGeom) {
4870: PetscFE fe;
4872: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4873: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4874: PetscCall(PetscObjectReference((PetscObject)qGeom));
4875: }
4876: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4877: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4878: for (face = 0; face < numFaces; ++face) {
4879: const PetscInt point = points[face], *support;
4880: PetscScalar *x = NULL;
4881: PetscInt i;
4883: PetscCall(DMPlexGetSupport(dm, point, &support));
4884: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4885: for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4886: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4887: if (locX_t) {
4888: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4889: for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4890: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4891: }
4892: if (locA) {
4893: PetscInt subp;
4895: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4896: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4897: for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4898: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4899: }
4900: }
4901: PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4902: {
4903: PetscFE fe;
4904: PetscInt Nb;
4905: PetscFEGeom *chunkGeom = NULL;
4906: /* Conforming batches */
4907: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4908: /* Remainder */
4909: PetscInt Nr, offset;
4911: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4912: PetscCall(PetscFEGetDimension(fe, &Nb));
4913: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4914: /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4915: blockSize = Nb;
4916: batchSize = numBlocks * blockSize;
4917: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4918: numChunks = numFaces / (numBatches * batchSize);
4919: Ne = numChunks * numBatches * batchSize;
4920: Nr = numFaces % (numBatches * batchSize);
4921: offset = numFaces - Nr;
4922: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4923: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4924: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4925: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4926: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
4927: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4928: }
4929: for (face = 0; face < numFaces; ++face) {
4930: const PetscInt point = points[face], *support;
4932: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, name, totDim, &elemVec[face * totDim]));
4933: PetscCall(DMPlexGetSupport(plex, point, &support));
4934: PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
4935: }
4936: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4937: PetscCall(PetscQuadratureDestroy(&qGeom));
4938: PetscCall(ISRestoreIndices(pointIS, &points));
4939: PetscCall(ISDestroy(&pointIS));
4940: PetscCall(PetscFree4(u, u_t, elemVec, a));
4941: }
4942: end:
4943: if (mesh->printFEM) {
4944: PetscSection s;
4945: Vec locFbc;
4946: PetscInt pStart, pEnd, maxDof;
4947: PetscScalar *zeroes;
4949: PetscCall(DMGetLocalSection(dm, &s));
4950: PetscCall(VecDuplicate(locF, &locFbc));
4951: PetscCall(VecCopy(locF, locFbc));
4952: PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
4953: PetscCall(PetscSectionGetMaxDof(s, &maxDof));
4954: PetscCall(PetscCalloc1(maxDof, &zeroes));
4955: for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES));
4956: PetscCall(PetscFree(zeroes));
4957: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
4958: PetscCall(VecDestroy(&locFbc));
4959: }
4960: PetscCall(DMDestroy(&plex));
4961: PetscCall(DMDestroy(&plexA));
4962: PetscFunctionReturn(PETSC_SUCCESS);
4963: }
4965: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4966: {
4967: DMField coordField;
4968: DMLabel depthLabel;
4969: IS facetIS;
4970: PetscInt dim;
4972: PetscFunctionBegin;
4973: PetscCall(DMGetDimension(dm, &dim));
4974: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4975: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4976: PetscCall(DMGetCoordinateField(dm, &coordField));
4977: PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4978: PetscCall(ISDestroy(&facetIS));
4979: PetscFunctionReturn(PETSC_SUCCESS);
4980: }
4982: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4983: {
4984: PetscDS prob;
4985: PetscInt numBd, bd;
4986: DMField coordField = NULL;
4987: IS facetIS = NULL;
4988: DMLabel depthLabel;
4989: PetscInt dim;
4991: PetscFunctionBegin;
4992: PetscCall(DMGetDS(dm, &prob));
4993: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4994: PetscCall(DMGetDimension(dm, &dim));
4995: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4996: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4997: for (bd = 0; bd < numBd; ++bd) {
4998: PetscWeakForm wf;
4999: DMBoundaryConditionType type;
5000: DMLabel label;
5001: const PetscInt *values;
5002: PetscInt field, numValues, v;
5003: PetscObject obj;
5004: PetscClassId id;
5005: PetscFormKey key;
5007: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
5008: if (type & DM_BC_ESSENTIAL) continue;
5009: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
5010: PetscCall(PetscObjectGetClassId(obj, &id));
5011: if (id != PETSCFE_CLASSID) continue;
5012: if (!facetIS) {
5013: DMLabel depthLabel;
5014: PetscInt dim;
5016: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5017: PetscCall(DMGetDimension(dm, &dim));
5018: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5019: }
5020: PetscCall(DMGetCoordinateField(dm, &coordField));
5021: for (v = 0; v < numValues; ++v) {
5022: key.label = label;
5023: key.value = values[v];
5024: key.field = field;
5025: key.part = 0;
5026: PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
5027: }
5028: }
5029: PetscCall(ISDestroy(&facetIS));
5030: PetscFunctionReturn(PETSC_SUCCESS);
5031: }
5033: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5034: {
5035: DM_Plex *mesh = (DM_Plex *)dm->data;
5036: const char *name = "Residual";
5037: DM dmAux = NULL;
5038: DM dmGrad = NULL;
5039: DMLabel ghostLabel = NULL;
5040: PetscDS ds = NULL;
5041: PetscDS dsAux = NULL;
5042: PetscSection section = NULL;
5043: PetscBool useFEM = PETSC_FALSE;
5044: PetscBool useFVM = PETSC_FALSE;
5045: PetscBool isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
5046: PetscFV fvm = NULL;
5047: DMField coordField = NULL;
5048: Vec locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
5049: PetscScalar *u = NULL, *u_t, *a, *uL, *uR;
5050: IS chunkIS;
5051: const PetscInt *cells;
5052: PetscInt cStart, cEnd, numCells;
5053: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
5054: PetscInt maxDegree = PETSC_INT_MAX;
5055: PetscQuadrature affineQuad = NULL, *quads = NULL;
5056: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
5058: PetscFunctionBegin;
5059: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5060: if (!cellIS) goto end;
5061: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5062: if (cStart >= cEnd) goto end;
5063: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5064: /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
5065: /* FEM+FVM */
5066: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
5067: /* 1: Get sizes from dm and dmAux */
5068: PetscCall(DMGetLocalSection(dm, §ion));
5069: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5070: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
5071: PetscCall(PetscDSGetNumFields(ds, &Nf));
5072: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5073: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
5074: if (locA) {
5075: PetscInt subcell;
5076: PetscCall(VecGetDM(locA, &dmAux));
5077: PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
5078: PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
5079: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5080: }
5081: /* 2: Get geometric data */
5082: for (f = 0; f < Nf; ++f) {
5083: PetscObject obj;
5084: PetscClassId id;
5085: PetscBool fimp;
5087: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5088: if (isImplicit != fimp) continue;
5089: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5090: PetscCall(PetscObjectGetClassId(obj, &id));
5091: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
5092: if (id == PETSCFV_CLASSID) {
5093: useFVM = PETSC_TRUE;
5094: fvm = (PetscFV)obj;
5095: }
5096: }
5097: if (useFEM) {
5098: PetscCall(DMGetCoordinateField(dm, &coordField));
5099: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5100: if (maxDegree <= 1) {
5101: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
5102: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5103: } else {
5104: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5105: for (f = 0; f < Nf; ++f) {
5106: PetscObject obj;
5107: PetscClassId id;
5108: PetscBool fimp;
5110: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5111: if (isImplicit != fimp) continue;
5112: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5113: PetscCall(PetscObjectGetClassId(obj, &id));
5114: if (id == PETSCFE_CLASSID) {
5115: PetscFE fe = (PetscFE)obj;
5117: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5118: PetscCall(PetscObjectReference((PetscObject)quads[f]));
5119: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5120: }
5121: }
5122: }
5123: }
5124: // Handle non-essential (e.g. outflow) boundary values
5125: if (useFVM) {
5126: PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad));
5127: PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
5128: PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
5129: }
5130: /* Loop over chunks */
5131: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
5132: numCells = cEnd - cStart;
5133: numChunks = 1;
5134: cellChunkSize = numCells / numChunks;
5135: faceChunkSize = (fEnd - fStart) / numChunks;
5136: numChunks = PetscMin(1, numCells);
5137: for (chunk = 0; chunk < numChunks; ++chunk) {
5138: PetscScalar *elemVec, *fluxL, *fluxR;
5139: PetscReal *vol;
5140: PetscFVFaceGeom *fgeom;
5141: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5142: PetscInt fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;
5144: /* Extract field coefficients */
5145: if (useFEM) {
5146: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
5147: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5148: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5149: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
5150: }
5151: if (useFVM) {
5152: PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5153: PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5154: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5155: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5156: PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
5157: PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
5158: }
5159: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
5160: /* Loop over fields */
5161: for (f = 0; f < Nf; ++f) {
5162: PetscObject obj;
5163: PetscClassId id;
5164: PetscBool fimp;
5165: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
5167: key.field = f;
5168: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
5169: if (isImplicit != fimp) continue;
5170: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5171: PetscCall(PetscObjectGetClassId(obj, &id));
5172: if (id == PETSCFE_CLASSID) {
5173: PetscFE fe = (PetscFE)obj;
5174: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
5175: PetscFEGeom *chunkGeom = NULL;
5176: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5177: PetscInt Nq, Nb;
5179: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5180: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5181: PetscCall(PetscFEGetDimension(fe, &Nb));
5182: blockSize = Nb;
5183: batchSize = numBlocks * blockSize;
5184: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5185: numChunks = numCells / (numBatches * batchSize);
5186: Ne = numChunks * numBatches * batchSize;
5187: Nr = numCells % (numBatches * batchSize);
5188: offset = numCells - Nr;
5189: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
5190: /* 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) */
5191: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
5192: PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
5193: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
5194: PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
5195: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
5196: } else if (id == PETSCFV_CLASSID) {
5197: PetscFV fv = (PetscFV)obj;
5199: Ne = numFaces;
5200: /* Riemann solve over faces (need fields at face centroids) */
5201: /* We need to evaluate FE fields at those coordinates */
5202: PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
5203: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
5204: }
5205: /* Loop over domain */
5206: if (useFEM) {
5207: /* Add elemVec to locX */
5208: for (c = cS; c < cE; ++c) {
5209: const PetscInt cell = cells ? cells[c] : c;
5210: const PetscInt cind = c - cStart;
5212: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
5213: if (ghostLabel) {
5214: PetscInt ghostVal;
5216: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5217: if (ghostVal > 0) continue;
5218: }
5219: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5220: }
5221: }
5222: if (useFVM) {
5223: PetscScalar *fa;
5224: PetscInt iface;
5226: PetscCall(VecGetArray(locF, &fa));
5227: for (f = 0; f < Nf; ++f) {
5228: PetscFV fv;
5229: PetscObject obj;
5230: PetscClassId id;
5231: PetscInt cdim, foff, pdim;
5233: PetscCall(DMGetCoordinateDim(dm, &cdim));
5234: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5235: PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
5236: PetscCall(PetscObjectGetClassId(obj, &id));
5237: if (id != PETSCFV_CLASSID) continue;
5238: fv = (PetscFV)obj;
5239: PetscCall(PetscFVGetNumComponents(fv, &pdim));
5240: /* Accumulate fluxes to cells */
5241: for (face = fS, iface = 0; face < fE; ++face) {
5242: const PetscInt *scells;
5243: PetscScalar *fL = NULL, *fR = NULL;
5244: PetscInt ghost, d, nsupp, nchild;
5246: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
5247: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
5248: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
5249: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
5250: PetscCall(DMPlexGetSupport(dm, face, &scells));
5251: PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
5252: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
5253: PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
5254: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
5255: if (mesh->printFVM > 1) {
5256: PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal));
5257: PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff]));
5258: PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff]));
5259: PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff]));
5260: PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff]));
5261: }
5262: for (d = 0; d < pdim; ++d) {
5263: if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
5264: if (fR) fR[d] += fluxR[iface * totDim + foff + d];
5265: }
5266: ++iface;
5267: }
5268: }
5269: PetscCall(VecRestoreArray(locF, &fa));
5270: }
5271: /* Handle time derivative */
5272: if (locX_t) {
5273: PetscScalar *x_t, *fa;
5275: PetscCall(VecGetArray(locF, &fa));
5276: PetscCall(VecGetArray(locX_t, &x_t));
5277: for (f = 0; f < Nf; ++f) {
5278: PetscFV fv;
5279: PetscObject obj;
5280: PetscClassId id;
5281: PetscInt pdim, d;
5283: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5284: PetscCall(PetscObjectGetClassId(obj, &id));
5285: if (id != PETSCFV_CLASSID) continue;
5286: fv = (PetscFV)obj;
5287: PetscCall(PetscFVGetNumComponents(fv, &pdim));
5288: for (c = cS; c < cE; ++c) {
5289: const PetscInt cell = cells ? cells[c] : c;
5290: PetscScalar *u_t, *r;
5292: if (ghostLabel) {
5293: PetscInt ghostVal;
5295: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5296: if (ghostVal > 0) continue;
5297: }
5298: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
5299: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
5300: for (d = 0; d < pdim; ++d) r[d] += u_t[d];
5301: }
5302: }
5303: PetscCall(VecRestoreArray(locX_t, &x_t));
5304: PetscCall(VecRestoreArray(locF, &fa));
5305: }
5306: if (useFEM) {
5307: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5308: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5309: }
5310: if (useFVM) {
5311: PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5312: PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5313: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5314: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5315: if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
5316: }
5317: }
5318: if (useFEM) PetscCall(ISDestroy(&chunkIS));
5319: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5321: if (useFEM) {
5322: PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user));
5324: if (maxDegree <= 1) {
5325: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5326: PetscCall(PetscQuadratureDestroy(&affineQuad));
5327: } else {
5328: for (f = 0; f < Nf; ++f) {
5329: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5330: PetscCall(PetscQuadratureDestroy(&quads[f]));
5331: }
5332: PetscCall(PetscFree2(quads, geoms));
5333: }
5334: }
5336: /* FEM */
5337: /* 1: Get sizes from dm and dmAux */
5338: /* 2: Get geometric data */
5339: /* 3: Handle boundary values */
5340: /* 4: Loop over domain */
5341: /* Extract coefficients */
5342: /* Loop over fields */
5343: /* Set tiling for FE*/
5344: /* Integrate FE residual to get elemVec */
5345: /* Loop over subdomain */
5346: /* Loop over quad points */
5347: /* Transform coords to real space */
5348: /* Evaluate field and aux fields at point */
5349: /* Evaluate residual at point */
5350: /* Transform residual to real space */
5351: /* Add residual to elemVec */
5352: /* Loop over domain */
5353: /* Add elemVec to locX */
5355: /* FVM */
5356: /* Get geometric data */
5357: /* If using gradients */
5358: /* Compute gradient data */
5359: /* Loop over domain faces */
5360: /* Count computational faces */
5361: /* Reconstruct cell gradient */
5362: /* Loop over domain cells */
5363: /* Limit cell gradients */
5364: /* Handle boundary values */
5365: /* Loop over domain faces */
5366: /* Read out field, centroid, normal, volume for each side of face */
5367: /* Riemann solve over faces */
5368: /* Loop over domain faces */
5369: /* Accumulate fluxes to cells */
5370: /* TODO Change printFEM to printDisc here */
5371: if (mesh->printFEM) {
5372: Vec locFbc;
5373: PetscInt pStart, pEnd, p, maxDof;
5374: PetscScalar *zeroes;
5376: PetscCall(VecDuplicate(locF, &locFbc));
5377: PetscCall(VecCopy(locF, locFbc));
5378: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5379: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5380: PetscCall(PetscCalloc1(maxDof, &zeroes));
5381: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5382: PetscCall(PetscFree(zeroes));
5383: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5384: PetscCall(VecDestroy(&locFbc));
5385: }
5386: end:
5387: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5388: PetscFunctionReturn(PETSC_SUCCESS);
5389: }
5391: /*
5392: 1) Allow multiple kernels for BdResidual for hybrid DS
5394: DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux
5396: DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
5397: - I think I just need to replace a[] with the closure from each face
5399: 4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
5400: */
5401: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5402: {
5403: DM_Plex *mesh = (DM_Plex *)dm->data;
5404: const char *name = "Hybrid Residual";
5405: DM dmAux[3] = {NULL, NULL, NULL};
5406: DMLabel ghostLabel = NULL;
5407: PetscDS ds = NULL;
5408: PetscDS dsIn = NULL;
5409: PetscDS dsAux[3] = {NULL, NULL, NULL};
5410: Vec locA[3] = {NULL, NULL, NULL};
5411: DM dmScale[3] = {NULL, NULL, NULL};
5412: PetscDS dsScale[3] = {NULL, NULL, NULL};
5413: Vec locS[3] = {NULL, NULL, NULL};
5414: PetscSection section = NULL;
5415: DMField coordField = NULL;
5416: PetscScalar *a[3] = {NULL, NULL, NULL};
5417: PetscScalar *s[3] = {NULL, NULL, NULL};
5418: PetscScalar *u = NULL, *u_t;
5419: PetscScalar *elemVecNeg, *elemVecPos, *elemVecCoh;
5420: IS chunkIS;
5421: const PetscInt *cells;
5422: PetscInt *faces;
5423: PetscInt cStart, cEnd, numCells;
5424: PetscInt Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5425: PetscInt maxDegree = PETSC_INT_MAX;
5426: PetscQuadrature affineQuad = NULL, *quads = NULL;
5427: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
5429: PetscFunctionBegin;
5430: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5431: if (!cellIS) goto end;
5432: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5433: PetscCall(ISGetLocalSize(cellIS, &numCells));
5434: if (cStart >= cEnd) goto end;
5435: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5436: const char *name;
5437: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5438: 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);
5439: }
5440: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5441: /* FEM */
5442: /* 1: Get sizes from dm and dmAux */
5443: PetscCall(DMGetLocalSection(dm, §ion));
5444: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5445: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5446: PetscCall(PetscDSGetNumFields(ds, &Nf));
5447: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5448: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5449: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5450: if (locA[2]) {
5451: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5453: PetscCall(VecGetDM(locA[2], &dmAux[2]));
5454: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5455: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5456: {
5457: const PetscInt *cone;
5458: PetscInt c;
5460: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5461: for (c = 0; c < 2; ++c) {
5462: const PetscInt *support;
5463: PetscInt ssize, s;
5465: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5466: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5467: 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);
5468: if (support[0] == cellStart) s = 1;
5469: else if (support[1] == cellStart) s = 0;
5470: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5471: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5472: 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);
5473: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5474: else dmAux[c] = dmAux[2];
5475: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5476: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5477: }
5478: }
5479: }
5480: /* Handle mass matrix scaling
5481: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5482: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5483: if (locS[2]) {
5484: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5485: PetscInt Nb, Nbs;
5487: PetscCall(VecGetDM(locS[2], &dmScale[2]));
5488: PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5489: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5490: // BRAD: This is not set correctly
5491: key[2].field = 2;
5492: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5493: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5494: 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);
5495: {
5496: const PetscInt *cone;
5497: PetscInt c;
5499: locS[1] = locS[0] = locS[2];
5500: dmScale[1] = dmScale[0] = dmScale[2];
5501: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5502: for (c = 0; c < 2; ++c) {
5503: const PetscInt *support;
5504: PetscInt ssize, s;
5506: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5507: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5508: 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);
5509: if (support[0] == cellStart) s = 1;
5510: else if (support[1] == cellStart) s = 0;
5511: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5512: PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5513: PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5514: }
5515: }
5516: }
5517: /* 2: Setup geometric data */
5518: PetscCall(DMGetCoordinateField(dm, &coordField));
5519: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5520: if (maxDegree > 1) {
5521: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5522: for (f = 0; f < Nf; ++f) {
5523: PetscFE fe;
5525: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5526: if (fe) {
5527: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5528: PetscCall(PetscObjectReference((PetscObject)quads[f]));
5529: }
5530: }
5531: }
5532: /* Loop over chunks */
5533: cellChunkSize = numCells;
5534: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5535: PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5536: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5537: /* Extract field coefficients */
5538: /* NOTE This needs the end cap faces to have identical orientations */
5539: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5540: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5541: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5542: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5543: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5544: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5545: for (chunk = 0; chunk < numChunks; ++chunk) {
5546: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5548: PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5549: PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5550: PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5551: /* Get faces */
5552: for (c = cS; c < cE; ++c) {
5553: const PetscInt cell = cells ? cells[c] : c;
5554: const PetscInt *cone;
5555: PetscCall(DMPlexGetCone(dm, cell, &cone));
5556: faces[(c - cS) * 2 + 0] = cone[0];
5557: faces[(c - cS) * 2 + 1] = cone[1];
5558: }
5559: PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5560: /* Get geometric data */
5561: if (maxDegree <= 1) {
5562: if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5563: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5564: } else {
5565: for (f = 0; f < Nf; ++f) {
5566: if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5567: }
5568: }
5569: /* Loop over fields */
5570: for (f = 0; f < Nf; ++f) {
5571: PetscFE fe;
5572: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
5573: PetscFEGeom *chunkGeom = NULL, *remGeom = NULL;
5574: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5575: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5576: PetscBool isCohesiveField;
5578: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5579: if (!fe) continue;
5580: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5581: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5582: PetscCall(PetscFEGetDimension(fe, &Nb));
5583: blockSize = Nb;
5584: batchSize = numBlocks * blockSize;
5585: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5586: numChunks = numCells / (numBatches * batchSize);
5587: Ne = numChunks * numBatches * batchSize;
5588: Nr = numCells % (numBatches * batchSize);
5589: offset = numCells - Nr;
5590: PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5591: PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5592: PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5593: chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5594: key[0].field = f;
5595: key[1].field = f;
5596: key[2].field = f;
5597: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5598: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, &elemVecNeg[offset * totDim]));
5599: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
5600: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, &elemVecPos[offset * totDim]));
5601: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5602: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, &elemVecCoh[offset * totDim]));
5603: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5604: PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5605: }
5606: /* Add elemVec to locX */
5607: for (c = cS; c < cE; ++c) {
5608: const PetscInt cell = cells ? cells[c] : c;
5609: const PetscInt cind = c - cStart;
5610: PetscInt i;
5612: /* Scale element values */
5613: if (locS[0]) {
5614: PetscInt Nb, off = cind * totDim, soff = cind * totDimScale[0];
5615: PetscBool cohesive;
5617: for (f = 0; f < Nf; ++f) {
5618: PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5619: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5620: if (f == key[2].field) {
5621: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5622: // No cohesive scaling field is currently input
5623: for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5624: off += Nb;
5625: } else {
5626: const PetscInt N = cohesive ? Nb : Nb * 2;
5628: for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5629: off += N;
5630: }
5631: }
5632: } else {
5633: for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5634: }
5635: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5636: if (ghostLabel) {
5637: PetscInt ghostVal;
5639: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5640: if (ghostVal > 0) continue;
5641: }
5642: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5643: }
5644: }
5645: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5646: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5647: PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5648: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5649: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5650: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5651: PetscCall(PetscFree(faces));
5652: PetscCall(ISDestroy(&chunkIS));
5653: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5654: if (maxDegree <= 1) {
5655: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5656: PetscCall(PetscQuadratureDestroy(&affineQuad));
5657: } else {
5658: for (f = 0; f < Nf; ++f) {
5659: if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5660: if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5661: }
5662: PetscCall(PetscFree2(quads, geoms));
5663: }
5664: if (mesh->printFEM) {
5665: Vec locFbc;
5666: PetscInt pStart, pEnd, p, maxDof;
5667: PetscScalar *zeroes;
5669: PetscCall(VecDuplicate(locF, &locFbc));
5670: PetscCall(VecCopy(locF, locFbc));
5671: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5672: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5673: PetscCall(PetscCalloc1(maxDof, &zeroes));
5674: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5675: PetscCall(PetscFree(zeroes));
5676: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5677: PetscCall(VecDestroy(&locFbc));
5678: }
5679: end:
5680: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5681: PetscFunctionReturn(PETSC_SUCCESS);
5682: }
5684: static PetscErrorCode DMPlexComputeBdJacobian_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP, DMField coordField, IS facetIS)
5685: {
5686: DM_Plex *mesh = (DM_Plex *)dm->data;
5687: DM plex = NULL, plexA = NULL, tdm;
5688: DMEnclosureType encAux;
5689: PetscDS ds, dsAux = NULL;
5690: PetscSection section, sectionAux = NULL;
5691: PetscSection globalSection;
5692: Vec locA = NULL, tv;
5693: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
5694: PetscInt v;
5695: PetscInt Nf, totDim, totDimAux = 0;
5696: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;
5698: PetscFunctionBegin;
5699: PetscCall(DMHasBasisTransform(dm, &transform));
5700: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5701: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5702: PetscCall(DMGetLocalSection(dm, §ion));
5703: PetscCall(DMGetDS(dm, &ds));
5704: PetscCall(PetscDSGetNumFields(ds, &Nf));
5705: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5706: PetscCall(PetscWeakFormHasBdJacobian(wf, &hasJac));
5707: PetscCall(PetscWeakFormHasBdJacobianPreconditioner(wf, &hasPrec));
5708: if (!hasJac && !hasPrec) PetscFunctionReturn(PETSC_SUCCESS);
5709: PetscCall(DMConvert(dm, DMPLEX, &plex));
5710: PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5711: if (locA) {
5712: DM dmAux;
5714: PetscCall(VecGetDM(locA, &dmAux));
5715: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5716: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5717: PetscCall(DMGetDS(plexA, &dsAux));
5718: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
5719: PetscCall(DMGetLocalSection(plexA, §ionAux));
5720: }
5722: PetscCall(DMGetGlobalSection(dm, &globalSection));
5723: for (v = 0; v < numValues; ++v) {
5724: PetscFEGeom *fgeom;
5725: PetscInt maxDegree;
5726: PetscQuadrature qGeom = NULL;
5727: IS pointIS;
5728: const PetscInt *points;
5729: PetscFormKey key;
5730: PetscInt numFaces, face, Nq;
5732: key.label = label;
5733: key.value = values[v];
5734: key.part = 0;
5735: PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5736: if (!pointIS) continue; /* No points with that id on this process */
5737: {
5738: IS isectIS;
5740: /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5741: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5742: PetscCall(ISDestroy(&pointIS));
5743: pointIS = isectIS;
5744: }
5745: PetscCall(ISGetLocalSize(pointIS, &numFaces));
5746: PetscCall(ISGetIndices(pointIS, &points));
5747: 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));
5748: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5749: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5750: if (!qGeom) {
5751: PetscFE fe;
5753: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5754: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5755: PetscCall(PetscObjectReference((PetscObject)qGeom));
5756: }
5757: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5758: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5759: for (face = 0; face < numFaces; ++face) {
5760: const PetscInt point = points[face], *support;
5761: PetscScalar *x = NULL;
5762: PetscInt i;
5764: PetscCall(DMPlexGetSupport(dm, point, &support));
5765: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5766: for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5767: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5768: if (locX_t) {
5769: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5770: for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5771: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5772: }
5773: if (locA) {
5774: PetscInt subp;
5775: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5776: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5777: for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5778: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5779: }
5780: }
5781: if (elemMat) PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5782: if (elemMatP) PetscCall(PetscArrayzero(elemMatP, numFaces * totDim * totDim));
5783: {
5784: PetscFE fe;
5785: PetscInt Nb;
5786: /* Conforming batches */
5787: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5788: /* Remainder */
5789: PetscFEGeom *chunkGeom = NULL;
5790: PetscInt fieldJ, Nr, offset;
5792: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
5793: PetscCall(PetscFEGetDimension(fe, &Nb));
5794: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5795: blockSize = Nb;
5796: batchSize = numBlocks * blockSize;
5797: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5798: numChunks = numFaces / (numBatches * batchSize);
5799: Ne = numChunks * numBatches * batchSize;
5800: Nr = numFaces % (numBatches * batchSize);
5801: offset = numFaces - Nr;
5802: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5803: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5804: key.field = fieldI * Nf + fieldJ;
5805: if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
5806: if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
5807: }
5808: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5809: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5810: key.field = fieldI * Nf + fieldJ;
5811: if (hasJac)
5812: 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]));
5813: if (hasPrec)
5814: 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]));
5815: }
5816: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5817: }
5818: for (face = 0; face < numFaces; ++face) {
5819: const PetscInt point = points[face], *support;
5821: /* Transform to global basis before insertion in Jacobian */
5822: PetscCall(DMPlexGetSupport(plex, point, &support));
5823: if (hasJac && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5824: if (hasPrec && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMatP[face * totDim * totDim]));
5825: if (hasPrec) {
5826: if (hasJac) {
5827: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5828: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5829: }
5830: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
5831: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
5832: } else {
5833: if (hasJac) {
5834: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5835: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5836: }
5837: }
5838: }
5839: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5840: PetscCall(PetscQuadratureDestroy(&qGeom));
5841: PetscCall(ISRestoreIndices(pointIS, &points));
5842: PetscCall(ISDestroy(&pointIS));
5843: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
5844: }
5845: if (plex) PetscCall(DMDestroy(&plex));
5846: if (plexA) PetscCall(DMDestroy(&plexA));
5847: PetscFunctionReturn(PETSC_SUCCESS);
5848: }
5850: PetscErrorCode DMPlexComputeBdJacobianSingle(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt field, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP)
5851: {
5852: DMField coordField;
5853: DMLabel depthLabel;
5854: IS facetIS;
5855: PetscInt dim;
5857: PetscFunctionBegin;
5858: PetscCall(DMGetDimension(dm, &dim));
5859: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5860: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5861: PetscCall(DMGetCoordinateField(dm, &coordField));
5862: PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5863: PetscCall(ISDestroy(&facetIS));
5864: PetscFunctionReturn(PETSC_SUCCESS);
5865: }
5867: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5868: {
5869: PetscDS prob;
5870: PetscInt dim, numBd, bd;
5871: DMLabel depthLabel;
5872: DMField coordField = NULL;
5873: IS facetIS;
5875: PetscFunctionBegin;
5876: PetscCall(DMGetDS(dm, &prob));
5877: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5878: PetscCall(DMGetDimension(dm, &dim));
5879: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5880: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5881: PetscCall(DMGetCoordinateField(dm, &coordField));
5882: for (bd = 0; bd < numBd; ++bd) {
5883: PetscWeakForm wf;
5884: DMBoundaryConditionType type;
5885: DMLabel label;
5886: const PetscInt *values;
5887: PetscInt fieldI, numValues;
5888: PetscObject obj;
5889: PetscClassId id;
5891: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5892: if (type & DM_BC_ESSENTIAL) continue;
5893: PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5894: PetscCall(PetscObjectGetClassId(obj, &id));
5895: if (id != PETSCFE_CLASSID) continue;
5896: PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5897: }
5898: PetscCall(ISDestroy(&facetIS));
5899: PetscFunctionReturn(PETSC_SUCCESS);
5900: }
5902: PetscErrorCode DMPlexComputeJacobian_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *user)
5903: {
5904: DM_Plex *mesh = (DM_Plex *)dm->data;
5905: const char *name = "Jacobian";
5906: DM dmAux = NULL, plex, tdm;
5907: DMEnclosureType encAux;
5908: Vec A, tv;
5909: DMField coordField;
5910: PetscDS prob, probAux = NULL;
5911: PetscSection section, globalSection, sectionAux;
5912: PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5913: const PetscInt *cells;
5914: PetscInt Nf, fieldI, fieldJ;
5915: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5916: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
5918: PetscFunctionBegin;
5919: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5920: PetscCall(DMGetLocalSection(dm, §ion));
5921: PetscCall(DMGetGlobalSection(dm, &globalSection));
5922: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5923: if (A) {
5924: PetscCall(VecGetDM(A, &dmAux));
5925: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5926: PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5927: PetscCall(DMGetLocalSection(plex, §ionAux));
5928: PetscCall(DMGetDS(dmAux, &probAux));
5929: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5930: }
5931: PetscCall(DMGetCoordinateField(dm, &coordField));
5932: if (!cellIS) goto end;
5933: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5934: PetscCall(ISGetLocalSize(cellIS, &numCells));
5935: if (cStart >= cEnd) goto end;
5936: PetscCall(DMHasBasisTransform(dm, &transform));
5937: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5938: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5939: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
5940: PetscCall(PetscDSGetNumFields(prob, &Nf));
5941: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5942: PetscCall(PetscDSHasJacobian(prob, &hasJac));
5943: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5944: /* user passed in the same matrix, avoid double contributions and
5945: only assemble the Jacobian */
5946: if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5947: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5948: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5949: PetscCall(PetscMalloc5(numCells * totDim, &u, (X_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));
5950: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5951: for (c = cStart; c < cEnd; ++c) {
5952: const PetscInt cell = cells ? cells[c] : c;
5953: const PetscInt cind = c - cStart;
5954: PetscScalar *x = NULL, *x_t = NULL;
5955: PetscInt i;
5957: PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5958: for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5959: PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5960: if (X_t) {
5961: PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5962: for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5963: PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5964: }
5965: if (dmAux) {
5966: PetscInt subcell;
5967: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5968: PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5969: for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5970: PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5971: }
5972: }
5973: if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5974: if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5975: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5976: for (fieldI = 0; fieldI < Nf; ++fieldI) {
5977: PetscClassId id;
5978: PetscFE fe;
5979: PetscQuadrature qGeom = NULL;
5980: PetscInt Nb;
5981: /* Conforming batches */
5982: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5983: /* Remainder */
5984: PetscInt Nr, offset, Nq;
5985: PetscInt maxDegree;
5986: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5988: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5989: PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5990: if (id == PETSCFV_CLASSID) {
5991: hasFV = PETSC_TRUE;
5992: continue;
5993: }
5994: PetscCall(PetscFEGetDimension(fe, &Nb));
5995: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5996: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5997: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5998: if (!qGeom) {
5999: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6000: PetscCall(PetscObjectReference((PetscObject)qGeom));
6001: }
6002: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6003: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6004: blockSize = Nb;
6005: batchSize = numBlocks * blockSize;
6006: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6007: numChunks = numCells / (numBatches * batchSize);
6008: Ne = numChunks * numBatches * batchSize;
6009: Nr = numCells % (numBatches * batchSize);
6010: offset = numCells - Nr;
6011: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6012: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6013: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6014: key.field = fieldI * Nf + fieldJ;
6015: if (hasJac) {
6016: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6017: PetscCall(PetscFEIntegrateJacobian(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]));
6018: }
6019: if (hasPrec) {
6020: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
6021: PetscCall(PetscFEIntegrateJacobian(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]));
6022: }
6023: if (hasDyn) {
6024: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6025: PetscCall(PetscFEIntegrateJacobian(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]));
6026: }
6027: }
6028: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6029: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6030: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6031: PetscCall(PetscQuadratureDestroy(&qGeom));
6032: }
6033: /* Add contribution from X_t */
6034: if (hasDyn) {
6035: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6036: }
6037: if (hasFV) {
6038: PetscClassId id;
6039: PetscFV fv;
6040: PetscInt offsetI, NcI, NbI = 1, fc, f;
6042: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6043: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
6044: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
6045: PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
6046: if (id != PETSCFV_CLASSID) continue;
6047: /* Put in the weighted identity */
6048: PetscCall(PetscFVGetNumComponents(fv, &NcI));
6049: for (c = cStart; c < cEnd; ++c) {
6050: const PetscInt cind = c - cStart;
6051: const PetscInt eOffset = cind * totDim * totDim;
6052: PetscReal vol;
6054: PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
6055: for (fc = 0; fc < NcI; ++fc) {
6056: for (f = 0; f < NbI; ++f) {
6057: const PetscInt i = offsetI + f * NcI + fc;
6058: if (hasPrec) {
6059: if (hasJac) elemMat[eOffset + i * totDim + i] = vol;
6060: elemMatP[eOffset + i * totDim + i] = vol;
6061: } else {
6062: elemMat[eOffset + i * totDim + i] = vol;
6063: }
6064: }
6065: }
6066: }
6067: }
6068: /* No allocated space for FV stuff, so ignore the zero entries */
6069: PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
6070: }
6071: /* Insert values into matrix */
6072: for (c = cStart; c < cEnd; ++c) {
6073: const PetscInt cell = cells ? cells[c] : c;
6074: const PetscInt cind = c - cStart;
6076: /* Transform to global basis before insertion in Jacobian */
6077: if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
6078: if (hasPrec) {
6079: if (hasJac) {
6080: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6081: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6082: }
6083: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
6084: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
6085: } else {
6086: if (hasJac) {
6087: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6088: PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
6089: }
6090: }
6091: }
6092: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6093: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
6094: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
6095: if (dmAux) {
6096: PetscCall(PetscFree(a));
6097: PetscCall(DMDestroy(&plex));
6098: }
6099: /* Compute boundary integrals */
6100: PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
6101: /* Assemble matrix */
6102: end: {
6103: PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
6105: PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
6106: if (hasJac && hasPrec) {
6107: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
6108: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
6109: }
6110: }
6111: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
6112: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
6113: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6114: PetscFunctionReturn(PETSC_SUCCESS);
6115: }
6117: PetscErrorCode DMPlexComputeJacobian_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, void *user)
6118: {
6119: DM_Plex *mesh = (DM_Plex *)dm->data;
6120: const char *name = "Hybrid Jacobian";
6121: DM dmAux[3] = {NULL, NULL, NULL};
6122: DMLabel ghostLabel = NULL;
6123: DM plex = NULL;
6124: DM plexA = NULL;
6125: PetscDS ds = NULL;
6126: PetscDS dsIn = NULL;
6127: PetscDS dsAux[3] = {NULL, NULL, NULL};
6128: Vec locA[3] = {NULL, NULL, NULL};
6129: DM dmScale[3] = {NULL, NULL, NULL};
6130: PetscDS dsScale[3] = {NULL, NULL, NULL};
6131: Vec locS[3] = {NULL, NULL, NULL};
6132: PetscSection section = NULL;
6133: PetscSection sectionAux[3] = {NULL, NULL, NULL};
6134: DMField coordField = NULL;
6135: PetscScalar *a[3] = {NULL, NULL, NULL};
6136: PetscScalar *s[3] = {NULL, NULL, NULL};
6137: PetscScalar *u = NULL, *u_t;
6138: PetscScalar *elemMatNeg, *elemMatPos, *elemMatCoh;
6139: PetscScalar *elemMatNegP, *elemMatPosP, *elemMatCohP;
6140: PetscSection globalSection;
6141: IS chunkIS;
6142: const PetscInt *cells;
6143: PetscInt *faces;
6144: PetscInt cStart, cEnd, numCells;
6145: PetscInt Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
6146: PetscInt maxDegree = PETSC_INT_MAX;
6147: PetscQuadrature affineQuad = NULL, *quads = NULL;
6148: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
6149: PetscBool hasBdJac, hasBdPrec;
6151: PetscFunctionBegin;
6152: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6153: if (!cellIS) goto end;
6154: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6155: PetscCall(ISGetLocalSize(cellIS, &numCells));
6156: if (cStart >= cEnd) goto end;
6157: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
6158: const char *name;
6159: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
6160: 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);
6161: }
6162: PetscCall(DMConvert(dm, DMPLEX, &plex));
6163: PetscCall(DMGetLocalSection(dm, §ion));
6164: PetscCall(DMGetGlobalSection(dm, &globalSection));
6165: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
6166: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
6167: PetscCall(PetscDSGetNumFields(ds, &Nf));
6168: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
6169: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
6170: PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
6171: PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
6172: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
6173: if (locA[2]) {
6174: const PetscInt cellStart = cells ? cells[cStart] : cStart;
6176: PetscCall(VecGetDM(locA[2], &dmAux[2]));
6177: PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
6178: PetscCall(DMGetLocalSection(dmAux[2], §ionAux[2]));
6179: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
6180: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
6181: {
6182: const PetscInt *cone;
6183: PetscInt c;
6185: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6186: for (c = 0; c < 2; ++c) {
6187: const PetscInt *support;
6188: PetscInt ssize, s;
6190: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6191: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6192: 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);
6193: if (support[0] == cellStart) s = 1;
6194: else if (support[1] == cellStart) s = 0;
6195: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6196: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
6197: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
6198: else dmAux[c] = dmAux[2];
6199: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
6200: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
6201: }
6202: }
6203: }
6204: /* Handle mass matrix scaling
6205: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
6206: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
6207: if (locS[2]) {
6208: const PetscInt cellStart = cells ? cells[cStart] : cStart;
6209: PetscInt Nb, Nbs;
6211: PetscCall(VecGetDM(locS[2], &dmScale[2]));
6212: PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
6213: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
6214: // BRAD: This is not set correctly
6215: key[2].field = 2;
6216: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
6217: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
6218: 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);
6219: {
6220: const PetscInt *cone;
6221: PetscInt c;
6223: locS[1] = locS[0] = locS[2];
6224: dmScale[1] = dmScale[0] = dmScale[2];
6225: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
6226: for (c = 0; c < 2; ++c) {
6227: const PetscInt *support;
6228: PetscInt ssize, s;
6230: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
6231: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
6232: 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);
6233: if (support[0] == cellStart) s = 1;
6234: else if (support[1] == cellStart) s = 0;
6235: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
6236: PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
6237: PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
6238: }
6239: }
6240: }
6241: /* 2: Setup geometric data */
6242: PetscCall(DMGetCoordinateField(dm, &coordField));
6243: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6244: if (maxDegree > 1) {
6245: PetscInt f;
6246: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
6247: for (f = 0; f < Nf; ++f) {
6248: PetscFE fe;
6250: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
6251: if (fe) {
6252: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
6253: PetscCall(PetscObjectReference((PetscObject)quads[f]));
6254: }
6255: }
6256: }
6257: /* Loop over chunks */
6258: cellChunkSize = numCells;
6259: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
6260: PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
6261: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
6262: /* Extract field coefficients */
6263: /* NOTE This needs the end cap faces to have identical orientations */
6264: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6265: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6266: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
6267: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6268: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6269: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6270: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6271: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6272: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6273: for (chunk = 0; chunk < numChunks; ++chunk) {
6274: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
6276: if (hasBdJac) {
6277: PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
6278: PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
6279: PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
6280: }
6281: if (hasBdPrec) {
6282: PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
6283: PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
6284: PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
6285: }
6286: /* Get faces */
6287: for (c = cS; c < cE; ++c) {
6288: const PetscInt cell = cells ? cells[c] : c;
6289: const PetscInt *cone;
6290: PetscCall(DMPlexGetCone(plex, cell, &cone));
6291: faces[(c - cS) * 2 + 0] = cone[0];
6292: faces[(c - cS) * 2 + 1] = cone[1];
6293: }
6294: PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
6295: if (maxDegree <= 1) {
6296: if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
6297: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
6298: } else {
6299: PetscInt f;
6300: for (f = 0; f < Nf; ++f) {
6301: if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
6302: }
6303: }
6305: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6306: PetscFE feI;
6307: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[fieldI];
6308: PetscFEGeom *chunkGeom = NULL, *remGeom = NULL;
6309: PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
6310: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6311: PetscBool isCohesiveField;
6313: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6314: if (!feI) continue;
6315: PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6316: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
6317: PetscCall(PetscFEGetDimension(feI, &Nb));
6318: blockSize = Nb;
6319: batchSize = numBlocks * blockSize;
6320: PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6321: numChunks = numCells / (numBatches * batchSize);
6322: Ne = numChunks * numBatches * batchSize;
6323: Nr = numCells % (numBatches * batchSize);
6324: offset = numCells - Nr;
6325: PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
6326: PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
6327: PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6328: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6329: PetscFE feJ;
6331: PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
6332: if (!feJ) continue;
6333: key[0].field = fieldI * Nf + fieldJ;
6334: key[1].field = fieldI * Nf + fieldJ;
6335: key[2].field = fieldI * Nf + fieldJ;
6336: if (hasBdJac) {
6337: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
6338: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
6339: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
6340: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, X_tShift, &elemMatPos[offset * totDim * totDim]));
6341: }
6342: if (hasBdPrec) {
6343: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
6344: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
6345: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
6346: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
6347: }
6348: if (hasBdJac) {
6349: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
6350: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
6351: }
6352: if (hasBdPrec) {
6353: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
6354: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
6355: }
6356: }
6357: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
6358: PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
6359: }
6360: /* Insert values into matrix */
6361: for (c = cS; c < cE; ++c) {
6362: const PetscInt cell = cells ? cells[c] : c;
6363: const PetscInt cind = c - cS, coff = cind * totDim * totDim;
6364: PetscInt i, j;
6366: /* Scale element values */
6367: if (locS[0]) {
6368: PetscInt Nb, soff = cind * totDimScale[0], off = 0;
6369: PetscBool cohesive;
6371: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6372: PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6373: PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));
6375: if (fieldI == key[2].field) {
6376: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6377: for (i = 0; i < Nb; ++i) {
6378: for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNeg[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPos[coff + (off + i) * totDim + j];
6379: if (hasBdPrec)
6380: for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNegP[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPosP[coff + (off + i) * totDim + j];
6381: }
6382: off += Nb;
6383: } else {
6384: const PetscInt N = cohesive ? Nb : Nb * 2;
6386: for (i = 0; i < N; ++i) {
6387: for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
6388: if (hasBdPrec)
6389: for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
6390: }
6391: off += N;
6392: }
6393: }
6394: } else {
6395: for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
6396: if (hasBdPrec)
6397: for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
6398: }
6399: if (hasBdPrec) {
6400: if (hasBdJac) {
6401: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6402: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6403: }
6404: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
6405: PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
6406: } else if (hasBdJac) {
6407: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6408: PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6409: }
6410: }
6411: }
6412: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6413: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6414: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6415: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6416: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6417: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6418: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6419: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6420: PetscCall(PetscFree(faces));
6421: PetscCall(ISDestroy(&chunkIS));
6422: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6423: if (maxDegree <= 1) {
6424: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
6425: PetscCall(PetscQuadratureDestroy(&affineQuad));
6426: } else {
6427: PetscInt f;
6428: for (f = 0; f < Nf; ++f) {
6429: if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
6430: if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
6431: }
6432: PetscCall(PetscFree2(quads, geoms));
6433: }
6434: if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6435: PetscCall(DMDestroy(&plex));
6436: end:
6437: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6438: PetscFunctionReturn(PETSC_SUCCESS);
6439: }
6441: /*
6442: DMPlexComputeJacobian_Action_Internal - Form the local portion of the Jacobian action Z = J(X) Y at the local solution X using pointwise functions specified by the user.
6444: Input Parameters:
6445: + dm - The mesh
6446: . key - The PetscWeakFormKey indicating where integration should happen
6447: . cellIS - The cells to integrate over
6448: . t - The time
6449: . X_tShift - The multiplier for the Jacobian with respect to X_t
6450: . X - Local solution vector
6451: . X_t - Time-derivative of the local solution vector
6452: . Y - Local input vector
6453: - user - the user context
6455: Output Parameter:
6456: . Z - Local output vector
6458: Note:
6459: We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
6460: like a GPU, or vectorize on a multicore machine.
6461: */
6462: PetscErrorCode DMPlexComputeJacobian_Action_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Vec Y, Vec Z, void *user)
6463: {
6464: DM_Plex *mesh = (DM_Plex *)dm->data;
6465: const char *name = "Jacobian";
6466: DM dmAux = NULL, plex, plexAux = NULL;
6467: DMEnclosureType encAux;
6468: Vec A;
6469: DMField coordField;
6470: PetscDS prob, probAux = NULL;
6471: PetscQuadrature quad;
6472: PetscSection section, globalSection, sectionAux;
6473: PetscScalar *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6474: const PetscInt *cells;
6475: PetscInt Nf, fieldI, fieldJ;
6476: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6477: PetscBool hasDyn;
6479: PetscFunctionBegin;
6480: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6481: PetscCall(DMConvert(dm, DMPLEX, &plex));
6482: PetscCall(ISGetLocalSize(cellIS, &numCells));
6483: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6484: PetscCall(DMGetLocalSection(dm, §ion));
6485: PetscCall(DMGetGlobalSection(dm, &globalSection));
6486: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6487: PetscCall(PetscDSGetNumFields(prob, &Nf));
6488: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6489: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6490: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6491: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6492: if (A) {
6493: PetscCall(VecGetDM(A, &dmAux));
6494: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6495: PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6496: PetscCall(DMGetLocalSection(plexAux, §ionAux));
6497: PetscCall(DMGetDS(dmAux, &probAux));
6498: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6499: }
6500: PetscCall(VecSet(Z, 0.0));
6501: PetscCall(PetscMalloc6(numCells * totDim, &u, (X_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));
6502: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6503: PetscCall(DMGetCoordinateField(dm, &coordField));
6504: for (c = cStart; c < cEnd; ++c) {
6505: const PetscInt cell = cells ? cells[c] : c;
6506: const PetscInt cind = c - cStart;
6507: PetscScalar *x = NULL, *x_t = NULL;
6508: PetscInt i;
6510: PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
6511: for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6512: PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
6513: if (X_t) {
6514: PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
6515: for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6516: PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
6517: }
6518: if (dmAux) {
6519: PetscInt subcell;
6520: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6521: PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6522: for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6523: PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6524: }
6525: PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
6526: for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6527: PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
6528: }
6529: PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6530: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6531: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6532: PetscFE fe;
6533: PetscInt Nb;
6534: /* Conforming batches */
6535: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6536: /* Remainder */
6537: PetscInt Nr, offset, Nq;
6538: PetscQuadrature qGeom = NULL;
6539: PetscInt maxDegree;
6540: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6542: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6543: PetscCall(PetscFEGetQuadrature(fe, &quad));
6544: PetscCall(PetscFEGetDimension(fe, &Nb));
6545: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6546: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6547: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6548: if (!qGeom) {
6549: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6550: PetscCall(PetscObjectReference((PetscObject)qGeom));
6551: }
6552: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6553: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6554: blockSize = Nb;
6555: batchSize = numBlocks * blockSize;
6556: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6557: numChunks = numCells / (numBatches * batchSize);
6558: Ne = numChunks * numBatches * batchSize;
6559: Nr = numCells % (numBatches * batchSize);
6560: offset = numCells - Nr;
6561: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6562: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6563: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6564: key.field = fieldI * Nf + fieldJ;
6565: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6566: PetscCall(PetscFEIntegrateJacobian(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]));
6567: if (hasDyn) {
6568: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6569: PetscCall(PetscFEIntegrateJacobian(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]));
6570: }
6571: }
6572: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6573: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6574: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6575: PetscCall(PetscQuadratureDestroy(&qGeom));
6576: }
6577: if (hasDyn) {
6578: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6579: }
6580: for (c = cStart; c < cEnd; ++c) {
6581: const PetscInt cell = cells ? cells[c] : c;
6582: const PetscInt cind = c - cStart;
6583: const PetscBLASInt one = 1;
6584: PetscBLASInt M;
6585: const PetscScalar a = 1.0, b = 0.0;
6587: PetscCall(PetscBLASIntCast(totDim, &M));
6588: PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6589: if (mesh->printFEM > 1) {
6590: PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6591: PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6592: PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6593: }
6594: PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
6595: }
6596: PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6597: if (mesh->printFEM) {
6598: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
6599: PetscCall(VecView(Z, NULL));
6600: }
6601: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6602: PetscCall(PetscFree(a));
6603: PetscCall(DMDestroy(&plexAux));
6604: PetscCall(DMDestroy(&plex));
6605: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6606: PetscFunctionReturn(PETSC_SUCCESS);
6607: }
6609: 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[])
6610: {
6611: f0[0] = u[0];
6612: }
6614: 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[])
6615: {
6616: f0[0] = x[(int)PetscRealPart(constants[0])] * u[0];
6617: }
6619: 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[])
6620: {
6621: PetscInt d;
6623: f0[0] = 0.0;
6624: for (d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
6625: }
6627: /*@
6628: DMPlexComputeMoments - Compute the first three moments for a field
6630: Noncollective
6632: Input Parameters:
6633: + dm - the `DMPLEX`
6634: - u - the field
6636: Output Parameter:
6637: . moments - the field moments
6639: Level: intermediate
6641: Note:
6642: The `moments` array should be of length cdim + 2, where cdim is the number of components for the coordinate field.
6644: .seealso: `DM`, `DMPLEX`, `DMSwarmComputeMoments()`
6645: @*/
6646: PetscErrorCode DMPlexComputeMoments(DM dm, Vec u, PetscReal moments[])
6647: {
6648: PetscDS ds;
6649: PetscScalar mom, constants[1];
6650: const PetscScalar *oldConstants;
6651: PetscInt cdim, Nf, field = 0, Ncon;
6652: MPI_Comm comm;
6653: void *user;
6655: PetscFunctionBeginUser;
6656: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
6657: PetscCall(DMGetCoordinateDim(dm, &cdim));
6658: PetscCall(DMGetApplicationContext(dm, &user));
6659: PetscCall(DMGetDS(dm, &ds));
6660: PetscCall(PetscDSGetNumFields(ds, &Nf));
6661: PetscCall(PetscDSGetConstants(ds, &Ncon, &oldConstants));
6662: PetscCheck(Nf == 1, comm, PETSC_ERR_ARG_WRONG, "We currently only support 1 field, not %" PetscInt_FMT, Nf);
6663: PetscCall(PetscDSSetObjective(ds, field, &f0_1));
6664: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6665: moments[0] = PetscRealPart(mom);
6666: for (PetscInt c = 0; c < cdim; ++c) {
6667: constants[0] = c;
6668: PetscCall(PetscDSSetConstants(ds, 1, constants));
6669: PetscCall(PetscDSSetObjective(ds, field, &f0_x));
6670: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6671: moments[c + 1] = PetscRealPart(mom);
6672: }
6673: PetscCall(PetscDSSetObjective(ds, field, &f0_x2));
6674: PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
6675: moments[cdim + 1] = PetscRealPart(mom);
6676: PetscCall(PetscDSSetConstants(ds, Ncon, (PetscScalar *)oldConstants));
6677: PetscFunctionReturn(PETSC_SUCCESS);
6678: }