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