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: if (copy) {
34: DMSubDomainHookLink link;
36: PetscCall(DMCopyAuxiliaryVec(dm, *plex));
37: /* Run the subdomain hook (this will copy the DMSNES/DMTS) */
38: for (link = dm->subdomainhook; link; link = link->next) {
39: if (link->ddhook) PetscCall((*link->ddhook)(dm, *plex, link->ctx));
40: }
41: }
42: } else {
43: PetscCall(PetscObjectReference((PetscObject)*plex));
44: }
45: }
46: PetscFunctionReturn(PETSC_SUCCESS);
47: }
49: static PetscErrorCode PetscContainerUserDestroy_PetscFEGeom(void *ctx)
50: {
51: PetscFEGeom *geom = (PetscFEGeom *)ctx;
53: PetscFunctionBegin;
54: PetscCall(PetscFEGeomDestroy(&geom));
55: PetscFunctionReturn(PETSC_SUCCESS);
56: }
58: static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
59: {
60: char composeStr[33] = {0};
61: PetscObjectId id;
62: PetscContainer container;
64: PetscFunctionBegin;
65: PetscCall(PetscObjectGetId((PetscObject)quad, &id));
66: PetscCall(PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%" PetscInt64_FMT "\n", id));
67: PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
68: if (container) {
69: PetscCall(PetscContainerGetPointer(container, (void **)geom));
70: } else {
71: PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
72: PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
73: PetscCall(PetscContainerSetPointer(container, (void *)*geom));
74: PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
75: PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
76: PetscCall(PetscContainerDestroy(&container));
77: }
78: PetscFunctionReturn(PETSC_SUCCESS);
79: }
81: static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
82: {
83: PetscFunctionBegin;
84: *geom = NULL;
85: PetscFunctionReturn(PETSC_SUCCESS);
86: }
88: /*@
89: DMPlexGetScale - Get the scale for the specified fundamental unit
91: Not Collective
93: Input Parameters:
94: + dm - the `DM`
95: - unit - The SI unit
97: Output Parameter:
98: . scale - The value used to scale all quantities with this unit
100: Level: advanced
102: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetScale()`, `PetscUnit`
103: @*/
104: PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
105: {
106: DM_Plex *mesh = (DM_Plex *)dm->data;
108: PetscFunctionBegin;
111: *scale = mesh->scale[unit];
112: PetscFunctionReturn(PETSC_SUCCESS);
113: }
115: /*@
116: DMPlexSetScale - Set the scale for the specified fundamental unit
118: Not Collective
120: Input Parameters:
121: + dm - the `DM`
122: . unit - The SI unit
123: - scale - The value used to scale all quantities with this unit
125: Level: advanced
127: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetScale()`, `PetscUnit`
128: @*/
129: PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
130: {
131: DM_Plex *mesh = (DM_Plex *)dm->data;
133: PetscFunctionBegin;
135: mesh->scale[unit] = scale;
136: PetscFunctionReturn(PETSC_SUCCESS);
137: }
139: static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, void *ctx)
140: {
141: const PetscInt eps[3][3][3] = {
142: {{0, 0, 0}, {0, 0, 1}, {0, -1, 0}},
143: {{0, 0, -1}, {0, 0, 0}, {1, 0, 0} },
144: {{0, 1, 0}, {-1, 0, 0}, {0, 0, 0} }
145: };
146: PetscInt *ctxInt = (PetscInt *)ctx;
147: PetscInt dim2 = ctxInt[0];
148: PetscInt d = ctxInt[1];
149: PetscInt i, j, k = dim > 2 ? d - dim : d;
151: PetscFunctionBegin;
152: PetscCheck(dim == dim2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %" PetscInt_FMT " does not match context dimension %" PetscInt_FMT, dim, dim2);
153: for (i = 0; i < dim; i++) mode[i] = 0.;
154: if (d < dim) {
155: mode[d] = 1.; /* Translation along axis d */
156: } else {
157: for (i = 0; i < dim; i++) {
158: for (j = 0; j < dim; j++) { mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */ }
159: }
160: }
161: PetscFunctionReturn(PETSC_SUCCESS);
162: }
164: /*@
165: DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation
167: Collective
169: Input Parameters:
170: + dm - the `DM`
171: - field - The field number for the rigid body space, or 0 for the default
173: Output Parameter:
174: . sp - the null space
176: Level: advanced
178: Note:
179: This is necessary to provide a suitable coarse space for algebraic multigrid
181: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`, `PCGAMG`
182: @*/
183: PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
184: {
185: PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
186: MPI_Comm comm;
187: Vec mode[6];
188: PetscSection section, globalSection;
189: PetscInt dim, dimEmbed, Nf, n, m, mmin, d, i, j;
190: void **ctxs;
192: PetscFunctionBegin;
193: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
194: PetscCall(DMGetDimension(dm, &dim));
195: PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
196: PetscCall(DMGetNumFields(dm, &Nf));
197: PetscCheck(!Nf || !(field < 0 || field >= Nf), comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", field, Nf);
198: if (dim == 1 && Nf < 2) {
199: PetscCall(MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp));
200: PetscFunctionReturn(PETSC_SUCCESS);
201: }
202: PetscCall(DMGetLocalSection(dm, §ion));
203: PetscCall(DMGetGlobalSection(dm, &globalSection));
204: PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
205: PetscCall(PetscCalloc2(Nf, &func, Nf, &ctxs));
206: m = (dim * (dim + 1)) / 2;
207: PetscCall(VecCreate(comm, &mode[0]));
208: PetscCall(VecSetType(mode[0], dm->vectype));
209: PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
210: PetscCall(VecSetUp(mode[0]));
211: PetscCall(VecGetSize(mode[0], &n));
212: mmin = PetscMin(m, n);
213: func[field] = DMPlexProjectRigidBody_Private;
214: for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
215: for (d = 0; d < m; d++) {
216: PetscInt ctx[2];
218: ctxs[field] = (void *)(&ctx[0]);
219: ctx[0] = dimEmbed;
220: ctx[1] = d;
221: PetscCall(DMProjectFunction(dm, 0.0, func, ctxs, INSERT_VALUES, mode[d]));
222: }
223: /* Orthonormalize system */
224: for (i = 0; i < mmin; ++i) {
225: PetscScalar dots[6];
227: PetscCall(VecNormalize(mode[i], NULL));
228: PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1));
229: for (j = i + 1; j < mmin; ++j) {
230: dots[j] *= -1.0;
231: PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
232: }
233: }
234: PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp));
235: for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
236: PetscCall(PetscFree2(func, ctxs));
237: PetscFunctionReturn(PETSC_SUCCESS);
238: }
240: /*@
241: DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation
243: Collective
245: Input Parameters:
246: + dm - the `DM`
247: . nb - The number of bodies
248: . label - The `DMLabel` marking each domain
249: . nids - The number of ids per body
250: - ids - An array of the label ids in sequence for each domain
252: Output Parameter:
253: . sp - the null space
255: Level: advanced
257: Note:
258: This is necessary to provide a suitable coarse space for algebraic multigrid
260: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`
261: @*/
262: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
263: {
264: MPI_Comm comm;
265: PetscSection section, globalSection;
266: Vec *mode;
267: PetscScalar *dots;
268: PetscInt dim, dimEmbed, n, m, b, d, i, j, off;
270: PetscFunctionBegin;
271: PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
272: PetscCall(DMGetDimension(dm, &dim));
273: PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
274: PetscCall(DMGetLocalSection(dm, §ion));
275: PetscCall(DMGetGlobalSection(dm, &globalSection));
276: PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
277: m = nb * (dim * (dim + 1)) / 2;
278: PetscCall(PetscMalloc2(m, &mode, m, &dots));
279: PetscCall(VecCreate(comm, &mode[0]));
280: PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
281: PetscCall(VecSetUp(mode[0]));
282: for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
283: for (b = 0, off = 0; b < nb; ++b) {
284: for (d = 0; d < m / nb; ++d) {
285: PetscInt ctx[2];
286: PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
287: void *voidctx = (void *)(&ctx[0]);
289: ctx[0] = dimEmbed;
290: ctx[1] = d;
291: PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]));
292: off += nids[b];
293: }
294: }
295: /* Orthonormalize system */
296: for (i = 0; i < m; ++i) {
297: PetscScalar dots[6];
299: PetscCall(VecNormalize(mode[i], NULL));
300: PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1));
301: for (j = i + 1; j < m; ++j) {
302: dots[j] *= -1.0;
303: PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
304: }
305: }
306: PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp));
307: for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
308: PetscCall(PetscFree2(mode, dots));
309: PetscFunctionReturn(PETSC_SUCCESS);
310: }
312: /*@
313: DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
314: are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
315: evaluating the dual space basis of that point. A basis function is associated with the point in its
316: transitively-closed support whose mesh height is highest (w.r.t. DAG height), but not greater than the maximum
317: projection height, which is set with this function. By default, the maximum projection height is zero, which means
318: that only mesh cells are used to project basis functions. A height of one, for example, evaluates a cell-interior
319: basis functions using its cells dual space basis, but all other basis functions with the dual space basis of a face.
321: Input Parameters:
322: + dm - the `DMPLEX` object
323: - height - the maximum projection height >= 0
325: Level: advanced
327: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
328: @*/
329: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
330: {
331: DM_Plex *plex = (DM_Plex *)dm->data;
333: PetscFunctionBegin;
335: plex->maxProjectionHeight = height;
336: PetscFunctionReturn(PETSC_SUCCESS);
337: }
339: /*@
340: DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in
341: DMPlexProjectXXXLocal() functions.
343: Input Parameter:
344: . dm - the `DMPLEX` object
346: Output Parameter:
347: . height - the maximum projection height
349: Level: intermediate
351: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
352: @*/
353: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
354: {
355: DM_Plex *plex = (DM_Plex *)dm->data;
357: PetscFunctionBegin;
359: *height = plex->maxProjectionHeight;
360: PetscFunctionReturn(PETSC_SUCCESS);
361: }
363: typedef struct {
364: PetscReal alpha; /* The first Euler angle, and in 2D the only one */
365: PetscReal beta; /* The second Euler angle */
366: PetscReal gamma; /* The third Euler angle */
367: PetscInt dim; /* The dimension of R */
368: PetscScalar *R; /* The rotation matrix, transforming a vector in the local basis to the global basis */
369: PetscScalar *RT; /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
370: } RotCtx;
372: /*
373: Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
374: 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:
375: $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
376: $ 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.
377: $ The XYZ system rotates a third time about the z axis by gamma.
378: */
379: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
380: {
381: RotCtx *rc = (RotCtx *)ctx;
382: PetscInt dim = rc->dim;
383: PetscReal c1, s1, c2, s2, c3, s3;
385: PetscFunctionBegin;
386: PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT));
387: switch (dim) {
388: case 2:
389: c1 = PetscCosReal(rc->alpha);
390: s1 = PetscSinReal(rc->alpha);
391: rc->R[0] = c1;
392: rc->R[1] = s1;
393: rc->R[2] = -s1;
394: rc->R[3] = c1;
395: PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
396: DMPlex_Transpose2D_Internal(rc->RT);
397: break;
398: case 3:
399: c1 = PetscCosReal(rc->alpha);
400: s1 = PetscSinReal(rc->alpha);
401: c2 = PetscCosReal(rc->beta);
402: s2 = PetscSinReal(rc->beta);
403: c3 = PetscCosReal(rc->gamma);
404: s3 = PetscSinReal(rc->gamma);
405: rc->R[0] = c1 * c3 - c2 * s1 * s3;
406: rc->R[1] = c3 * s1 + c1 * c2 * s3;
407: rc->R[2] = s2 * s3;
408: rc->R[3] = -c1 * s3 - c2 * c3 * s1;
409: rc->R[4] = c1 * c2 * c3 - s1 * s3;
410: rc->R[5] = c3 * s2;
411: rc->R[6] = s1 * s2;
412: rc->R[7] = -c1 * s2;
413: rc->R[8] = c2;
414: PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
415: DMPlex_Transpose3D_Internal(rc->RT);
416: break;
417: default:
418: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
419: }
420: PetscFunctionReturn(PETSC_SUCCESS);
421: }
423: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
424: {
425: RotCtx *rc = (RotCtx *)ctx;
427: PetscFunctionBegin;
428: PetscCall(PetscFree2(rc->R, rc->RT));
429: PetscCall(PetscFree(rc));
430: PetscFunctionReturn(PETSC_SUCCESS);
431: }
433: static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, void *ctx)
434: {
435: RotCtx *rc = (RotCtx *)ctx;
437: PetscFunctionBeginHot;
439: if (l2g) {
440: *A = rc->R;
441: } else {
442: *A = rc->RT;
443: }
444: PetscFunctionReturn(PETSC_SUCCESS);
445: }
447: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
448: {
449: PetscFunctionBegin;
450: #if defined(PETSC_USE_COMPLEX)
451: switch (dim) {
452: case 2: {
453: PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};
455: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
456: z[0] = PetscRealPart(zt[0]);
457: z[1] = PetscRealPart(zt[1]);
458: } break;
459: case 3: {
460: PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};
462: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
463: z[0] = PetscRealPart(zt[0]);
464: z[1] = PetscRealPart(zt[1]);
465: z[2] = PetscRealPart(zt[2]);
466: } break;
467: }
468: #else
469: PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx));
470: #endif
471: PetscFunctionReturn(PETSC_SUCCESS);
472: }
474: PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, void *ctx)
475: {
476: const PetscScalar *A;
478: PetscFunctionBeginHot;
479: PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx));
480: switch (dim) {
481: case 2:
482: DMPlex_Mult2D_Internal(A, 1, y, z);
483: break;
484: case 3:
485: DMPlex_Mult3D_Internal(A, 1, y, z);
486: break;
487: }
488: PetscFunctionReturn(PETSC_SUCCESS);
489: }
491: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
492: {
493: PetscSection ts;
494: const PetscScalar *ta, *tva;
495: PetscInt dof;
497: PetscFunctionBeginHot;
498: PetscCall(DMGetLocalSection(tdm, &ts));
499: PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
500: PetscCall(VecGetArrayRead(tv, &ta));
501: PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva));
502: if (l2g) {
503: switch (dof) {
504: case 4:
505: DMPlex_Mult2D_Internal(tva, 1, a, a);
506: break;
507: case 9:
508: DMPlex_Mult3D_Internal(tva, 1, a, a);
509: break;
510: }
511: } else {
512: switch (dof) {
513: case 4:
514: DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
515: break;
516: case 9:
517: DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
518: break;
519: }
520: }
521: PetscCall(VecRestoreArrayRead(tv, &ta));
522: PetscFunctionReturn(PETSC_SUCCESS);
523: }
525: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
526: {
527: PetscSection s, ts;
528: const PetscScalar *ta, *tvaf, *tvag;
529: PetscInt fdof, gdof, fpdof, gpdof;
531: PetscFunctionBeginHot;
532: PetscCall(DMGetLocalSection(dm, &s));
533: PetscCall(DMGetLocalSection(tdm, &ts));
534: PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof));
535: PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof));
536: PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof));
537: PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof));
538: PetscCall(VecGetArrayRead(tv, &ta));
539: PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf));
540: PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag));
541: if (l2g) {
542: switch (fdof) {
543: case 4:
544: DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
545: break;
546: case 9:
547: DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
548: break;
549: }
550: switch (gdof) {
551: case 4:
552: DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
553: break;
554: case 9:
555: DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
556: break;
557: }
558: } else {
559: switch (fdof) {
560: case 4:
561: DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
562: break;
563: case 9:
564: DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
565: break;
566: }
567: switch (gdof) {
568: case 4:
569: DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
570: break;
571: case 9:
572: DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
573: break;
574: }
575: }
576: PetscCall(VecRestoreArrayRead(tv, &ta));
577: PetscFunctionReturn(PETSC_SUCCESS);
578: }
580: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
581: {
582: PetscSection s;
583: PetscSection clSection;
584: IS clPoints;
585: const PetscInt *clp;
586: PetscInt *points = NULL;
587: PetscInt Nf, f, Np, cp, dof, d = 0;
589: PetscFunctionBegin;
590: PetscCall(DMGetLocalSection(dm, &s));
591: PetscCall(PetscSectionGetNumFields(s, &Nf));
592: PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
593: for (f = 0; f < Nf; ++f) {
594: for (cp = 0; cp < Np * 2; cp += 2) {
595: PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof));
596: if (!dof) continue;
597: if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]));
598: d += dof;
599: }
600: }
601: PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
602: PetscFunctionReturn(PETSC_SUCCESS);
603: }
605: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
606: {
607: PetscSection s;
608: PetscSection clSection;
609: IS clPoints;
610: const PetscInt *clp;
611: PetscInt *points = NULL;
612: PetscInt Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;
614: PetscFunctionBegin;
615: PetscCall(DMGetLocalSection(dm, &s));
616: PetscCall(PetscSectionGetNumFields(s, &Nf));
617: PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
618: for (f = 0, r = 0; f < Nf; ++f) {
619: for (cpf = 0; cpf < Np * 2; cpf += 2) {
620: PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof));
621: for (g = 0, c = 0; g < Nf; ++g) {
622: for (cpg = 0; cpg < Np * 2; cpg += 2) {
623: PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof));
624: PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]));
625: c += gdof;
626: }
627: }
628: PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
629: r += fdof;
630: }
631: }
632: PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
633: PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
634: PetscFunctionReturn(PETSC_SUCCESS);
635: }
637: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
638: {
639: DM tdm;
640: Vec tv;
641: PetscSection ts, s;
642: const PetscScalar *ta;
643: PetscScalar *a, *va;
644: PetscInt pStart, pEnd, p, Nf, f;
646: PetscFunctionBegin;
647: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
648: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
649: PetscCall(DMGetLocalSection(tdm, &ts));
650: PetscCall(DMGetLocalSection(dm, &s));
651: PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
652: PetscCall(PetscSectionGetNumFields(s, &Nf));
653: PetscCall(VecGetArray(lv, &a));
654: PetscCall(VecGetArrayRead(tv, &ta));
655: for (p = pStart; p < pEnd; ++p) {
656: for (f = 0; f < Nf; ++f) {
657: PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va));
658: PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va));
659: }
660: }
661: PetscCall(VecRestoreArray(lv, &a));
662: PetscCall(VecRestoreArrayRead(tv, &ta));
663: PetscFunctionReturn(PETSC_SUCCESS);
664: }
666: /*@
667: DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis
669: Input Parameters:
670: + dm - The `DM`
671: - lv - A local vector with values in the global basis
673: Output Parameter:
674: . lv - A local vector with values in the local basis
676: Level: developer
678: Note:
679: 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.
681: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
682: @*/
683: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
684: {
685: PetscFunctionBegin;
688: PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE));
689: PetscFunctionReturn(PETSC_SUCCESS);
690: }
692: /*@
693: DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis
695: Input Parameters:
696: + dm - The `DM`
697: - lv - A local vector with values in the local basis
699: Output Parameter:
700: . lv - A local vector with values in the global basis
702: Level: developer
704: Note:
705: 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.
707: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
708: @*/
709: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
710: {
711: PetscFunctionBegin;
714: PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE));
715: PetscFunctionReturn(PETSC_SUCCESS);
716: }
718: /*@
719: DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions
720: and global solutions, to a local basis, appropriate for discretization integrals and assembly.
722: Input Parameters:
723: + dm - The `DM`
724: . alpha - The first Euler angle, and in 2D the only one
725: . beta - The second Euler angle
726: - gamma - The third Euler angle
728: Level: developer
730: Note:
731: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
732: 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
733: .vb
734: The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
735: 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.
736: The XYZ system rotates a third time about the z axis by gamma.
737: .ve
739: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
740: @*/
741: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
742: {
743: RotCtx *rc;
744: PetscInt cdim;
746: PetscFunctionBegin;
747: PetscCall(DMGetCoordinateDim(dm, &cdim));
748: PetscCall(PetscMalloc1(1, &rc));
749: dm->transformCtx = rc;
750: dm->transformSetUp = DMPlexBasisTransformSetUp_Rotation_Internal;
751: dm->transformDestroy = DMPlexBasisTransformDestroy_Rotation_Internal;
752: dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
753: rc->dim = cdim;
754: rc->alpha = alpha;
755: rc->beta = beta;
756: rc->gamma = gamma;
757: PetscCall((*dm->transformSetUp)(dm, dm->transformCtx));
758: PetscCall(DMConstructBasisTransform_Internal(dm));
759: PetscFunctionReturn(PETSC_SUCCESS);
760: }
762: /*@C
763: DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector using a function of the coordinates
765: Input Parameters:
766: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
767: . time - The time
768: . field - The field to constrain
769: . Nc - The number of constrained field components, or 0 for all components
770: . comps - An array of constrained component numbers, or `NULL` for all components
771: . label - The `DMLabel` defining constrained points
772: . numids - The number of `DMLabel` ids for constrained points
773: . ids - An array of ids for constrained points
774: . func - A pointwise function giving boundary values
775: - ctx - An optional user context for bcFunc
777: Output Parameter:
778: . locX - A local vector to receives the boundary values
780: Level: developer
782: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
783: @*/
784: 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)
785: {
786: PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
787: void **ctxs;
788: PetscInt numFields;
790: PetscFunctionBegin;
791: PetscCall(DMGetNumFields(dm, &numFields));
792: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
793: funcs[field] = func;
794: ctxs[field] = ctx;
795: PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX));
796: PetscCall(PetscFree2(funcs, ctxs));
797: PetscFunctionReturn(PETSC_SUCCESS);
798: }
800: /*@C
801: DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector using a function of the coordinates and field data
803: Input Parameters:
804: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
805: . time - The time
806: . locU - A local vector with the input solution values
807: . field - The field to constrain
808: . Nc - The number of constrained field components, or 0 for all components
809: . comps - An array of constrained component numbers, or `NULL` for all components
810: . label - The `DMLabel` defining constrained points
811: . numids - The number of `DMLabel` ids for constrained points
812: . ids - An array of ids for constrained points
813: . func - A pointwise function giving boundary values
814: - ctx - An optional user context for bcFunc
816: Output Parameter:
817: . locX - A local vector to receives the boundary values
819: Level: developer
821: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
822: @*/
823: 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)
824: {
825: 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[]);
826: void **ctxs;
827: PetscInt numFields;
829: PetscFunctionBegin;
830: PetscCall(DMGetNumFields(dm, &numFields));
831: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
832: funcs[field] = func;
833: ctxs[field] = ctx;
834: PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
835: PetscCall(PetscFree2(funcs, ctxs));
836: PetscFunctionReturn(PETSC_SUCCESS);
837: }
839: /*@C
840: DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coordinates and boundary field data
842: Collective
844: Input Parameters:
845: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
846: . time - The time
847: . locU - A local vector with the input solution values
848: . field - The field to constrain
849: . Nc - The number of constrained field components, or 0 for all components
850: . comps - An array of constrained component numbers, or `NULL` for all components
851: . label - The `DMLabel` defining constrained points
852: . numids - The number of `DMLabel` ids for constrained points
853: . ids - An array of ids for constrained points
854: . func - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()`
855: - ctx - An optional user context for `func`
857: Output Parameter:
858: . locX - A local vector to receive the boundary values
860: Level: developer
862: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
863: @*/
864: 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)
865: {
866: 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[]);
867: void **ctxs;
868: PetscInt numFields;
870: PetscFunctionBegin;
871: PetscCall(DMGetNumFields(dm, &numFields));
872: PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
873: funcs[field] = func;
874: ctxs[field] = ctx;
875: PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
876: PetscCall(PetscFree2(funcs, ctxs));
877: PetscFunctionReturn(PETSC_SUCCESS);
878: }
880: /*@C
881: DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector
883: Input Parameters:
884: + dm - The `DM`, with a `PetscDS` that matches the problem being constrained
885: . time - The time
886: . faceGeometry - A vector with the FVM face geometry information
887: . cellGeometry - A vector with the FVM cell geometry information
888: . Grad - A vector with the FVM cell gradient information
889: . field - The field to constrain
890: . Nc - The number of constrained field components, or 0 for all components
891: . comps - An array of constrained component numbers, or `NULL` for all components
892: . label - The `DMLabel` defining constrained points
893: . numids - The number of `DMLabel` ids for constrained points
894: . ids - An array of ids for constrained points
895: . func - A pointwise function giving boundary values
896: - ctx - An optional user context for bcFunc
898: Output Parameter:
899: . locX - A local vector to receives the boundary values
901: Level: developer
903: Note:
904: This implementation currently ignores the numcomps/comps argument from `DMAddBoundary()`
906: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
907: @*/
908: 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)
909: {
910: PetscDS prob;
911: PetscSF sf;
912: DM dmFace, dmCell, dmGrad;
913: const PetscScalar *facegeom, *cellgeom = NULL, *grad;
914: const PetscInt *leaves;
915: PetscScalar *x, *fx;
916: PetscInt dim, nleaves, loc, fStart, fEnd, pdim, i;
917: PetscErrorCode ierru = PETSC_SUCCESS;
919: PetscFunctionBegin;
920: PetscCall(DMGetPointSF(dm, &sf));
921: PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
922: nleaves = PetscMax(0, nleaves);
923: PetscCall(DMGetDimension(dm, &dim));
924: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
925: PetscCall(DMGetDS(dm, &prob));
926: PetscCall(VecGetDM(faceGeometry, &dmFace));
927: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
928: if (cellGeometry) {
929: PetscCall(VecGetDM(cellGeometry, &dmCell));
930: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
931: }
932: if (Grad) {
933: PetscFV fv;
935: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv));
936: PetscCall(VecGetDM(Grad, &dmGrad));
937: PetscCall(VecGetArrayRead(Grad, &grad));
938: PetscCall(PetscFVGetNumComponents(fv, &pdim));
939: PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx));
940: }
941: PetscCall(VecGetArray(locX, &x));
942: for (i = 0; i < numids; ++i) {
943: IS faceIS;
944: const PetscInt *faces;
945: PetscInt numFaces, f;
947: PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS));
948: if (!faceIS) continue; /* No points with that id on this process */
949: PetscCall(ISGetLocalSize(faceIS, &numFaces));
950: PetscCall(ISGetIndices(faceIS, &faces));
951: for (f = 0; f < numFaces; ++f) {
952: const PetscInt face = faces[f], *cells;
953: PetscFVFaceGeom *fg;
955: if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
956: PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc));
957: if (loc >= 0) continue;
958: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
959: PetscCall(DMPlexGetSupport(dm, face, &cells));
960: if (Grad) {
961: PetscFVCellGeom *cg;
962: PetscScalar *cx, *cgrad;
963: PetscScalar *xG;
964: PetscReal dx[3];
965: PetscInt d;
967: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
968: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
969: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
970: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
971: DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
972: for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
973: PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
974: } else {
975: PetscScalar *xI;
976: PetscScalar *xG;
978: PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
979: PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
980: ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
981: if (ierru) {
982: PetscCall(ISRestoreIndices(faceIS, &faces));
983: PetscCall(ISDestroy(&faceIS));
984: goto cleanup;
985: }
986: }
987: }
988: PetscCall(ISRestoreIndices(faceIS, &faces));
989: PetscCall(ISDestroy(&faceIS));
990: }
991: cleanup:
992: PetscCall(VecRestoreArray(locX, &x));
993: if (Grad) {
994: PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
995: PetscCall(VecRestoreArrayRead(Grad, &grad));
996: }
997: if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
998: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
999: PetscCall(ierru);
1000: PetscFunctionReturn(PETSC_SUCCESS);
1001: }
1003: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
1004: {
1005: PetscInt c;
1006: for (c = 0; c < Nc; ++c) u[c] = 0.0;
1007: return PETSC_SUCCESS;
1008: }
1010: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1011: {
1012: PetscObject isZero;
1013: PetscDS prob;
1014: PetscInt numBd, b;
1016: PetscFunctionBegin;
1017: PetscCall(DMGetDS(dm, &prob));
1018: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1019: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1020: for (b = 0; b < numBd; ++b) {
1021: PetscWeakForm wf;
1022: DMBoundaryConditionType type;
1023: const char *name;
1024: DMLabel label;
1025: PetscInt field, Nc;
1026: const PetscInt *comps;
1027: PetscObject obj;
1028: PetscClassId id;
1029: void (*bvfunc)(void);
1030: PetscInt numids;
1031: const PetscInt *ids;
1032: void *ctx;
1034: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
1035: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1036: PetscCall(DMGetField(dm, field, NULL, &obj));
1037: PetscCall(PetscObjectGetClassId(obj, &id));
1038: if (id == PETSCFE_CLASSID) {
1039: switch (type) {
1040: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1041: case DM_BC_ESSENTIAL: {
1042: PetscSimplePointFunc func = (PetscSimplePointFunc)bvfunc;
1044: if (isZero) func = zero;
1045: PetscCall(DMPlexLabelAddCells(dm, label));
1046: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
1047: PetscCall(DMPlexLabelClearCells(dm, label));
1048: } break;
1049: case DM_BC_ESSENTIAL_FIELD: {
1050: PetscPointFunc func = (PetscPointFunc)bvfunc;
1052: PetscCall(DMPlexLabelAddCells(dm, label));
1053: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
1054: PetscCall(DMPlexLabelClearCells(dm, label));
1055: } break;
1056: default:
1057: break;
1058: }
1059: } else if (id == PETSCFV_CLASSID) {
1060: {
1061: PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode(*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;
1063: if (!faceGeomFVM) continue;
1064: PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
1065: }
1066: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1067: }
1068: PetscFunctionReturn(PETSC_SUCCESS);
1069: }
1071: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1072: {
1073: PetscObject isZero;
1074: PetscDS prob;
1075: PetscInt numBd, b;
1077: PetscFunctionBegin;
1078: if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
1079: PetscCall(DMGetDS(dm, &prob));
1080: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
1081: PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
1082: for (b = 0; b < numBd; ++b) {
1083: PetscWeakForm wf;
1084: DMBoundaryConditionType type;
1085: const char *name;
1086: DMLabel label;
1087: PetscInt field, Nc;
1088: const PetscInt *comps;
1089: PetscObject obj;
1090: PetscClassId id;
1091: PetscInt numids;
1092: const PetscInt *ids;
1093: void (*bvfunc)(void);
1094: void *ctx;
1096: PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
1097: if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
1098: PetscCall(DMGetField(dm, field, NULL, &obj));
1099: PetscCall(PetscObjectGetClassId(obj, &id));
1100: if (id == PETSCFE_CLASSID) {
1101: switch (type) {
1102: /* for FEM, there is no insertion to be done for non-essential boundary conditions */
1103: case DM_BC_ESSENTIAL: {
1104: PetscSimplePointFunc func_t = (PetscSimplePointFunc)bvfunc;
1106: if (isZero) func_t = zero;
1107: PetscCall(DMPlexLabelAddCells(dm, label));
1108: PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1109: PetscCall(DMPlexLabelClearCells(dm, label));
1110: } break;
1111: case DM_BC_ESSENTIAL_FIELD: {
1112: PetscPointFunc func_t = (PetscPointFunc)bvfunc;
1114: PetscCall(DMPlexLabelAddCells(dm, label));
1115: PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
1116: PetscCall(DMPlexLabelClearCells(dm, label));
1117: } break;
1118: default:
1119: break;
1120: }
1121: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1122: }
1123: PetscFunctionReturn(PETSC_SUCCESS);
1124: }
1126: /*@
1127: DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector
1129: Not Collective
1131: Input Parameters:
1132: + dm - The `DM`
1133: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1134: . time - The time
1135: . faceGeomFVM - Face geometry data for FV discretizations
1136: . cellGeomFVM - Cell geometry data for FV discretizations
1137: - gradFVM - Gradient reconstruction data for FV discretizations
1139: Output Parameter:
1140: . locX - Solution updated with boundary values
1142: Level: intermediate
1144: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
1145: @*/
1146: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1147: {
1148: PetscFunctionBegin;
1154: PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
1155: PetscFunctionReturn(PETSC_SUCCESS);
1156: }
1158: /*@
1159: DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector
1161: Input Parameters:
1162: + dm - The `DM`
1163: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
1164: . time - The time
1165: . faceGeomFVM - Face geometry data for FV discretizations
1166: . cellGeomFVM - Cell geometry data for FV discretizations
1167: - gradFVM - Gradient reconstruction data for FV discretizations
1169: Output Parameter:
1170: . locX_t - Solution updated with boundary values
1172: Level: developer
1174: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
1175: @*/
1176: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
1177: {
1178: PetscFunctionBegin;
1184: PetscTryMethod(dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
1185: PetscFunctionReturn(PETSC_SUCCESS);
1186: }
1188: PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1189: {
1190: Vec localX;
1192: PetscFunctionBegin;
1193: PetscCall(DMGetLocalVector(dm, &localX));
1194: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1195: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1196: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1197: PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1198: PetscCall(DMRestoreLocalVector(dm, &localX));
1199: PetscFunctionReturn(PETSC_SUCCESS);
1200: }
1202: /*@C
1203: DMComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
1205: Collective
1207: Input Parameters:
1208: + dm - The `DM`
1209: . time - The time
1210: . funcs - The functions to evaluate for each field component
1211: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1212: - localX - The coefficient vector u_h, a local vector
1214: Output Parameter:
1215: . diff - The diff ||u - u_h||_2
1217: Level: developer
1219: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1220: @*/
1221: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1222: {
1223: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1224: DM tdm;
1225: Vec tv;
1226: PetscSection section;
1227: PetscQuadrature quad;
1228: PetscFEGeom fegeom;
1229: PetscScalar *funcVal, *interpolant;
1230: PetscReal *coords, *gcoords;
1231: PetscReal localDiff = 0.0;
1232: const PetscReal *quadWeights;
1233: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1234: PetscBool transform;
1236: PetscFunctionBegin;
1237: PetscCall(DMGetDimension(dm, &dim));
1238: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1239: fegeom.dimEmbed = coordDim;
1240: PetscCall(DMGetLocalSection(dm, §ion));
1241: PetscCall(PetscSectionGetNumFields(section, &numFields));
1242: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1243: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1244: PetscCall(DMHasBasisTransform(dm, &transform));
1245: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1246: for (field = 0; field < numFields; ++field) {
1247: PetscObject obj;
1248: PetscClassId id;
1249: PetscInt Nc;
1251: PetscCall(DMGetField(dm, field, NULL, &obj));
1252: PetscCall(PetscObjectGetClassId(obj, &id));
1253: if (id == PETSCFE_CLASSID) {
1254: PetscFE fe = (PetscFE)obj;
1256: PetscCall(PetscFEGetQuadrature(fe, &quad));
1257: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1258: } else if (id == PETSCFV_CLASSID) {
1259: PetscFV fv = (PetscFV)obj;
1261: PetscCall(PetscFVGetQuadrature(fv, &quad));
1262: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1263: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1264: numComponents += Nc;
1265: }
1266: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1267: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1268: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1269: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1270: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1271: for (c = cStart; c < cEnd; ++c) {
1272: PetscScalar *x = NULL;
1273: PetscReal elemDiff = 0.0;
1274: PetscInt qc = 0;
1276: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1277: PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));
1279: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1280: PetscObject obj;
1281: PetscClassId id;
1282: void *const ctx = ctxs ? ctxs[field] : NULL;
1283: PetscInt Nb, Nc, q, fc;
1285: PetscCall(DMGetField(dm, field, NULL, &obj));
1286: PetscCall(PetscObjectGetClassId(obj, &id));
1287: if (id == PETSCFE_CLASSID) {
1288: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1289: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1290: } else if (id == PETSCFV_CLASSID) {
1291: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1292: Nb = 1;
1293: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1294: if (debug) {
1295: char title[1024];
1296: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1297: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1298: }
1299: for (q = 0; q < Nq; ++q) {
1300: PetscFEGeom qgeom;
1301: PetscErrorCode ierr;
1303: qgeom.dimEmbed = fegeom.dimEmbed;
1304: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1305: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1306: qgeom.detJ = &fegeom.detJ[q];
1307: 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);
1308: if (transform) {
1309: gcoords = &coords[coordDim * Nq];
1310: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1311: } else {
1312: gcoords = &coords[coordDim * q];
1313: }
1314: PetscCall(PetscArrayzero(funcVal, Nc));
1315: ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1316: if (ierr) {
1317: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1318: PetscCall(DMRestoreLocalVector(dm, &localX));
1319: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1320: }
1321: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1322: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1323: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1324: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1325: for (fc = 0; fc < Nc; ++fc) {
1326: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1327: if (debug)
1328: 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.),
1329: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1330: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1331: }
1332: }
1333: fieldOffset += Nb;
1334: qc += Nc;
1335: }
1336: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1337: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1338: localDiff += elemDiff;
1339: }
1340: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1341: PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1342: *diff = PetscSqrtReal(*diff);
1343: PetscFunctionReturn(PETSC_SUCCESS);
1344: }
1346: 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)
1347: {
1348: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1349: DM tdm;
1350: PetscSection section;
1351: PetscQuadrature quad;
1352: Vec localX, tv;
1353: PetscScalar *funcVal, *interpolant;
1354: const PetscReal *quadWeights;
1355: PetscFEGeom fegeom;
1356: PetscReal *coords, *gcoords;
1357: PetscReal localDiff = 0.0;
1358: PetscInt dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1359: PetscBool transform;
1361: PetscFunctionBegin;
1362: PetscCall(DMGetDimension(dm, &dim));
1363: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1364: fegeom.dimEmbed = coordDim;
1365: PetscCall(DMGetLocalSection(dm, §ion));
1366: PetscCall(PetscSectionGetNumFields(section, &numFields));
1367: PetscCall(DMGetLocalVector(dm, &localX));
1368: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1369: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1370: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1371: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1372: PetscCall(DMHasBasisTransform(dm, &transform));
1373: for (field = 0; field < numFields; ++field) {
1374: PetscFE fe;
1375: PetscInt Nc;
1377: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1378: PetscCall(PetscFEGetQuadrature(fe, &quad));
1379: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1380: numComponents += Nc;
1381: }
1382: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1383: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1384: /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1385: PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1386: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1387: for (c = cStart; c < cEnd; ++c) {
1388: PetscScalar *x = NULL;
1389: PetscReal elemDiff = 0.0;
1390: PetscInt qc = 0;
1392: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1393: PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));
1395: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1396: PetscFE fe;
1397: void *const ctx = ctxs ? ctxs[field] : NULL;
1398: PetscInt Nb, Nc, q, fc;
1400: PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1401: PetscCall(PetscFEGetDimension(fe, &Nb));
1402: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1403: if (debug) {
1404: char title[1024];
1405: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1406: PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1407: }
1408: for (q = 0; q < Nq; ++q) {
1409: PetscFEGeom qgeom;
1410: PetscErrorCode ierr;
1412: qgeom.dimEmbed = fegeom.dimEmbed;
1413: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1414: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1415: qgeom.detJ = &fegeom.detJ[q];
1416: 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);
1417: if (transform) {
1418: gcoords = &coords[coordDim * Nq];
1419: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1420: } else {
1421: gcoords = &coords[coordDim * q];
1422: }
1423: PetscCall(PetscArrayzero(funcVal, Nc));
1424: ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1425: if (ierr) {
1426: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1427: PetscCall(DMRestoreLocalVector(dm, &localX));
1428: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1429: }
1430: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1431: PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1432: /* Overwrite with the dot product if the normal is given */
1433: if (n) {
1434: for (fc = 0; fc < Nc; ++fc) {
1435: PetscScalar sum = 0.0;
1436: PetscInt d;
1437: for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1438: interpolant[fc] = sum;
1439: }
1440: }
1441: for (fc = 0; fc < Nc; ++fc) {
1442: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1443: 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])));
1444: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1445: }
1446: }
1447: fieldOffset += Nb;
1448: qc += Nc;
1449: }
1450: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1451: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1452: localDiff += elemDiff;
1453: }
1454: PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1455: PetscCall(DMRestoreLocalVector(dm, &localX));
1456: PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1457: *diff = PetscSqrtReal(*diff);
1458: PetscFunctionReturn(PETSC_SUCCESS);
1459: }
1461: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1462: {
1463: const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1464: DM tdm;
1465: DMLabel depthLabel;
1466: PetscSection section;
1467: Vec localX, tv;
1468: PetscReal *localDiff;
1469: PetscInt dim, depth, dE, Nf, f, Nds, s;
1470: PetscBool transform;
1472: PetscFunctionBegin;
1473: PetscCall(DMGetDimension(dm, &dim));
1474: PetscCall(DMGetCoordinateDim(dm, &dE));
1475: PetscCall(DMGetLocalSection(dm, §ion));
1476: PetscCall(DMGetLocalVector(dm, &localX));
1477: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1478: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1479: PetscCall(DMHasBasisTransform(dm, &transform));
1480: PetscCall(DMGetNumFields(dm, &Nf));
1481: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1482: PetscCall(DMLabelGetNumValues(depthLabel, &depth));
1484: PetscCall(VecSet(localX, 0.0));
1485: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1486: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1487: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1488: PetscCall(DMGetNumDS(dm, &Nds));
1489: PetscCall(PetscCalloc1(Nf, &localDiff));
1490: for (s = 0; s < Nds; ++s) {
1491: PetscDS ds;
1492: DMLabel label;
1493: IS fieldIS, pointIS;
1494: const PetscInt *fields, *points = NULL;
1495: PetscQuadrature quad;
1496: const PetscReal *quadPoints, *quadWeights;
1497: PetscFEGeom fegeom;
1498: PetscReal *coords, *gcoords;
1499: PetscScalar *funcVal, *interpolant;
1500: PetscBool isCohesive;
1501: PetscInt qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;
1503: PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1504: PetscCall(ISGetIndices(fieldIS, &fields));
1505: PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1506: PetscCall(PetscDSGetNumFields(ds, &dsNf));
1507: PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1508: PetscCall(PetscDSGetQuadrature(ds, &quad));
1509: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1510: PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1511: PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1512: if (!label) {
1513: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1514: } else {
1515: PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1516: PetscCall(ISGetLocalSize(pointIS, &cEnd));
1517: PetscCall(ISGetIndices(pointIS, &points));
1518: }
1519: for (c = cStart; c < cEnd; ++c) {
1520: const PetscInt cell = points ? points[c] : c;
1521: PetscScalar *x = NULL;
1522: const PetscInt *cone;
1523: PetscInt qc = 0, fOff = 0, dep;
1525: PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1526: if (dep != depth - 1) continue;
1527: if (isCohesive) {
1528: PetscCall(DMPlexGetCone(dm, cell, &cone));
1529: PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1530: } else {
1531: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1532: }
1533: PetscCall(DMPlexVecGetClosure(dm, NULL, localX, cell, NULL, &x));
1534: for (f = 0; f < dsNf; ++f) {
1535: PetscObject obj;
1536: PetscClassId id;
1537: void *const ctx = ctxs ? ctxs[fields[f]] : NULL;
1538: PetscInt Nb, Nc, q, fc;
1539: PetscReal elemDiff = 0.0;
1540: PetscBool cohesive;
1542: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1543: if (isCohesive && !cohesive) continue;
1544: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1545: PetscCall(PetscObjectGetClassId(obj, &id));
1546: if (id == PETSCFE_CLASSID) {
1547: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1548: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1549: } else if (id == PETSCFV_CLASSID) {
1550: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1551: Nb = 1;
1552: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1553: if (debug) {
1554: char title[1024];
1555: PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1556: PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1557: }
1558: for (q = 0; q < Nq; ++q) {
1559: PetscFEGeom qgeom;
1560: PetscErrorCode ierr;
1562: qgeom.dimEmbed = fegeom.dimEmbed;
1563: qgeom.J = &fegeom.J[q * dE * dE];
1564: qgeom.invJ = &fegeom.invJ[q * dE * dE];
1565: qgeom.detJ = &fegeom.detJ[q];
1566: 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);
1567: if (transform) {
1568: gcoords = &coords[dE * Nq];
1569: PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1570: } else {
1571: gcoords = &coords[dE * q];
1572: }
1573: for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1574: ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1575: if (ierr) {
1576: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1577: PetscCall(DMRestoreLocalVector(dm, &localX));
1578: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1579: }
1580: if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1581: /* Call once for each face, except for lagrange field */
1582: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1583: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1584: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1585: for (fc = 0; fc < Nc; ++fc) {
1586: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1587: if (debug)
1588: 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.),
1589: (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1590: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1591: }
1592: }
1593: fOff += Nb;
1594: qc += Nc;
1595: localDiff[fields[f]] += elemDiff;
1596: if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1597: }
1598: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1599: }
1600: if (label) {
1601: PetscCall(ISRestoreIndices(pointIS, &points));
1602: PetscCall(ISDestroy(&pointIS));
1603: }
1604: PetscCall(ISRestoreIndices(fieldIS, &fields));
1605: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1606: }
1607: PetscCall(DMRestoreLocalVector(dm, &localX));
1608: PetscCall(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1609: PetscCall(PetscFree(localDiff));
1610: for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1611: PetscFunctionReturn(PETSC_SUCCESS);
1612: }
1614: /*@C
1615: 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.
1617: Collective
1619: Input Parameters:
1620: + dm - The `DM`
1621: . time - The time
1622: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1623: . ctxs - Optional array of contexts to pass to each function, or `NULL`.
1624: - X - The coefficient vector u_h
1626: Output Parameter:
1627: . D - A `Vec` which holds the difference ||u - u_h||_2 for each cell
1629: Level: developer
1631: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1632: @*/
1633: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1634: {
1635: PetscSection section;
1636: PetscQuadrature quad;
1637: Vec localX;
1638: PetscFEGeom fegeom;
1639: PetscScalar *funcVal, *interpolant;
1640: PetscReal *coords;
1641: const PetscReal *quadPoints, *quadWeights;
1642: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;
1644: PetscFunctionBegin;
1645: PetscCall(VecSet(D, 0.0));
1646: PetscCall(DMGetDimension(dm, &dim));
1647: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1648: PetscCall(DMGetLocalSection(dm, §ion));
1649: PetscCall(PetscSectionGetNumFields(section, &numFields));
1650: PetscCall(DMGetLocalVector(dm, &localX));
1651: PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1652: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1653: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1654: for (field = 0; field < numFields; ++field) {
1655: PetscObject obj;
1656: PetscClassId id;
1657: PetscInt Nc;
1659: PetscCall(DMGetField(dm, field, NULL, &obj));
1660: PetscCall(PetscObjectGetClassId(obj, &id));
1661: if (id == PETSCFE_CLASSID) {
1662: PetscFE fe = (PetscFE)obj;
1664: PetscCall(PetscFEGetQuadrature(fe, &quad));
1665: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1666: } else if (id == PETSCFV_CLASSID) {
1667: PetscFV fv = (PetscFV)obj;
1669: PetscCall(PetscFVGetQuadrature(fv, &quad));
1670: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1671: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1672: numComponents += Nc;
1673: }
1674: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1675: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1676: PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1677: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1678: for (c = cStart; c < cEnd; ++c) {
1679: PetscScalar *x = NULL;
1680: PetscScalar elemDiff = 0.0;
1681: PetscInt qc = 0;
1683: PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1684: PetscCall(DMPlexVecGetClosure(dm, NULL, localX, c, NULL, &x));
1686: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1687: PetscObject obj;
1688: PetscClassId id;
1689: void *const ctx = ctxs ? ctxs[field] : NULL;
1690: PetscInt Nb, Nc, q, fc;
1692: PetscCall(DMGetField(dm, field, NULL, &obj));
1693: PetscCall(PetscObjectGetClassId(obj, &id));
1694: if (id == PETSCFE_CLASSID) {
1695: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1696: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1697: } else if (id == PETSCFV_CLASSID) {
1698: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1699: Nb = 1;
1700: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1701: if (funcs[field]) {
1702: for (q = 0; q < Nq; ++q) {
1703: PetscFEGeom qgeom;
1705: qgeom.dimEmbed = fegeom.dimEmbed;
1706: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1707: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1708: qgeom.detJ = &fegeom.detJ[q];
1709: 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);
1710: PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1711: #if defined(needs_fix_with_return_code_argument)
1712: if (ierr) {
1713: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1714: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1715: PetscCall(DMRestoreLocalVector(dm, &localX));
1716: }
1717: #endif
1718: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1719: else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1720: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1721: for (fc = 0; fc < Nc; ++fc) {
1722: const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1723: elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1724: }
1725: }
1726: }
1727: fieldOffset += Nb;
1728: qc += Nc;
1729: }
1730: PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1731: PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1732: }
1733: PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1734: PetscCall(DMRestoreLocalVector(dm, &localX));
1735: PetscCall(VecSqrtAbs(D));
1736: PetscFunctionReturn(PETSC_SUCCESS);
1737: }
1739: /*@
1740: DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1
1742: Collective
1744: Input Parameters:
1745: + dm - The `DM`
1746: - locX - The coefficient vector u_h
1748: Output Parameter:
1749: . locC - A `Vec` which holds the Clement interpolant of the function
1751: Level: developer
1753: Note:
1754: $ 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
1756: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1757: @*/
1758: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
1759: {
1760: PetscInt debug = ((DM_Plex *)dm->data)->printFEM;
1761: DM dmc;
1762: PetscQuadrature quad;
1763: PetscScalar *interpolant, *valsum;
1764: PetscFEGeom fegeom;
1765: PetscReal *coords;
1766: const PetscReal *quadPoints, *quadWeights;
1767: PetscInt dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;
1769: PetscFunctionBegin;
1770: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1771: PetscCall(VecGetDM(locC, &dmc));
1772: PetscCall(VecSet(locC, 0.0));
1773: PetscCall(DMGetDimension(dm, &dim));
1774: PetscCall(DMGetCoordinateDim(dm, &cdim));
1775: fegeom.dimEmbed = cdim;
1776: PetscCall(DMGetNumFields(dm, &Nf));
1777: PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1778: for (f = 0; f < Nf; ++f) {
1779: PetscObject obj;
1780: PetscClassId id;
1781: PetscInt fNc;
1783: PetscCall(DMGetField(dm, f, NULL, &obj));
1784: PetscCall(PetscObjectGetClassId(obj, &id));
1785: if (id == PETSCFE_CLASSID) {
1786: PetscFE fe = (PetscFE)obj;
1788: PetscCall(PetscFEGetQuadrature(fe, &quad));
1789: PetscCall(PetscFEGetNumComponents(fe, &fNc));
1790: } else if (id == PETSCFV_CLASSID) {
1791: PetscFV fv = (PetscFV)obj;
1793: PetscCall(PetscFVGetQuadrature(fv, &quad));
1794: PetscCall(PetscFVGetNumComponents(fv, &fNc));
1795: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1796: Nc += fNc;
1797: }
1798: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1799: PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
1800: PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
1801: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1802: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1803: for (v = vStart; v < vEnd; ++v) {
1804: PetscScalar volsum = 0.0;
1805: PetscInt *star = NULL;
1806: PetscInt starSize, st, fc;
1808: PetscCall(PetscArrayzero(valsum, Nc));
1809: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1810: for (st = 0; st < starSize * 2; st += 2) {
1811: const PetscInt cell = star[st];
1812: PetscScalar *val = &valsum[Nc];
1813: PetscScalar *x = NULL;
1814: PetscReal vol = 0.0;
1815: PetscInt foff = 0;
1817: if ((cell < cStart) || (cell >= cEnd)) continue;
1818: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1819: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
1820: for (f = 0; f < Nf; ++f) {
1821: PetscObject obj;
1822: PetscClassId id;
1823: PetscInt Nb, fNc, q;
1825: PetscCall(PetscArrayzero(val, Nc));
1826: PetscCall(DMGetField(dm, f, NULL, &obj));
1827: PetscCall(PetscObjectGetClassId(obj, &id));
1828: if (id == PETSCFE_CLASSID) {
1829: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
1830: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1831: } else if (id == PETSCFV_CLASSID) {
1832: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
1833: Nb = 1;
1834: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1835: for (q = 0; q < Nq; ++q) {
1836: const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
1837: PetscFEGeom qgeom;
1839: qgeom.dimEmbed = fegeom.dimEmbed;
1840: qgeom.J = &fegeom.J[q * cdim * cdim];
1841: qgeom.invJ = &fegeom.invJ[q * cdim * cdim];
1842: qgeom.detJ = &fegeom.detJ[q];
1843: 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);
1844: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
1845: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1846: for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
1847: vol += wt;
1848: }
1849: foff += Nb;
1850: }
1851: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
1852: for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
1853: volsum += vol;
1854: if (debug) {
1855: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
1856: for (fc = 0; fc < Nc; ++fc) {
1857: if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1858: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
1859: }
1860: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
1861: }
1862: }
1863: for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
1864: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1865: PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
1866: }
1867: PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1868: PetscFunctionReturn(PETSC_SUCCESS);
1869: }
1871: /*@
1872: DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1
1874: Collective
1876: Input Parameters:
1877: + dm - The `DM`
1878: - locX - The coefficient vector u_h
1880: Output Parameter:
1881: . locC - A `Vec` which holds the Clement interpolant of the gradient
1883: Level: developer
1885: Note:
1886: $\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
1888: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1889: @*/
1890: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
1891: {
1892: DM_Plex *mesh = (DM_Plex *)dm->data;
1893: PetscInt debug = mesh->printFEM;
1894: DM dmC;
1895: PetscQuadrature quad;
1896: PetscScalar *interpolant, *gradsum;
1897: PetscFEGeom fegeom;
1898: PetscReal *coords;
1899: const PetscReal *quadPoints, *quadWeights;
1900: PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;
1902: PetscFunctionBegin;
1903: PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1904: PetscCall(VecGetDM(locC, &dmC));
1905: PetscCall(VecSet(locC, 0.0));
1906: PetscCall(DMGetDimension(dm, &dim));
1907: PetscCall(DMGetCoordinateDim(dm, &coordDim));
1908: fegeom.dimEmbed = coordDim;
1909: PetscCall(DMGetNumFields(dm, &numFields));
1910: PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1911: for (field = 0; field < numFields; ++field) {
1912: PetscObject obj;
1913: PetscClassId id;
1914: PetscInt Nc;
1916: PetscCall(DMGetField(dm, field, NULL, &obj));
1917: PetscCall(PetscObjectGetClassId(obj, &id));
1918: if (id == PETSCFE_CLASSID) {
1919: PetscFE fe = (PetscFE)obj;
1921: PetscCall(PetscFEGetQuadrature(fe, &quad));
1922: PetscCall(PetscFEGetNumComponents(fe, &Nc));
1923: } else if (id == PETSCFV_CLASSID) {
1924: PetscFV fv = (PetscFV)obj;
1926: PetscCall(PetscFVGetQuadrature(fv, &quad));
1927: PetscCall(PetscFVGetNumComponents(fv, &Nc));
1928: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1929: numComponents += Nc;
1930: }
1931: PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1932: PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1933: PetscCall(PetscMalloc6(coordDim * numComponents * 2, &gradsum, coordDim * numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1934: PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1935: PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1936: for (v = vStart; v < vEnd; ++v) {
1937: PetscScalar volsum = 0.0;
1938: PetscInt *star = NULL;
1939: PetscInt starSize, st, d, fc;
1941: PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
1942: PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1943: for (st = 0; st < starSize * 2; st += 2) {
1944: const PetscInt cell = star[st];
1945: PetscScalar *grad = &gradsum[coordDim * numComponents];
1946: PetscScalar *x = NULL;
1947: PetscReal vol = 0.0;
1949: if ((cell < cStart) || (cell >= cEnd)) continue;
1950: PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1951: PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
1952: for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1953: PetscObject obj;
1954: PetscClassId id;
1955: PetscInt Nb, Nc, q, qc = 0;
1957: PetscCall(PetscArrayzero(grad, coordDim * numComponents));
1958: PetscCall(DMGetField(dm, field, NULL, &obj));
1959: PetscCall(PetscObjectGetClassId(obj, &id));
1960: if (id == PETSCFE_CLASSID) {
1961: PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1962: PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1963: } else if (id == PETSCFV_CLASSID) {
1964: PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1965: Nb = 1;
1966: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1967: for (q = 0; q < Nq; ++q) {
1968: PetscFEGeom qgeom;
1970: qgeom.dimEmbed = fegeom.dimEmbed;
1971: qgeom.J = &fegeom.J[q * coordDim * coordDim];
1972: qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim];
1973: qgeom.detJ = &fegeom.detJ[q];
1974: 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);
1975: if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
1976: else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1977: for (fc = 0; fc < Nc; ++fc) {
1978: const PetscReal wt = quadWeights[q * qNc + qc];
1980: for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
1981: }
1982: vol += quadWeights[q * qNc] * fegeom.detJ[q];
1983: }
1984: fieldOffset += Nb;
1985: qc += Nc;
1986: }
1987: PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
1988: for (fc = 0; fc < numComponents; ++fc) {
1989: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
1990: }
1991: volsum += vol;
1992: if (debug) {
1993: PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
1994: for (fc = 0; fc < numComponents; ++fc) {
1995: for (d = 0; d < coordDim; ++d) {
1996: if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1997: PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
1998: }
1999: }
2000: PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2001: }
2002: }
2003: for (fc = 0; fc < numComponents; ++fc) {
2004: for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2005: }
2006: PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2007: PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2008: }
2009: PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2010: PetscFunctionReturn(PETSC_SUCCESS);
2011: }
2013: static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
2014: {
2015: DM dmAux = NULL;
2016: PetscDS prob, probAux = NULL;
2017: PetscSection section, sectionAux;
2018: Vec locX, locA;
2019: PetscInt dim, numCells = cEnd - cStart, c, f;
2020: PetscBool useFVM = PETSC_FALSE;
2021: /* DS */
2022: PetscInt Nf, totDim, *uOff, *uOff_x, numConstants;
2023: PetscInt NfAux, totDimAux, *aOff;
2024: PetscScalar *u, *a;
2025: const PetscScalar *constants;
2026: /* Geometry */
2027: PetscFEGeom *cgeomFEM;
2028: DM dmGrad;
2029: PetscQuadrature affineQuad = NULL;
2030: Vec cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2031: PetscFVCellGeom *cgeomFVM;
2032: const PetscScalar *lgrad;
2033: PetscInt maxDegree;
2034: DMField coordField;
2035: IS cellIS;
2037: PetscFunctionBegin;
2038: PetscCall(DMGetDS(dm, &prob));
2039: PetscCall(DMGetDimension(dm, &dim));
2040: PetscCall(DMGetLocalSection(dm, §ion));
2041: PetscCall(DMGetNumFields(dm, &Nf));
2042: /* Determine which discretizations we have */
2043: for (f = 0; f < Nf; ++f) {
2044: PetscObject obj;
2045: PetscClassId id;
2047: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2048: PetscCall(PetscObjectGetClassId(obj, &id));
2049: if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2050: }
2051: /* Get local solution with boundary values */
2052: PetscCall(DMGetLocalVector(dm, &locX));
2053: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2054: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2055: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2056: /* Read DS information */
2057: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2058: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2059: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2060: PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2061: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2062: /* Read Auxiliary DS information */
2063: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2064: if (locA) {
2065: PetscCall(VecGetDM(locA, &dmAux));
2066: PetscCall(DMGetDS(dmAux, &probAux));
2067: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2068: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2069: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2070: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2071: }
2072: /* Allocate data arrays */
2073: PetscCall(PetscCalloc1(numCells * totDim, &u));
2074: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2075: /* Read out geometry */
2076: PetscCall(DMGetCoordinateField(dm, &coordField));
2077: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2078: if (maxDegree <= 1) {
2079: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2080: if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM));
2081: }
2082: if (useFVM) {
2083: PetscFV fv = NULL;
2084: Vec grad;
2085: PetscInt fStart, fEnd;
2086: PetscBool compGrad;
2088: for (f = 0; f < Nf; ++f) {
2089: PetscObject obj;
2090: PetscClassId id;
2092: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2093: PetscCall(PetscObjectGetClassId(obj, &id));
2094: if (id == PETSCFV_CLASSID) {
2095: fv = (PetscFV)obj;
2096: break;
2097: }
2098: }
2099: PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2100: PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2101: PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2102: PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2103: PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2104: PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2105: /* Reconstruct and limit cell gradients */
2106: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2107: PetscCall(DMGetGlobalVector(dmGrad, &grad));
2108: PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2109: /* Communicate gradient values */
2110: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2111: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2112: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2113: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2114: /* Handle non-essential (e.g. outflow) boundary values */
2115: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2116: PetscCall(VecGetArrayRead(locGrad, &lgrad));
2117: }
2118: /* Read out data from inputs */
2119: for (c = cStart; c < cEnd; ++c) {
2120: PetscScalar *x = NULL;
2121: PetscInt i;
2123: PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2124: for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2125: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2126: if (dmAux) {
2127: PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x));
2128: for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2129: PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x));
2130: }
2131: }
2132: /* Do integration for each field */
2133: for (f = 0; f < Nf; ++f) {
2134: PetscObject obj;
2135: PetscClassId id;
2136: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
2138: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2139: PetscCall(PetscObjectGetClassId(obj, &id));
2140: if (id == PETSCFE_CLASSID) {
2141: PetscFE fe = (PetscFE)obj;
2142: PetscQuadrature q;
2143: PetscFEGeom *chunkGeom = NULL;
2144: PetscInt Nq, Nb;
2146: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2147: PetscCall(PetscFEGetQuadrature(fe, &q));
2148: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2149: PetscCall(PetscFEGetDimension(fe, &Nb));
2150: blockSize = Nb * Nq;
2151: batchSize = numBlocks * blockSize;
2152: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2153: numChunks = numCells / (numBatches * batchSize);
2154: Ne = numChunks * numBatches * batchSize;
2155: Nr = numCells % (numBatches * batchSize);
2156: offset = numCells - Nr;
2157: if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM));
2158: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2159: PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2160: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2161: PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, &a[offset * totDimAux], &cintegral[offset * Nf]));
2162: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2163: if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2164: } else if (id == PETSCFV_CLASSID) {
2165: PetscInt foff;
2166: PetscPointFunc obj_func;
2167: PetscScalar lint;
2169: PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2170: PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2171: if (obj_func) {
2172: for (c = 0; c < numCells; ++c) {
2173: PetscScalar *u_x;
2175: PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2176: obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim * c + foff], NULL, u_x, aOff, NULL, &a[totDimAux * c], NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
2177: cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2178: }
2179: }
2180: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2181: }
2182: /* Cleanup data arrays */
2183: if (useFVM) {
2184: PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2185: PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2186: PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2187: PetscCall(VecDestroy(&faceGeometryFVM));
2188: PetscCall(VecDestroy(&cellGeometryFVM));
2189: PetscCall(DMDestroy(&dmGrad));
2190: }
2191: if (dmAux) PetscCall(PetscFree(a));
2192: PetscCall(PetscFree(u));
2193: /* Cleanup */
2194: if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2195: PetscCall(PetscQuadratureDestroy(&affineQuad));
2196: PetscCall(ISDestroy(&cellIS));
2197: PetscCall(DMRestoreLocalVector(dm, &locX));
2198: PetscFunctionReturn(PETSC_SUCCESS);
2199: }
2201: /*@
2202: DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user
2204: Input Parameters:
2205: + dm - The mesh
2206: . X - Global input vector
2207: - user - The user context
2209: Output Parameter:
2210: . integral - Integral for each field
2212: Level: developer
2214: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2215: @*/
2216: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2217: {
2218: DM_Plex *mesh = (DM_Plex *)dm->data;
2219: PetscScalar *cintegral, *lintegral;
2220: PetscInt Nf, f, cellHeight, cStart, cEnd, cell;
2222: PetscFunctionBegin;
2226: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2227: PetscCall(DMGetNumFields(dm, &Nf));
2228: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2229: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2230: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2231: PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2232: PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2233: /* Sum up values */
2234: for (cell = cStart; cell < cEnd; ++cell) {
2235: const PetscInt c = cell - cStart;
2237: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2238: for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2239: }
2240: PetscCall(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2241: if (mesh->printFEM) {
2242: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2243: for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2244: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2245: }
2246: PetscCall(PetscFree2(lintegral, cintegral));
2247: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2248: PetscFunctionReturn(PETSC_SUCCESS);
2249: }
2251: /*@
2252: DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user
2254: Input Parameters:
2255: + dm - The mesh
2256: . X - Global input vector
2257: - user - The user context
2259: Output Parameter:
2260: . integral - Cellwise integrals for each field
2262: Level: developer
2264: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2265: @*/
2266: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2267: {
2268: DM_Plex *mesh = (DM_Plex *)dm->data;
2269: DM dmF;
2270: PetscSection sectionF;
2271: PetscScalar *cintegral, *af;
2272: PetscInt Nf, f, cellHeight, cStart, cEnd, cell;
2274: PetscFunctionBegin;
2278: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2279: PetscCall(DMGetNumFields(dm, &Nf));
2280: PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2281: PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2282: /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2283: PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2284: PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2285: /* Put values in F*/
2286: PetscCall(VecGetDM(F, &dmF));
2287: PetscCall(DMGetLocalSection(dmF, §ionF));
2288: PetscCall(VecGetArray(F, &af));
2289: for (cell = cStart; cell < cEnd; ++cell) {
2290: const PetscInt c = cell - cStart;
2291: PetscInt dof, off;
2293: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2294: PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2295: PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2296: PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2297: for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2298: }
2299: PetscCall(VecRestoreArray(F, &af));
2300: PetscCall(PetscFree(cintegral));
2301: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2302: PetscFunctionReturn(PETSC_SUCCESS);
2303: }
2305: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, 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[]), PetscScalar *fintegral, void *user)
2306: {
2307: DM plex = NULL, plexA = NULL;
2308: DMEnclosureType encAux;
2309: PetscDS prob, probAux = NULL;
2310: PetscSection section, sectionAux = NULL;
2311: Vec locA = NULL;
2312: DMField coordField;
2313: PetscInt Nf, totDim, *uOff, *uOff_x;
2314: PetscInt NfAux = 0, totDimAux = 0, *aOff = NULL;
2315: PetscScalar *u, *a = NULL;
2316: const PetscScalar *constants;
2317: PetscInt numConstants, f;
2319: PetscFunctionBegin;
2320: PetscCall(DMGetCoordinateField(dm, &coordField));
2321: PetscCall(DMConvert(dm, DMPLEX, &plex));
2322: PetscCall(DMGetDS(dm, &prob));
2323: PetscCall(DMGetLocalSection(dm, §ion));
2324: PetscCall(PetscSectionGetNumFields(section, &Nf));
2325: /* Determine which discretizations we have */
2326: for (f = 0; f < Nf; ++f) {
2327: PetscObject obj;
2328: PetscClassId id;
2330: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2331: PetscCall(PetscObjectGetClassId(obj, &id));
2332: PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2333: }
2334: /* Read DS information */
2335: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2336: PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2337: PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2338: PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2339: /* Read Auxiliary DS information */
2340: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2341: if (locA) {
2342: DM dmAux;
2344: PetscCall(VecGetDM(locA, &dmAux));
2345: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2346: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2347: PetscCall(DMGetDS(dmAux, &probAux));
2348: PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2349: PetscCall(DMGetLocalSection(dmAux, §ionAux));
2350: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2351: PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2352: }
2353: /* Integrate over points */
2354: {
2355: PetscFEGeom *fgeom, *chunkGeom = NULL;
2356: PetscInt maxDegree;
2357: PetscQuadrature qGeom = NULL;
2358: const PetscInt *points;
2359: PetscInt numFaces, face, Nq, field;
2360: PetscInt numChunks, chunkSize, chunk, Nr, offset;
2362: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2363: PetscCall(ISGetIndices(pointIS, &points));
2364: PetscCall(PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a));
2365: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2366: for (field = 0; field < Nf; ++field) {
2367: PetscFE fe;
2369: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2370: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2371: if (!qGeom) {
2372: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2373: PetscCall(PetscObjectReference((PetscObject)qGeom));
2374: }
2375: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2376: PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2377: for (face = 0; face < numFaces; ++face) {
2378: const PetscInt point = points[face], *support;
2379: PetscScalar *x = NULL;
2380: PetscInt i;
2382: PetscCall(DMPlexGetSupport(dm, point, &support));
2383: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2384: for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2385: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2386: if (locA) {
2387: PetscInt subp;
2388: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2389: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2390: for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2391: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2392: }
2393: }
2394: /* Get blocking */
2395: {
2396: PetscQuadrature q;
2397: PetscInt numBatches, batchSize, numBlocks, blockSize;
2398: PetscInt Nq, Nb;
2400: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2401: PetscCall(PetscFEGetQuadrature(fe, &q));
2402: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2403: PetscCall(PetscFEGetDimension(fe, &Nb));
2404: blockSize = Nb * Nq;
2405: batchSize = numBlocks * blockSize;
2406: chunkSize = numBatches * batchSize;
2407: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2408: numChunks = numFaces / chunkSize;
2409: Nr = numFaces % chunkSize;
2410: offset = numFaces - Nr;
2411: }
2412: /* Do integration for each field */
2413: for (chunk = 0; chunk < numChunks; ++chunk) {
2414: PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2415: PetscCall(PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral));
2416: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2417: }
2418: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2419: PetscCall(PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset * totDim], probAux, a ? &a[offset * totDimAux] : NULL, &fintegral[offset * Nf]));
2420: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2421: /* Cleanup data arrays */
2422: PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2423: PetscCall(PetscQuadratureDestroy(&qGeom));
2424: PetscCall(PetscFree2(u, a));
2425: PetscCall(ISRestoreIndices(pointIS, &points));
2426: }
2427: }
2428: if (plex) PetscCall(DMDestroy(&plex));
2429: if (plexA) PetscCall(DMDestroy(&plexA));
2430: PetscFunctionReturn(PETSC_SUCCESS);
2431: }
2433: /*@
2434: DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user
2436: Input Parameters:
2437: + dm - The mesh
2438: . X - Global input vector
2439: . label - The boundary `DMLabel`
2440: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2441: . vals - The label values to use, or NULL for all values
2442: . func - The function to integrate along the boundary
2443: - user - The user context
2445: Output Parameter:
2446: . integral - Integral for each field
2448: Level: developer
2450: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2451: @*/
2452: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], 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[]), PetscScalar *integral, void *user)
2453: {
2454: Vec locX;
2455: PetscSection section;
2456: DMLabel depthLabel;
2457: IS facetIS;
2458: PetscInt dim, Nf, f, v;
2460: PetscFunctionBegin;
2466: PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2467: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2468: PetscCall(DMGetDimension(dm, &dim));
2469: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2470: PetscCall(DMGetLocalSection(dm, §ion));
2471: PetscCall(PetscSectionGetNumFields(section, &Nf));
2472: /* Get local solution with boundary values */
2473: PetscCall(DMGetLocalVector(dm, &locX));
2474: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2475: PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2476: PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2477: /* Loop over label values */
2478: PetscCall(PetscArrayzero(integral, Nf));
2479: for (v = 0; v < numVals; ++v) {
2480: IS pointIS;
2481: PetscInt numFaces, face;
2482: PetscScalar *fintegral;
2484: PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2485: if (!pointIS) continue; /* No points with that id on this process */
2486: {
2487: IS isectIS;
2489: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2490: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2491: PetscCall(ISDestroy(&pointIS));
2492: pointIS = isectIS;
2493: }
2494: PetscCall(ISGetLocalSize(pointIS, &numFaces));
2495: PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2496: PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user));
2497: /* Sum point contributions into integral */
2498: for (f = 0; f < Nf; ++f)
2499: for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2500: PetscCall(PetscFree(fintegral));
2501: PetscCall(ISDestroy(&pointIS));
2502: }
2503: PetscCall(DMRestoreLocalVector(dm, &locX));
2504: PetscCall(ISDestroy(&facetIS));
2505: PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2506: PetscFunctionReturn(PETSC_SUCCESS);
2507: }
2509: /*@
2510: DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix I from the coarse `DM` to a uniformly refined `DM`.
2512: Input Parameters:
2513: + dmc - The coarse mesh
2514: . dmf - The fine mesh
2515: . isRefined - Flag indicating regular refinement, rather than the same topology
2516: - user - The user context
2518: Output Parameter:
2519: . In - The interpolation matrix
2521: Level: developer
2523: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2524: @*/
2525: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2526: {
2527: DM_Plex *mesh = (DM_Plex *)dmc->data;
2528: const char *name = "Interpolator";
2529: PetscFE *feRef;
2530: PetscFV *fvRef;
2531: PetscSection fsection, fglobalSection;
2532: PetscSection csection, cglobalSection;
2533: PetscScalar *elemMat;
2534: PetscInt dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2535: PetscInt cTotDim = 0, rTotDim = 0;
2537: PetscFunctionBegin;
2538: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2539: PetscCall(DMGetDimension(dmf, &dim));
2540: PetscCall(DMGetLocalSection(dmf, &fsection));
2541: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2542: PetscCall(DMGetLocalSection(dmc, &csection));
2543: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2544: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2545: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2546: PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2547: for (f = 0; f < Nf; ++f) {
2548: PetscObject obj, objc;
2549: PetscClassId id, idc;
2550: PetscInt rNb = 0, Nc = 0, cNb = 0;
2552: PetscCall(DMGetField(dmf, f, NULL, &obj));
2553: PetscCall(PetscObjectGetClassId(obj, &id));
2554: if (id == PETSCFE_CLASSID) {
2555: PetscFE fe = (PetscFE)obj;
2557: if (isRefined) {
2558: PetscCall(PetscFERefine(fe, &feRef[f]));
2559: } else {
2560: PetscCall(PetscObjectReference((PetscObject)fe));
2561: feRef[f] = fe;
2562: }
2563: PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2564: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2565: } else if (id == PETSCFV_CLASSID) {
2566: PetscFV fv = (PetscFV)obj;
2567: PetscDualSpace Q;
2569: if (isRefined) {
2570: PetscCall(PetscFVRefine(fv, &fvRef[f]));
2571: } else {
2572: PetscCall(PetscObjectReference((PetscObject)fv));
2573: fvRef[f] = fv;
2574: }
2575: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2576: PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2577: PetscCall(PetscFVGetDualSpace(fv, &Q));
2578: PetscCall(PetscFVGetNumComponents(fv, &Nc));
2579: }
2580: PetscCall(DMGetField(dmc, f, NULL, &objc));
2581: PetscCall(PetscObjectGetClassId(objc, &idc));
2582: if (idc == PETSCFE_CLASSID) {
2583: PetscFE fe = (PetscFE)objc;
2585: PetscCall(PetscFEGetDimension(fe, &cNb));
2586: } else if (id == PETSCFV_CLASSID) {
2587: PetscFV fv = (PetscFV)obj;
2588: PetscDualSpace Q;
2590: PetscCall(PetscFVGetDualSpace(fv, &Q));
2591: PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
2592: }
2593: rTotDim += rNb;
2594: cTotDim += cNb;
2595: }
2596: PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
2597: PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
2598: for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2599: PetscDualSpace Qref;
2600: PetscQuadrature f;
2601: const PetscReal *qpoints, *qweights;
2602: PetscReal *points;
2603: PetscInt npoints = 0, Nc, Np, fpdim, i, k, p, d;
2605: /* Compose points from all dual basis functionals */
2606: if (feRef[fieldI]) {
2607: PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
2608: PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
2609: } else {
2610: PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
2611: PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
2612: }
2613: PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
2614: for (i = 0; i < fpdim; ++i) {
2615: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2616: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
2617: npoints += Np;
2618: }
2619: PetscCall(PetscMalloc1(npoints * dim, &points));
2620: for (i = 0, k = 0; i < fpdim; ++i) {
2621: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2622: PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
2623: for (p = 0; p < Np; ++p, ++k)
2624: for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2625: }
2627: for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2628: PetscObject obj;
2629: PetscClassId id;
2630: PetscInt NcJ = 0, cpdim = 0, j, qNc;
2632: PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
2633: PetscCall(PetscObjectGetClassId(obj, &id));
2634: if (id == PETSCFE_CLASSID) {
2635: PetscFE fe = (PetscFE)obj;
2636: PetscTabulation T = NULL;
2638: /* Evaluate basis at points */
2639: PetscCall(PetscFEGetNumComponents(fe, &NcJ));
2640: PetscCall(PetscFEGetDimension(fe, &cpdim));
2641: /* For now, fields only interpolate themselves */
2642: if (fieldI == fieldJ) {
2643: 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);
2644: PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
2645: for (i = 0, k = 0; i < fpdim; ++i) {
2646: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2647: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2648: 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);
2649: for (p = 0; p < Np; ++p, ++k) {
2650: for (j = 0; j < cpdim; ++j) {
2651: /*
2652: cTotDim: Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2653: offsetI, offsetJ: Offsets into the larger element interpolation matrix for different fields
2654: fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2655: qNC, Nc, Ncj, c: Number of components in this field
2656: Np, p: Number of quad points in the fine grid functional i
2657: k: i*Np + p, overall point number for the interpolation
2658: */
2659: 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];
2660: }
2661: }
2662: }
2663: PetscCall(PetscTabulationDestroy(&T));
2664: }
2665: } else if (id == PETSCFV_CLASSID) {
2666: PetscFV fv = (PetscFV)obj;
2668: /* Evaluate constant function at points */
2669: PetscCall(PetscFVGetNumComponents(fv, &NcJ));
2670: cpdim = 1;
2671: /* For now, fields only interpolate themselves */
2672: if (fieldI == fieldJ) {
2673: 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);
2674: for (i = 0, k = 0; i < fpdim; ++i) {
2675: PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2676: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2677: 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);
2678: for (p = 0; p < Np; ++p, ++k) {
2679: for (j = 0; j < cpdim; ++j) {
2680: for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
2681: }
2682: }
2683: }
2684: }
2685: }
2686: offsetJ += cpdim;
2687: }
2688: offsetI += fpdim;
2689: PetscCall(PetscFree(points));
2690: }
2691: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
2692: /* Preallocate matrix */
2693: {
2694: Mat preallocator;
2695: PetscScalar *vals;
2696: PetscInt *cellCIndices, *cellFIndices;
2697: PetscInt locRows, locCols, cell;
2699: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2700: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
2701: PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
2702: PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2703: PetscCall(MatSetUp(preallocator));
2704: PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
2705: for (cell = cStart; cell < cEnd; ++cell) {
2706: if (isRefined) {
2707: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
2708: PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
2709: } else {
2710: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, preallocator, cell, vals, INSERT_VALUES));
2711: }
2712: }
2713: PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
2714: PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
2715: PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
2716: PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
2717: PetscCall(MatDestroy(&preallocator));
2718: }
2719: /* Fill matrix */
2720: PetscCall(MatZeroEntries(In));
2721: for (c = cStart; c < cEnd; ++c) {
2722: if (isRefined) {
2723: PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2724: } else {
2725: PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2726: }
2727: }
2728: for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
2729: PetscCall(PetscFree2(feRef, fvRef));
2730: PetscCall(PetscFree(elemMat));
2731: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2732: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2733: if (mesh->printFEM > 1) {
2734: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
2735: PetscCall(MatChop(In, 1.0e-10));
2736: PetscCall(MatView(In, NULL));
2737: }
2738: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2739: PetscFunctionReturn(PETSC_SUCCESS);
2740: }
2742: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2743: {
2744: SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
2745: }
2747: /*@
2748: DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix I from the coarse `DM` to a non-nested fine `DM`.
2750: Input Parameters:
2751: + dmf - The fine mesh
2752: . dmc - The coarse mesh
2753: - user - The user context
2755: Output Parameter:
2756: . In - The interpolation matrix
2758: Level: developer
2760: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
2761: @*/
2762: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2763: {
2764: DM_Plex *mesh = (DM_Plex *)dmf->data;
2765: const char *name = "Interpolator";
2766: PetscDS prob;
2767: Mat interp;
2768: PetscSection fsection, globalFSection;
2769: PetscSection csection, globalCSection;
2770: PetscInt locRows, locCols;
2771: PetscReal *x, *v0, *J, *invJ, detJ;
2772: PetscReal *v0c, *Jc, *invJc, detJc;
2773: PetscScalar *elemMat;
2774: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;
2776: PetscFunctionBegin;
2777: PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2778: PetscCall(DMGetCoordinateDim(dmc, &dim));
2779: PetscCall(DMGetDS(dmc, &prob));
2780: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2781: PetscCall(PetscDSGetNumFields(prob, &Nf));
2782: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2783: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2784: PetscCall(DMGetLocalSection(dmf, &fsection));
2785: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2786: PetscCall(DMGetLocalSection(dmc, &csection));
2787: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2788: PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
2789: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2790: PetscCall(PetscMalloc1(totDim, &elemMat));
2792: PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2793: PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
2794: PetscCall(MatSetType(interp, MATPREALLOCATOR));
2795: PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2796: PetscCall(MatSetUp(interp));
2797: for (s = 0; s < 2; ++s) {
2798: for (field = 0; field < Nf; ++field) {
2799: PetscObject obj;
2800: PetscClassId id;
2801: PetscDualSpace Q = NULL;
2802: PetscTabulation T = NULL;
2803: PetscQuadrature f;
2804: const PetscReal *qpoints, *qweights;
2805: PetscInt Nc, qNc, Np, fpdim, off, i, d;
2807: PetscCall(PetscDSGetFieldOffset(prob, field, &off));
2808: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2809: PetscCall(PetscObjectGetClassId(obj, &id));
2810: if (id == PETSCFE_CLASSID) {
2811: PetscFE fe = (PetscFE)obj;
2813: PetscCall(PetscFEGetDualSpace(fe, &Q));
2814: PetscCall(PetscFEGetNumComponents(fe, &Nc));
2815: if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
2816: } else if (id == PETSCFV_CLASSID) {
2817: PetscFV fv = (PetscFV)obj;
2819: PetscCall(PetscFVGetDualSpace(fv, &Q));
2820: Nc = 1;
2821: } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2822: PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
2823: /* For each fine grid cell */
2824: for (cell = cStart; cell < cEnd; ++cell) {
2825: PetscInt *findices, *cindices;
2826: PetscInt numFIndices, numCIndices;
2828: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2829: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2830: PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
2831: for (i = 0; i < fpdim; ++i) {
2832: Vec pointVec;
2833: PetscScalar *pV;
2834: PetscSF coarseCellSF = NULL;
2835: const PetscSFNode *coarseCells;
2836: PetscInt numCoarseCells, cpdim, row = findices[i + off], q, c, j;
2838: /* Get points from the dual basis functional quadrature */
2839: PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
2840: PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
2841: 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);
2842: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
2843: PetscCall(VecSetBlockSize(pointVec, dim));
2844: PetscCall(VecGetArray(pointVec, &pV));
2845: for (q = 0; q < Np; ++q) {
2846: const PetscReal xi0[3] = {-1., -1., -1.};
2848: /* Transform point to real space */
2849: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
2850: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
2851: }
2852: PetscCall(VecRestoreArray(pointVec, &pV));
2853: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2854: /* OPT: Read this out from preallocation information */
2855: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
2856: /* Update preallocation info */
2857: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
2858: PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
2859: PetscCall(VecGetArray(pointVec, &pV));
2860: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2861: PetscReal pVReal[3];
2862: const PetscReal xi0[3] = {-1., -1., -1.};
2864: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2865: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim));
2866: else cpdim = 1;
2868: if (s) {
2869: /* Transform points from real space to coarse reference space */
2870: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
2871: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
2872: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
2874: if (id == PETSCFE_CLASSID) {
2875: /* Evaluate coarse basis on contained point */
2876: PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
2877: PetscCall(PetscArrayzero(elemMat, cpdim));
2878: /* Get elemMat entries by multiplying by weight */
2879: for (j = 0; j < cpdim; ++j) {
2880: for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
2881: }
2882: } else {
2883: for (j = 0; j < cpdim; ++j) {
2884: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
2885: }
2886: }
2887: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
2888: }
2889: /* Update interpolator */
2890: PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
2891: PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
2892: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
2893: }
2894: PetscCall(VecRestoreArray(pointVec, &pV));
2895: PetscCall(PetscSFDestroy(&coarseCellSF));
2896: PetscCall(VecDestroy(&pointVec));
2897: }
2898: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2899: }
2900: if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
2901: }
2902: if (!s) {
2903: PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
2904: PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
2905: PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
2906: PetscCall(MatDestroy(&interp));
2907: interp = In;
2908: }
2909: }
2910: PetscCall(PetscFree3(v0, J, invJ));
2911: PetscCall(PetscFree3(v0c, Jc, invJc));
2912: PetscCall(PetscFree(elemMat));
2913: PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2914: PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2915: PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2916: PetscFunctionReturn(PETSC_SUCCESS);
2917: }
2919: /*@
2920: DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse `DM` to a non-nested fine `DM`.
2922: Input Parameters:
2923: + dmf - The fine mesh
2924: . dmc - The coarse mesh
2925: - user - The user context
2927: Output Parameter:
2928: . mass - The mass matrix
2930: Level: developer
2932: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2933: @*/
2934: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
2935: {
2936: DM_Plex *mesh = (DM_Plex *)dmf->data;
2937: const char *name = "Mass Matrix";
2938: PetscDS prob;
2939: PetscSection fsection, csection, globalFSection, globalCSection;
2940: PetscHSetIJ ht;
2941: PetscLayout rLayout;
2942: PetscInt *dnz, *onz;
2943: PetscInt locRows, rStart, rEnd;
2944: PetscReal *x, *v0, *J, *invJ, detJ;
2945: PetscReal *v0c, *Jc, *invJc, detJc;
2946: PetscScalar *elemMat;
2947: PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell;
2949: PetscFunctionBegin;
2950: PetscCall(DMGetCoordinateDim(dmc, &dim));
2951: PetscCall(DMGetDS(dmc, &prob));
2952: PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2953: PetscCall(PetscDSGetNumFields(prob, &Nf));
2954: PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2955: PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2956: PetscCall(DMGetLocalSection(dmf, &fsection));
2957: PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2958: PetscCall(DMGetLocalSection(dmc, &csection));
2959: PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2960: PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
2961: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2962: PetscCall(PetscMalloc1(totDim, &elemMat));
2964: PetscCall(MatGetLocalSize(mass, &locRows, NULL));
2965: PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
2966: PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
2967: PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
2968: PetscCall(PetscLayoutSetUp(rLayout));
2969: PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
2970: PetscCall(PetscLayoutDestroy(&rLayout));
2971: PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
2972: PetscCall(PetscHSetIJCreate(&ht));
2973: for (field = 0; field < Nf; ++field) {
2974: PetscObject obj;
2975: PetscClassId id;
2976: PetscQuadrature quad;
2977: const PetscReal *qpoints;
2978: PetscInt Nq, Nc, i, d;
2980: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2981: PetscCall(PetscObjectGetClassId(obj, &id));
2982: if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
2983: else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
2984: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
2985: /* For each fine grid cell */
2986: for (cell = cStart; cell < cEnd; ++cell) {
2987: Vec pointVec;
2988: PetscScalar *pV;
2989: PetscSF coarseCellSF = NULL;
2990: const PetscSFNode *coarseCells;
2991: PetscInt numCoarseCells, q, c;
2992: PetscInt *findices, *cindices;
2993: PetscInt numFIndices, numCIndices;
2995: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2996: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2997: /* Get points from the quadrature */
2998: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
2999: PetscCall(VecSetBlockSize(pointVec, dim));
3000: PetscCall(VecGetArray(pointVec, &pV));
3001: for (q = 0; q < Nq; ++q) {
3002: const PetscReal xi0[3] = {-1., -1., -1.};
3004: /* Transform point to real space */
3005: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3006: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3007: }
3008: PetscCall(VecRestoreArray(pointVec, &pV));
3009: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3010: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3011: PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3012: /* Update preallocation info */
3013: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3014: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3015: {
3016: PetscHashIJKey key;
3017: PetscBool missing;
3019: for (i = 0; i < numFIndices; ++i) {
3020: key.i = findices[i];
3021: if (key.i >= 0) {
3022: /* Get indices for coarse elements */
3023: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3024: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3025: for (c = 0; c < numCIndices; ++c) {
3026: key.j = cindices[c];
3027: if (key.j < 0) continue;
3028: PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3029: if (missing) {
3030: if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3031: else ++onz[key.i - rStart];
3032: }
3033: }
3034: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3035: }
3036: }
3037: }
3038: }
3039: PetscCall(PetscSFDestroy(&coarseCellSF));
3040: PetscCall(VecDestroy(&pointVec));
3041: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3042: }
3043: }
3044: PetscCall(PetscHSetIJDestroy(&ht));
3045: PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3046: PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3047: PetscCall(PetscFree2(dnz, onz));
3048: for (field = 0; field < Nf; ++field) {
3049: PetscObject obj;
3050: PetscClassId id;
3051: PetscTabulation T, Tfine;
3052: PetscQuadrature quad;
3053: const PetscReal *qpoints, *qweights;
3054: PetscInt Nq, Nc, i, d;
3056: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3057: PetscCall(PetscObjectGetClassId(obj, &id));
3058: if (id == PETSCFE_CLASSID) {
3059: PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3060: PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3061: PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3062: } else {
3063: PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3064: }
3065: PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3066: /* For each fine grid cell */
3067: for (cell = cStart; cell < cEnd; ++cell) {
3068: Vec pointVec;
3069: PetscScalar *pV;
3070: PetscSF coarseCellSF = NULL;
3071: const PetscSFNode *coarseCells;
3072: PetscInt numCoarseCells, cpdim, q, c, j;
3073: PetscInt *findices, *cindices;
3074: PetscInt numFIndices, numCIndices;
3076: PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3077: PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3078: /* Get points from the quadrature */
3079: PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3080: PetscCall(VecSetBlockSize(pointVec, dim));
3081: PetscCall(VecGetArray(pointVec, &pV));
3082: for (q = 0; q < Nq; ++q) {
3083: const PetscReal xi0[3] = {-1., -1., -1.};
3085: /* Transform point to real space */
3086: CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3087: for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3088: }
3089: PetscCall(VecRestoreArray(pointVec, &pV));
3090: /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3091: PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3092: /* Update matrix */
3093: PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3094: PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3095: PetscCall(VecGetArray(pointVec, &pV));
3096: for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3097: PetscReal pVReal[3];
3098: const PetscReal xi0[3] = {-1., -1., -1.};
3100: PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3101: /* Transform points from real space to coarse reference space */
3102: PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
3103: for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
3104: CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);
3106: if (id == PETSCFE_CLASSID) {
3107: PetscFE fe = (PetscFE)obj;
3109: /* Evaluate coarse basis on contained point */
3110: PetscCall(PetscFEGetDimension(fe, &cpdim));
3111: PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3112: /* Get elemMat entries by multiplying by weight */
3113: for (i = 0; i < numFIndices; ++i) {
3114: PetscCall(PetscArrayzero(elemMat, cpdim));
3115: for (j = 0; j < cpdim; ++j) {
3116: 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;
3117: }
3118: /* Update interpolator */
3119: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3120: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3121: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3122: }
3123: } else {
3124: cpdim = 1;
3125: for (i = 0; i < numFIndices; ++i) {
3126: PetscCall(PetscArrayzero(elemMat, cpdim));
3127: for (j = 0; j < cpdim; ++j) {
3128: for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3129: }
3130: /* Update interpolator */
3131: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3132: 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));
3133: PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3134: PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3135: }
3136: }
3137: PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3138: }
3139: PetscCall(VecRestoreArray(pointVec, &pV));
3140: PetscCall(PetscSFDestroy(&coarseCellSF));
3141: PetscCall(VecDestroy(&pointVec));
3142: PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3143: }
3144: if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3145: }
3146: PetscCall(PetscFree3(v0, J, invJ));
3147: PetscCall(PetscFree3(v0c, Jc, invJc));
3148: PetscCall(PetscFree(elemMat));
3149: PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3150: PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3151: PetscFunctionReturn(PETSC_SUCCESS);
3152: }
3154: /*@
3155: DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns
3157: Input Parameters:
3158: + dmc - The coarse mesh
3159: - dmf - The fine mesh
3160: - user - The user context
3162: Output Parameter:
3163: . sc - The mapping
3165: Level: developer
3167: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3168: @*/
3169: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3170: {
3171: PetscDS prob;
3172: PetscFE *feRef;
3173: PetscFV *fvRef;
3174: Vec fv, cv;
3175: IS fis, cis;
3176: PetscSection fsection, fglobalSection, csection, cglobalSection;
3177: PetscInt *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3178: PetscInt cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3179: PetscBool *needAvg;
3181: PetscFunctionBegin;
3182: PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3183: PetscCall(DMGetDimension(dmf, &dim));
3184: PetscCall(DMGetLocalSection(dmf, &fsection));
3185: PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3186: PetscCall(DMGetLocalSection(dmc, &csection));
3187: PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3188: PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3189: PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3190: PetscCall(DMGetDS(dmc, &prob));
3191: PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3192: for (f = 0; f < Nf; ++f) {
3193: PetscObject obj;
3194: PetscClassId id;
3195: PetscInt fNb = 0, Nc = 0;
3197: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3198: PetscCall(PetscObjectGetClassId(obj, &id));
3199: if (id == PETSCFE_CLASSID) {
3200: PetscFE fe = (PetscFE)obj;
3201: PetscSpace sp;
3202: PetscInt maxDegree;
3204: PetscCall(PetscFERefine(fe, &feRef[f]));
3205: PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3206: PetscCall(PetscFEGetNumComponents(fe, &Nc));
3207: PetscCall(PetscFEGetBasisSpace(fe, &sp));
3208: PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3209: if (!maxDegree) needAvg[f] = PETSC_TRUE;
3210: } else if (id == PETSCFV_CLASSID) {
3211: PetscFV fv = (PetscFV)obj;
3212: PetscDualSpace Q;
3214: PetscCall(PetscFVRefine(fv, &fvRef[f]));
3215: PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3216: PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3217: PetscCall(PetscFVGetNumComponents(fv, &Nc));
3218: needAvg[f] = PETSC_TRUE;
3219: }
3220: fTotDim += fNb;
3221: }
3222: PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3223: PetscCall(PetscMalloc1(cTotDim, &cmap));
3224: for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3225: PetscFE feC;
3226: PetscFV fvC;
3227: PetscDualSpace QF, QC;
3228: PetscInt order = -1, NcF, NcC, fpdim, cpdim;
3230: if (feRef[field]) {
3231: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3232: PetscCall(PetscFEGetNumComponents(feC, &NcC));
3233: PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3234: PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3235: PetscCall(PetscDualSpaceGetOrder(QF, &order));
3236: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3237: PetscCall(PetscFEGetDualSpace(feC, &QC));
3238: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3239: } else {
3240: PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3241: PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3242: PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3243: PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3244: PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3245: PetscCall(PetscFVGetDualSpace(fvC, &QC));
3246: PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3247: }
3248: 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);
3249: for (c = 0; c < cpdim; ++c) {
3250: PetscQuadrature cfunc;
3251: const PetscReal *cqpoints, *cqweights;
3252: PetscInt NqcC, NpC;
3253: PetscBool found = PETSC_FALSE;
3255: PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3256: PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3257: 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);
3258: PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3259: for (f = 0; f < fpdim; ++f) {
3260: PetscQuadrature ffunc;
3261: const PetscReal *fqpoints, *fqweights;
3262: PetscReal sum = 0.0;
3263: PetscInt NqcF, NpF;
3265: PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3266: PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3267: 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);
3268: if (NpC != NpF) continue;
3269: for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3270: if (sum > 1.0e-9) continue;
3271: for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3272: if (sum < 1.0e-9) continue;
3273: cmap[offsetC + c] = offsetF + f;
3274: found = PETSC_TRUE;
3275: break;
3276: }
3277: if (!found) {
3278: /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3279: if (fvRef[field] || (feRef[field] && order == 0)) {
3280: cmap[offsetC + c] = offsetF + 0;
3281: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3282: }
3283: }
3284: offsetC += cpdim;
3285: offsetF += fpdim;
3286: }
3287: for (f = 0; f < Nf; ++f) {
3288: PetscCall(PetscFEDestroy(&feRef[f]));
3289: PetscCall(PetscFVDestroy(&fvRef[f]));
3290: }
3291: PetscCall(PetscFree3(feRef, fvRef, needAvg));
3293: PetscCall(DMGetGlobalVector(dmf, &fv));
3294: PetscCall(DMGetGlobalVector(dmc, &cv));
3295: PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3296: PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3297: PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3298: PetscCall(PetscMalloc1(m, &cindices));
3299: PetscCall(PetscMalloc1(m, &findices));
3300: for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3301: for (c = cStart; c < cEnd; ++c) {
3302: PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3303: for (d = 0; d < cTotDim; ++d) {
3304: if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3305: PetscCheck(!(findices[cellCIndices[d] - startC] >= 0) || !(findices[cellCIndices[d] - startC] != cellFIndices[cmap[d]]), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Coarse dof %" PetscInt_FMT " maps to both %" PetscInt_FMT " and %" PetscInt_FMT, cindices[cellCIndices[d] - startC], findices[cellCIndices[d] - startC], cellFIndices[cmap[d]]);
3306: cindices[cellCIndices[d] - startC] = cellCIndices[d];
3307: findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3308: }
3309: }
3310: PetscCall(PetscFree(cmap));
3311: PetscCall(PetscFree2(cellCIndices, cellFIndices));
3313: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3314: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3315: PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3316: PetscCall(ISDestroy(&cis));
3317: PetscCall(ISDestroy(&fis));
3318: PetscCall(DMRestoreGlobalVector(dmf, &fv));
3319: PetscCall(DMRestoreGlobalVector(dmc, &cv));
3320: PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3321: PetscFunctionReturn(PETSC_SUCCESS);
3322: }
3324: /*@C
3325: DMPlexGetCellFields - Retrieve the field values values for a chunk of cells
3327: Input Parameters:
3328: + dm - The `DM`
3329: . cellIS - The cells to include
3330: . locX - A local vector with the solution fields
3331: . locX_t - A local vector with solution field time derivatives, or NULL
3332: - locA - A local vector with auxiliary fields, or NULL
3334: Output Parameters:
3335: + u - The field coefficients
3336: . u_t - The fields derivative coefficients
3337: - a - The auxiliary field coefficients
3339: Level: developer
3341: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3342: @*/
3343: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3344: {
3345: DM plex, plexA = NULL;
3346: DMEnclosureType encAux;
3347: PetscSection section, sectionAux;
3348: PetscDS prob;
3349: const PetscInt *cells;
3350: PetscInt cStart, cEnd, numCells, totDim, totDimAux, c;
3352: PetscFunctionBegin;
3360: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3361: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3362: PetscCall(DMGetLocalSection(dm, §ion));
3363: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3364: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3365: if (locA) {
3366: DM dmAux;
3367: PetscDS probAux;
3369: PetscCall(VecGetDM(locA, &dmAux));
3370: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3371: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3372: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3373: PetscCall(DMGetDS(dmAux, &probAux));
3374: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3375: }
3376: numCells = cEnd - cStart;
3377: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3378: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3379: else *u_t = NULL;
3380: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3381: else *a = NULL;
3382: for (c = cStart; c < cEnd; ++c) {
3383: const PetscInt cell = cells ? cells[c] : c;
3384: const PetscInt cind = c - cStart;
3385: PetscScalar *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3386: PetscInt i;
3388: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3389: for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3390: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3391: if (locX_t) {
3392: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3393: for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3394: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3395: }
3396: if (locA) {
3397: PetscInt subcell;
3398: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3399: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3400: for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3401: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3402: }
3403: }
3404: PetscCall(DMDestroy(&plex));
3405: if (locA) PetscCall(DMDestroy(&plexA));
3406: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3407: PetscFunctionReturn(PETSC_SUCCESS);
3408: }
3410: /*@C
3411: DMPlexRestoreCellFields - Restore the field values values for a chunk of cells
3413: Input Parameters:
3414: + dm - The `DM`
3415: . cellIS - The cells to include
3416: . locX - A local vector with the solution fields
3417: . locX_t - A local vector with solution field time derivatives, or NULL
3418: - locA - A local vector with auxiliary fields, or NULL
3420: Output Parameters:
3421: + u - The field coefficients
3422: . u_t - The fields derivative coefficients
3423: - a - The auxiliary field coefficients
3425: Level: developer
3427: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3428: @*/
3429: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3430: {
3431: PetscFunctionBegin;
3432: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3433: if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3434: if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3435: PetscFunctionReturn(PETSC_SUCCESS);
3436: }
3438: PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3439: {
3440: DM plex, plexA = NULL;
3441: DMEnclosureType encAux;
3442: PetscSection section, sectionAux;
3443: PetscDS ds, dsIn;
3444: const PetscInt *cells;
3445: PetscInt cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;
3447: PetscFunctionBegin;
3456: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3457: numCells = cEnd - cStart;
3458: PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3459: PetscCall(DMGetLocalSection(dm, §ion));
3460: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3461: PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3462: PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3463: if (locA) {
3464: DM dmAux;
3465: PetscDS probAux;
3467: PetscCall(VecGetDM(locA, &dmAux));
3468: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3469: PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3470: PetscCall(DMGetLocalSection(dmAux, §ionAux));
3471: PetscCall(DMGetDS(dmAux, &probAux));
3472: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3473: }
3474: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3475: if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3476: else {
3477: *u_t = NULL;
3478: }
3479: if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3480: else {
3481: *a = NULL;
3482: }
3483: // Loop over cohesive cells
3484: for (c = cStart; c < cEnd; ++c) {
3485: const PetscInt cell = cells ? cells[c] : c;
3486: const PetscInt cind = c - cStart;
3487: PetscScalar *xf = NULL, *xc = NULL, *x = NULL, *x_t = NULL, *ul = &(*u)[cind * totDim];
3488: const PetscInt *cone, *ornt;
3489: PetscInt Nx = 0, Nxf, s;
3491: PetscCall(DMPlexGetCone(dm, cell, &cone));
3492: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3493: // Put in cohesive unknowns
3494: PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3495: for (f = 0; f < Nf; ++f) {
3496: PetscInt fdofIn, foff, foffIn;
3497: PetscBool cohesive;
3499: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3500: if (!cohesive) continue;
3501: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3502: PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3503: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3504: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3505: Nx += fdofIn;
3506: }
3507: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3508: // Loop over sides of surface
3509: for (s = 0; s < 2; ++s) {
3510: const PetscInt *support;
3511: const PetscInt face = cone[s];
3512: PetscInt ssize, ncell, Nxc;
3514: // I don't think I need the face to have 0 orientation in the hybrid cell
3515: //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]);
3516: PetscCall(DMPlexGetSupport(dm, face, &support));
3517: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3518: if (support[0] == cell) ncell = support[1];
3519: else if (support[1] == cell) ncell = support[0];
3520: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3521: // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3522: PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3523: for (f = 0; f < Nf; ++f) {
3524: PetscInt fdofIn, foffIn;
3525: PetscBool cohesive;
3527: PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3528: if (cohesive) continue;
3529: PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3530: PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3531: for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i];
3532: Nx += fdofIn;
3533: }
3534: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3535: }
3536: 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);
3538: if (locX_t) {
3539: PetscScalar *ul_t = &(*u_t)[cind * totDim];
3541: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3542: for (PetscInt i = 0; i < totDim; ++i) ul_t[i] = x_t[i];
3543: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3544: }
3545: if (locA) {
3546: PetscScalar *al = &(*a)[cind * totDimAux];
3547: PetscInt subcell;
3549: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3550: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3551: 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);
3552: for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3553: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3554: }
3555: }
3556: PetscCall(DMDestroy(&plex));
3557: PetscCall(DMDestroy(&plexA));
3558: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3559: PetscFunctionReturn(PETSC_SUCCESS);
3560: }
3562: /*
3563: DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interfaace
3565: Input Parameters:
3566: + dm - The full domain DM
3567: . dmX - An array of DM for the field, say an auxiliary DM, indexed by s
3568: . dsX - An array of PetscDS for the field, indexed by s
3569: . cellIS - The interface cells for which we want values
3570: . locX - An array of local vectors with the field values, indexed by s
3571: - useCell - Flag to have values come from neighboring cell rather than endcap face
3573: Output Parameter:
3574: . x - An array of field values, indexed by s
3576: Note:
3577: The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`.
3579: Level: advanced
3581: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
3582: */
3583: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3584: {
3585: DM plexX[2];
3586: DMEnclosureType encX[2];
3587: PetscSection sectionX[2];
3588: const PetscInt *cells;
3589: PetscInt cStart, cEnd, numCells, c, s, totDimX[2];
3591: PetscFunctionBegin;
3593: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3598: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3599: numCells = cEnd - cStart;
3600: for (s = 0; s < 2; ++s) {
3604: PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
3605: PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
3606: PetscCall(DMGetLocalSection(dmX[s], §ionX[s]));
3607: PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
3608: PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
3609: }
3610: for (c = cStart; c < cEnd; ++c) {
3611: const PetscInt cell = cells ? cells[c] : c;
3612: const PetscInt cind = c - cStart;
3613: const PetscInt *cone, *ornt;
3615: PetscCall(DMPlexGetCone(dm, cell, &cone));
3616: PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3617: //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]);
3618: for (s = 0; s < 2; ++s) {
3619: const PetscInt tdX = totDimX[s];
3620: PetscScalar *closure = NULL, *xl = &x[s][cind * tdX];
3621: PetscInt face = cone[s], point = face, subpoint, Nx, i;
3623: if (useCell) {
3624: const PetscInt *support;
3625: PetscInt ssize;
3627: PetscCall(DMPlexGetSupport(dm, face, &support));
3628: PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3629: 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);
3630: if (support[0] == cell) point = support[1];
3631: else if (support[1] == cell) point = support[0];
3632: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3633: }
3634: PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
3635: PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], locX[s], subpoint, ornt[s], &Nx, &closure));
3636: 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);
3637: for (i = 0; i < Nx; ++i) xl[i] = closure[i];
3638: PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
3639: }
3640: }
3641: for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
3642: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3643: PetscFunctionReturn(PETSC_SUCCESS);
3644: }
3646: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3647: {
3648: PetscFunctionBegin;
3649: if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3650: PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
3651: PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
3652: PetscFunctionReturn(PETSC_SUCCESS);
3653: }
3655: /*@C
3656: DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces
3658: Input Parameters:
3659: + dm - The `DM`
3660: . fStart - The first face to include
3661: . fEnd - The first face to exclude
3662: . locX - A local vector with the solution fields
3663: . locX_t - A local vector with solution field time derivatives, or NULL
3664: . faceGeometry - A local vector with face geometry
3665: . cellGeometry - A local vector with cell geometry
3666: - locaGrad - A local vector with field gradients, or NULL
3668: Output Parameters:
3669: + Nface - The number of faces with field values
3670: . uL - The field values at the left side of the face
3671: - uR - The field values at the right side of the face
3673: Level: developer
3675: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3676: @*/
3677: 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)
3678: {
3679: DM dmFace, dmCell, dmGrad = NULL;
3680: PetscSection section;
3681: PetscDS prob;
3682: DMLabel ghostLabel;
3683: const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3684: PetscBool *isFE;
3685: PetscInt dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;
3687: PetscFunctionBegin;
3696: PetscCall(DMGetDimension(dm, &dim));
3697: PetscCall(DMGetDS(dm, &prob));
3698: PetscCall(DMGetLocalSection(dm, §ion));
3699: PetscCall(PetscDSGetNumFields(prob, &Nf));
3700: PetscCall(PetscDSGetTotalComponents(prob, &Nc));
3701: PetscCall(PetscMalloc1(Nf, &isFE));
3702: for (f = 0; f < Nf; ++f) {
3703: PetscObject obj;
3704: PetscClassId id;
3706: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3707: PetscCall(PetscObjectGetClassId(obj, &id));
3708: if (id == PETSCFE_CLASSID) {
3709: isFE[f] = PETSC_TRUE;
3710: } else if (id == PETSCFV_CLASSID) {
3711: isFE[f] = PETSC_FALSE;
3712: } else {
3713: isFE[f] = PETSC_FALSE;
3714: }
3715: }
3716: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3717: PetscCall(VecGetArrayRead(locX, &x));
3718: PetscCall(VecGetDM(faceGeometry, &dmFace));
3719: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3720: PetscCall(VecGetDM(cellGeometry, &dmCell));
3721: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3722: if (locGrad) {
3723: PetscCall(VecGetDM(locGrad, &dmGrad));
3724: PetscCall(VecGetArrayRead(locGrad, &lgrad));
3725: }
3726: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
3727: PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
3728: /* Right now just eat the extra work for FE (could make a cell loop) */
3729: for (face = fStart, iface = 0; face < fEnd; ++face) {
3730: const PetscInt *cells;
3731: PetscFVFaceGeom *fg;
3732: PetscFVCellGeom *cgL, *cgR;
3733: PetscScalar *xL, *xR, *gL, *gR;
3734: PetscScalar *uLl = *uL, *uRl = *uR;
3735: PetscInt ghost, nsupp, nchild;
3737: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3738: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3739: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3740: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3741: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3742: PetscCall(DMPlexGetSupport(dm, face, &cells));
3743: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3744: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3745: for (f = 0; f < Nf; ++f) {
3746: PetscInt off;
3748: PetscCall(PetscDSGetComponentOffset(prob, f, &off));
3749: if (isFE[f]) {
3750: const PetscInt *cone;
3751: PetscInt comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;
3753: xL = xR = NULL;
3754: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3755: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3756: PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3757: PetscCall(DMPlexGetCone(dm, cells[0], &cone));
3758: PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
3759: for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
3760: if (cone[faceLocL] == face) break;
3761: PetscCall(DMPlexGetCone(dm, cells[1], &cone));
3762: PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
3763: for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
3764: if (cone[faceLocR] == face) break;
3765: 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]);
3766: /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3767: /* TODO: this is a hack that might not be right for nonconforming */
3768: if (faceLocL < coneSizeL) {
3769: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
3770: if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3771: else {
3772: for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
3773: }
3774: } else {
3775: PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3776: PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3777: for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
3778: }
3779: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3780: PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3781: } else {
3782: PetscFV fv;
3783: PetscInt numComp, c;
3785: PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
3786: PetscCall(PetscFVGetNumComponents(fv, &numComp));
3787: PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
3788: PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
3789: if (dmGrad) {
3790: PetscReal dxL[3], dxR[3];
3792: PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
3793: PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
3794: DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3795: DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3796: for (c = 0; c < numComp; ++c) {
3797: uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
3798: uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
3799: }
3800: } else {
3801: for (c = 0; c < numComp; ++c) {
3802: uLl[iface * Nc + off + c] = xL[c];
3803: uRl[iface * Nc + off + c] = xR[c];
3804: }
3805: }
3806: }
3807: }
3808: ++iface;
3809: }
3810: *Nface = iface;
3811: PetscCall(VecRestoreArrayRead(locX, &x));
3812: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3813: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3814: if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
3815: PetscCall(PetscFree(isFE));
3816: PetscFunctionReturn(PETSC_SUCCESS);
3817: }
3819: /*@C
3820: DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces
3822: Input Parameters:
3823: + dm - The `DM`
3824: . fStart - The first face to include
3825: . fEnd - The first face to exclude
3826: . locX - A local vector with the solution fields
3827: . locX_t - A local vector with solution field time derivatives, or NULL
3828: . faceGeometry - A local vector with face geometry
3829: . cellGeometry - A local vector with cell geometry
3830: - locaGrad - A local vector with field gradients, or NULL
3832: Output Parameters:
3833: + Nface - The number of faces with field values
3834: . uL - The field values at the left side of the face
3835: - uR - The field values at the right side of the face
3837: Level: developer
3839: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3840: @*/
3841: 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)
3842: {
3843: PetscFunctionBegin;
3844: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
3845: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
3846: PetscFunctionReturn(PETSC_SUCCESS);
3847: }
3849: /*@C
3850: DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces
3852: Input Parameters:
3853: + dm - The `DM`
3854: . fStart - The first face to include
3855: . fEnd - The first face to exclude
3856: . faceGeometry - A local vector with face geometry
3857: - cellGeometry - A local vector with cell geometry
3859: Output Parameters:
3860: + Nface - The number of faces with field values
3861: . fgeom - The extract the face centroid and normal
3862: - vol - The cell volume
3864: Level: developer
3866: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3867: @*/
3868: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3869: {
3870: DM dmFace, dmCell;
3871: DMLabel ghostLabel;
3872: const PetscScalar *facegeom, *cellgeom;
3873: PetscInt dim, numFaces = fEnd - fStart, iface, face;
3875: PetscFunctionBegin;
3881: PetscCall(DMGetDimension(dm, &dim));
3882: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3883: PetscCall(VecGetDM(faceGeometry, &dmFace));
3884: PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3885: PetscCall(VecGetDM(cellGeometry, &dmCell));
3886: PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3887: PetscCall(PetscMalloc1(numFaces, fgeom));
3888: PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
3889: for (face = fStart, iface = 0; face < fEnd; ++face) {
3890: const PetscInt *cells;
3891: PetscFVFaceGeom *fg;
3892: PetscFVCellGeom *cgL, *cgR;
3893: PetscFVFaceGeom *fgeoml = *fgeom;
3894: PetscReal *voll = *vol;
3895: PetscInt ghost, d, nchild, nsupp;
3897: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3898: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3899: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3900: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3901: PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3902: PetscCall(DMPlexGetSupport(dm, face, &cells));
3903: PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3904: PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3905: for (d = 0; d < dim; ++d) {
3906: fgeoml[iface].centroid[d] = fg->centroid[d];
3907: fgeoml[iface].normal[d] = fg->normal[d];
3908: }
3909: voll[iface * 2 + 0] = cgL->volume;
3910: voll[iface * 2 + 1] = cgR->volume;
3911: ++iface;
3912: }
3913: *Nface = iface;
3914: PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3915: PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3916: PetscFunctionReturn(PETSC_SUCCESS);
3917: }
3919: /*@C
3920: DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces
3922: Input Parameters:
3923: + dm - The `DM`
3924: . fStart - The first face to include
3925: . fEnd - The first face to exclude
3926: . faceGeometry - A local vector with face geometry
3927: - cellGeometry - A local vector with cell geometry
3929: Output Parameters:
3930: + Nface - The number of faces with field values
3931: . fgeom - The extract the face centroid and normal
3932: - vol - The cell volume
3934: Level: developer
3936: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3937: @*/
3938: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3939: {
3940: PetscFunctionBegin;
3941: PetscCall(PetscFree(*fgeom));
3942: PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
3943: PetscFunctionReturn(PETSC_SUCCESS);
3944: }
3946: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3947: {
3948: char composeStr[33] = {0};
3949: PetscObjectId id;
3950: PetscContainer container;
3952: PetscFunctionBegin;
3953: PetscCall(PetscObjectGetId((PetscObject)quad, &id));
3954: PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
3955: PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
3956: if (container) {
3957: PetscCall(PetscContainerGetPointer(container, (void **)geom));
3958: } else {
3959: PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
3960: PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
3961: PetscCall(PetscContainerSetPointer(container, (void *)*geom));
3962: PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
3963: PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
3964: PetscCall(PetscContainerDestroy(&container));
3965: }
3966: PetscFunctionReturn(PETSC_SUCCESS);
3967: }
3969: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
3970: {
3971: PetscFunctionBegin;
3972: *geom = NULL;
3973: PetscFunctionReturn(PETSC_SUCCESS);
3974: }
3976: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
3977: {
3978: DM_Plex *mesh = (DM_Plex *)dm->data;
3979: const char *name = "Residual";
3980: DM dmAux = NULL;
3981: DMLabel ghostLabel = NULL;
3982: PetscDS prob = NULL;
3983: PetscDS probAux = NULL;
3984: PetscBool useFEM = PETSC_FALSE;
3985: PetscBool isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
3986: DMField coordField = NULL;
3987: Vec locA;
3988: PetscScalar *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
3989: IS chunkIS;
3990: const PetscInt *cells;
3991: PetscInt cStart, cEnd, numCells;
3992: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
3993: PetscInt maxDegree = PETSC_MAX_INT;
3994: PetscFormKey key;
3995: PetscQuadrature affineQuad = NULL, *quads = NULL;
3996: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
3998: PetscFunctionBegin;
3999: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4000: /* FEM+FVM */
4001: /* 1: Get sizes from dm and dmAux */
4002: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4003: PetscCall(DMGetDS(dm, &prob));
4004: PetscCall(PetscDSGetNumFields(prob, &Nf));
4005: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4006: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4007: if (locA) {
4008: PetscCall(VecGetDM(locA, &dmAux));
4009: PetscCall(DMGetDS(dmAux, &probAux));
4010: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4011: }
4012: /* 2: Get geometric data */
4013: for (f = 0; f < Nf; ++f) {
4014: PetscObject obj;
4015: PetscClassId id;
4016: PetscBool fimp;
4018: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4019: if (isImplicit != fimp) continue;
4020: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4021: PetscCall(PetscObjectGetClassId(obj, &id));
4022: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4023: PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4024: }
4025: if (useFEM) {
4026: PetscCall(DMGetCoordinateField(dm, &coordField));
4027: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4028: if (maxDegree <= 1) {
4029: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4030: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4031: } else {
4032: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4033: for (f = 0; f < Nf; ++f) {
4034: PetscObject obj;
4035: PetscClassId id;
4036: PetscBool fimp;
4038: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4039: if (isImplicit != fimp) continue;
4040: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4041: PetscCall(PetscObjectGetClassId(obj, &id));
4042: if (id == PETSCFE_CLASSID) {
4043: PetscFE fe = (PetscFE)obj;
4045: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4046: PetscCall(PetscObjectReference((PetscObject)quads[f]));
4047: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4048: }
4049: }
4050: }
4051: }
4052: /* Loop over chunks */
4053: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4054: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4055: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4056: numCells = cEnd - cStart;
4057: numChunks = 1;
4058: cellChunkSize = numCells / numChunks;
4059: numChunks = PetscMin(1, numCells);
4060: key.label = NULL;
4061: key.value = 0;
4062: key.part = 0;
4063: for (chunk = 0; chunk < numChunks; ++chunk) {
4064: PetscScalar *elemVec, *fluxL = NULL, *fluxR = NULL;
4065: PetscReal *vol = NULL;
4066: PetscFVFaceGeom *fgeom = NULL;
4067: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4068: PetscInt numFaces = 0;
4070: /* Extract field coefficients */
4071: if (useFEM) {
4072: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4073: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4074: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4075: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4076: }
4077: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4078: /* Loop over fields */
4079: for (f = 0; f < Nf; ++f) {
4080: PetscObject obj;
4081: PetscClassId id;
4082: PetscBool fimp;
4083: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4085: key.field = f;
4086: PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4087: if (isImplicit != fimp) continue;
4088: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4089: PetscCall(PetscObjectGetClassId(obj, &id));
4090: if (id == PETSCFE_CLASSID) {
4091: PetscFE fe = (PetscFE)obj;
4092: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
4093: PetscFEGeom *chunkGeom = NULL;
4094: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4095: PetscInt Nq, Nb;
4097: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4098: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4099: PetscCall(PetscFEGetDimension(fe, &Nb));
4100: blockSize = Nb;
4101: batchSize = numBlocks * blockSize;
4102: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4103: numChunks = numCells / (numBatches * batchSize);
4104: Ne = numChunks * numBatches * batchSize;
4105: Nr = numCells % (numBatches * batchSize);
4106: offset = numCells - Nr;
4107: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4108: /* 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) */
4109: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4110: PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4111: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4112: PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4113: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4114: } else if (id == PETSCFV_CLASSID) {
4115: PetscFV fv = (PetscFV)obj;
4117: Ne = numFaces;
4118: /* Riemann solve over faces (need fields at face centroids) */
4119: /* We need to evaluate FE fields at those coordinates */
4120: PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4121: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4122: }
4123: /* Loop over domain */
4124: if (useFEM) {
4125: /* Add elemVec to locX */
4126: for (c = cS; c < cE; ++c) {
4127: const PetscInt cell = cells ? cells[c] : c;
4128: const PetscInt cind = c - cStart;
4130: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4131: if (ghostLabel) {
4132: PetscInt ghostVal;
4134: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4135: if (ghostVal > 0) continue;
4136: }
4137: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4138: }
4139: }
4140: /* Handle time derivative */
4141: if (locX_t) {
4142: PetscScalar *x_t, *fa;
4144: PetscCall(VecGetArray(locF, &fa));
4145: PetscCall(VecGetArray(locX_t, &x_t));
4146: for (f = 0; f < Nf; ++f) {
4147: PetscFV fv;
4148: PetscObject obj;
4149: PetscClassId id;
4150: PetscInt pdim, d;
4152: PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4153: PetscCall(PetscObjectGetClassId(obj, &id));
4154: if (id != PETSCFV_CLASSID) continue;
4155: fv = (PetscFV)obj;
4156: PetscCall(PetscFVGetNumComponents(fv, &pdim));
4157: for (c = cS; c < cE; ++c) {
4158: const PetscInt cell = cells ? cells[c] : c;
4159: PetscScalar *u_t, *r;
4161: if (ghostLabel) {
4162: PetscInt ghostVal;
4164: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4165: if (ghostVal > 0) continue;
4166: }
4167: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4168: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4169: for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4170: }
4171: }
4172: PetscCall(VecRestoreArray(locX_t, &x_t));
4173: PetscCall(VecRestoreArray(locF, &fa));
4174: }
4175: if (useFEM) {
4176: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4177: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4178: }
4179: }
4180: if (useFEM) PetscCall(ISDestroy(&chunkIS));
4181: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4182: /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
4183: if (useFEM) {
4184: if (maxDegree <= 1) {
4185: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4186: PetscCall(PetscQuadratureDestroy(&affineQuad));
4187: } else {
4188: for (f = 0; f < Nf; ++f) {
4189: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4190: PetscCall(PetscQuadratureDestroy(&quads[f]));
4191: }
4192: PetscCall(PetscFree2(quads, geoms));
4193: }
4194: }
4195: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4196: PetscFunctionReturn(PETSC_SUCCESS);
4197: }
4199: /*
4200: 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
4202: X - The local solution vector
4203: X_t - The local solution time derivative vector, or NULL
4204: */
4205: 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)
4206: {
4207: DM_Plex *mesh = (DM_Plex *)dm->data;
4208: const char *name = "Jacobian", *nameP = "JacobianPre";
4209: DM dmAux = NULL;
4210: PetscDS prob, probAux = NULL;
4211: PetscSection sectionAux = NULL;
4212: Vec A;
4213: DMField coordField;
4214: PetscFEGeom *cgeomFEM;
4215: PetscQuadrature qGeom = NULL;
4216: Mat J = Jac, JP = JacP;
4217: PetscScalar *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4218: PetscBool hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4219: const PetscInt *cells;
4220: PetscFormKey key;
4221: PetscInt Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;
4223: PetscFunctionBegin;
4224: PetscCall(ISGetLocalSize(cellIS, &numCells));
4225: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4226: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4227: PetscCall(DMGetDS(dm, &prob));
4228: PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4229: if (A) {
4230: PetscCall(VecGetDM(A, &dmAux));
4231: PetscCall(DMGetLocalSection(dmAux, §ionAux));
4232: PetscCall(DMGetDS(dmAux, &probAux));
4233: }
4234: /* Get flags */
4235: PetscCall(PetscDSGetNumFields(prob, &Nf));
4236: PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4237: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4238: PetscObject disc;
4239: PetscClassId id;
4240: PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4241: PetscCall(PetscObjectGetClassId(disc, &id));
4242: if (id == PETSCFE_CLASSID) {
4243: isFE[fieldI] = PETSC_TRUE;
4244: } else if (id == PETSCFV_CLASSID) {
4245: hasFV = PETSC_TRUE;
4246: isFE[fieldI] = PETSC_FALSE;
4247: }
4248: }
4249: PetscCall(PetscDSHasJacobian(prob, &hasJac));
4250: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4251: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4252: assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4253: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4254: if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4255: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4256: if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4257: /* Compute batch sizes */
4258: if (isFE[0]) {
4259: PetscFE fe;
4260: PetscQuadrature q;
4261: PetscInt numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;
4263: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4264: PetscCall(PetscFEGetQuadrature(fe, &q));
4265: PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4266: PetscCall(PetscFEGetDimension(fe, &Nb));
4267: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4268: blockSize = Nb * numQuadPoints;
4269: batchSize = numBlocks * blockSize;
4270: chunkSize = numBatches * batchSize;
4271: numChunks = numCells / chunkSize + numCells % chunkSize;
4272: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4273: } else {
4274: chunkSize = numCells;
4275: numChunks = 1;
4276: }
4277: /* Get work space */
4278: wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4279: PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4280: PetscCall(PetscArrayzero(work, wsz));
4281: off = 0;
4282: u = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4283: u_t = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4284: a = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4285: elemMat = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4286: elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4287: elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4288: PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4289: /* Setup geometry */
4290: PetscCall(DMGetCoordinateField(dm, &coordField));
4291: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4292: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4293: if (!qGeom) {
4294: PetscFE fe;
4296: PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4297: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4298: PetscCall(PetscObjectReference((PetscObject)qGeom));
4299: }
4300: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4301: /* Compute volume integrals */
4302: if (assembleJac) PetscCall(MatZeroEntries(J));
4303: PetscCall(MatZeroEntries(JP));
4304: key.label = NULL;
4305: key.value = 0;
4306: key.part = 0;
4307: for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4308: const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4309: PetscInt c;
4311: /* Extract values */
4312: for (c = 0; c < Ncell; ++c) {
4313: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4314: PetscScalar *x = NULL, *x_t = NULL;
4315: PetscInt i;
4317: if (X) {
4318: PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4319: for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4320: PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4321: }
4322: if (X_t) {
4323: PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4324: for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4325: PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4326: }
4327: if (dmAux) {
4328: PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4329: for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4330: PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4331: }
4332: }
4333: for (fieldI = 0; fieldI < Nf; ++fieldI) {
4334: PetscFE fe;
4335: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4336: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4337: key.field = fieldI * Nf + fieldJ;
4338: if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4339: if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4340: if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4341: }
4342: /* For finite volume, add the identity */
4343: if (!isFE[fieldI]) {
4344: PetscFV fv;
4345: PetscInt eOffset = 0, Nc, fc, foff;
4347: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4348: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4349: PetscCall(PetscFVGetNumComponents(fv, &Nc));
4350: for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4351: for (fc = 0; fc < Nc; ++fc) {
4352: const PetscInt i = foff + fc;
4353: if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4354: if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4355: }
4356: }
4357: }
4358: }
4359: /* Add contribution from X_t */
4360: if (hasDyn) {
4361: for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4362: }
4363: /* Insert values into matrix */
4364: for (c = 0; c < Ncell; ++c) {
4365: const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4366: if (mesh->printFEM > 1) {
4367: if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4368: if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4369: }
4370: if (assembleJac) PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4371: PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4372: }
4373: }
4374: /* Cleanup */
4375: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4376: PetscCall(PetscQuadratureDestroy(&qGeom));
4377: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4378: PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4379: 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));
4380: /* Compute boundary integrals */
4381: /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4382: /* Assemble matrix */
4383: if (assembleJac) {
4384: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4385: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4386: }
4387: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4388: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4389: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4390: PetscFunctionReturn(PETSC_SUCCESS);
4391: }
4393: /******** FEM Assembly Function ********/
4395: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4396: {
4397: PetscBool isPlex;
4399: PetscFunctionBegin;
4400: PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4401: if (isPlex) {
4402: *plex = dm;
4403: PetscCall(PetscObjectReference((PetscObject)dm));
4404: } else {
4405: PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4406: if (!*plex) {
4407: PetscCall(DMConvert(dm, DMPLEX, plex));
4408: PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4409: if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4410: } else {
4411: PetscCall(PetscObjectReference((PetscObject)*plex));
4412: }
4413: }
4414: PetscFunctionReturn(PETSC_SUCCESS);
4415: }
4417: /*@
4418: DMPlexGetGeometryFVM - Return precomputed geometric data
4420: Collective
4422: Input Parameter:
4423: . dm - The `DM`
4425: Output Parameters:
4426: + facegeom - The values precomputed from face geometry
4427: . cellgeom - The values precomputed from cell geometry
4428: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell
4430: Level: developer
4432: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4433: @*/
4434: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4435: {
4436: DM plex;
4438: PetscFunctionBegin;
4440: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4441: PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4442: if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4443: PetscCall(DMDestroy(&plex));
4444: PetscFunctionReturn(PETSC_SUCCESS);
4445: }
4447: /*@
4448: DMPlexGetGradientDM - Return gradient data layout
4450: Collective
4452: Input Parameters:
4453: + dm - The `DM`
4454: - fv - The `PetscFV`
4456: Output Parameter:
4457: . dmGrad - The layout for gradient values
4459: Level: developer
4461: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4462: @*/
4463: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4464: {
4465: DM plex;
4466: PetscBool computeGradients;
4468: PetscFunctionBegin;
4472: PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4473: if (!computeGradients) {
4474: *dmGrad = NULL;
4475: PetscFunctionReturn(PETSC_SUCCESS);
4476: }
4477: PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4478: PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4479: PetscCall(DMDestroy(&plex));
4480: PetscFunctionReturn(PETSC_SUCCESS);
4481: }
4483: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4484: {
4485: DM_Plex *mesh = (DM_Plex *)dm->data;
4486: DM plex = NULL, plexA = NULL;
4487: DMEnclosureType encAux;
4488: PetscDS prob, probAux = NULL;
4489: PetscSection section, sectionAux = NULL;
4490: Vec locA = NULL;
4491: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4492: PetscInt totDim, totDimAux = 0;
4494: PetscFunctionBegin;
4495: PetscCall(DMConvert(dm, DMPLEX, &plex));
4496: PetscCall(DMGetLocalSection(dm, §ion));
4497: PetscCall(DMGetDS(dm, &prob));
4498: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4499: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4500: if (locA) {
4501: DM dmAux;
4503: PetscCall(VecGetDM(locA, &dmAux));
4504: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4505: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4506: PetscCall(DMGetDS(plexA, &probAux));
4507: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4508: PetscCall(DMGetLocalSection(plexA, §ionAux));
4509: }
4510: {
4511: PetscFEGeom *fgeom;
4512: PetscInt maxDegree;
4513: PetscQuadrature qGeom = NULL;
4514: IS pointIS;
4515: const PetscInt *points;
4516: PetscInt numFaces, face, Nq;
4518: PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4519: if (!pointIS) goto end; /* No points with that id on this process */
4520: {
4521: IS isectIS;
4523: /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4524: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4525: PetscCall(ISDestroy(&pointIS));
4526: pointIS = isectIS;
4527: }
4528: PetscCall(ISGetLocalSize(pointIS, &numFaces));
4529: PetscCall(ISGetIndices(pointIS, &points));
4530: PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a));
4531: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4532: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4533: if (!qGeom) {
4534: PetscFE fe;
4536: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4537: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4538: PetscCall(PetscObjectReference((PetscObject)qGeom));
4539: }
4540: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4541: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4542: for (face = 0; face < numFaces; ++face) {
4543: const PetscInt point = points[face], *support;
4544: PetscScalar *x = NULL;
4545: PetscInt i;
4547: PetscCall(DMPlexGetSupport(dm, point, &support));
4548: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4549: for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4550: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4551: if (locX_t) {
4552: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4553: for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4554: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4555: }
4556: if (locA) {
4557: PetscInt subp;
4559: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4560: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4561: for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4562: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4563: }
4564: }
4565: PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4566: {
4567: PetscFE fe;
4568: PetscInt Nb;
4569: PetscFEGeom *chunkGeom = NULL;
4570: /* Conforming batches */
4571: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4572: /* Remainder */
4573: PetscInt Nr, offset;
4575: PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4576: PetscCall(PetscFEGetDimension(fe, &Nb));
4577: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4578: /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4579: blockSize = Nb;
4580: batchSize = numBlocks * blockSize;
4581: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4582: numChunks = numFaces / (numBatches * batchSize);
4583: Ne = numChunks * numBatches * batchSize;
4584: Nr = numFaces % (numBatches * batchSize);
4585: offset = numFaces - Nr;
4586: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4587: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4588: PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4589: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4590: PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim]));
4591: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4592: }
4593: for (face = 0; face < numFaces; ++face) {
4594: const PetscInt point = points[face], *support;
4596: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]));
4597: PetscCall(DMPlexGetSupport(plex, point, &support));
4598: PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
4599: }
4600: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4601: PetscCall(PetscQuadratureDestroy(&qGeom));
4602: PetscCall(ISRestoreIndices(pointIS, &points));
4603: PetscCall(ISDestroy(&pointIS));
4604: PetscCall(PetscFree4(u, u_t, elemVec, a));
4605: }
4606: end:
4607: PetscCall(DMDestroy(&plex));
4608: PetscCall(DMDestroy(&plexA));
4609: PetscFunctionReturn(PETSC_SUCCESS);
4610: }
4612: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4613: {
4614: DMField coordField;
4615: DMLabel depthLabel;
4616: IS facetIS;
4617: PetscInt dim;
4619: PetscFunctionBegin;
4620: PetscCall(DMGetDimension(dm, &dim));
4621: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4622: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4623: PetscCall(DMGetCoordinateField(dm, &coordField));
4624: PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4625: PetscCall(ISDestroy(&facetIS));
4626: PetscFunctionReturn(PETSC_SUCCESS);
4627: }
4629: PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4630: {
4631: PetscDS prob;
4632: PetscInt numBd, bd;
4633: DMField coordField = NULL;
4634: IS facetIS = NULL;
4635: DMLabel depthLabel;
4636: PetscInt dim;
4638: PetscFunctionBegin;
4639: PetscCall(DMGetDS(dm, &prob));
4640: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4641: PetscCall(DMGetDimension(dm, &dim));
4642: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4643: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4644: for (bd = 0; bd < numBd; ++bd) {
4645: PetscWeakForm wf;
4646: DMBoundaryConditionType type;
4647: DMLabel label;
4648: const PetscInt *values;
4649: PetscInt field, numValues, v;
4650: PetscObject obj;
4651: PetscClassId id;
4652: PetscFormKey key;
4654: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
4655: if (type & DM_BC_ESSENTIAL) continue;
4656: PetscCall(PetscDSGetDiscretization(prob, field, &obj));
4657: PetscCall(PetscObjectGetClassId(obj, &id));
4658: if (id != PETSCFE_CLASSID) continue;
4659: if (!facetIS) {
4660: DMLabel depthLabel;
4661: PetscInt dim;
4663: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4664: PetscCall(DMGetDimension(dm, &dim));
4665: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4666: }
4667: PetscCall(DMGetCoordinateField(dm, &coordField));
4668: for (v = 0; v < numValues; ++v) {
4669: key.label = label;
4670: key.value = values[v];
4671: key.field = field;
4672: key.part = 0;
4673: PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4674: }
4675: }
4676: PetscCall(ISDestroy(&facetIS));
4677: PetscFunctionReturn(PETSC_SUCCESS);
4678: }
4680: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4681: {
4682: DM_Plex *mesh = (DM_Plex *)dm->data;
4683: const char *name = "Residual";
4684: DM dmAux = NULL;
4685: DM dmGrad = NULL;
4686: DMLabel ghostLabel = NULL;
4687: PetscDS ds = NULL;
4688: PetscDS dsAux = NULL;
4689: PetscSection section = NULL;
4690: PetscBool useFEM = PETSC_FALSE;
4691: PetscBool useFVM = PETSC_FALSE;
4692: PetscBool isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4693: PetscFV fvm = NULL;
4694: PetscFVCellGeom *cgeomFVM = NULL;
4695: PetscFVFaceGeom *fgeomFVM = NULL;
4696: DMField coordField = NULL;
4697: Vec locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
4698: PetscScalar *u = NULL, *u_t, *a, *uL, *uR;
4699: IS chunkIS;
4700: const PetscInt *cells;
4701: PetscInt cStart, cEnd, numCells;
4702: PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4703: PetscInt maxDegree = PETSC_MAX_INT;
4704: PetscQuadrature affineQuad = NULL, *quads = NULL;
4705: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
4707: PetscFunctionBegin;
4708: if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
4709: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4710: if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
4711: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4712: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4713: /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4714: /* FEM+FVM */
4715: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4716: /* 1: Get sizes from dm and dmAux */
4717: PetscCall(DMGetLocalSection(dm, §ion));
4718: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4719: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
4720: PetscCall(PetscDSGetNumFields(ds, &Nf));
4721: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4722: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4723: if (locA) {
4724: PetscInt subcell;
4725: PetscCall(VecGetDM(locA, &dmAux));
4726: PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4727: PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
4728: PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4729: }
4730: /* 2: Get geometric data */
4731: for (f = 0; f < Nf; ++f) {
4732: PetscObject obj;
4733: PetscClassId id;
4734: PetscBool fimp;
4736: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4737: if (isImplicit != fimp) continue;
4738: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4739: PetscCall(PetscObjectGetClassId(obj, &id));
4740: if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4741: if (id == PETSCFV_CLASSID) {
4742: useFVM = PETSC_TRUE;
4743: fvm = (PetscFV)obj;
4744: }
4745: }
4746: if (useFEM) {
4747: PetscCall(DMGetCoordinateField(dm, &coordField));
4748: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4749: if (maxDegree <= 1) {
4750: PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4751: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4752: } else {
4753: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4754: for (f = 0; f < Nf; ++f) {
4755: PetscObject obj;
4756: PetscClassId id;
4757: PetscBool fimp;
4759: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4760: if (isImplicit != fimp) continue;
4761: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4762: PetscCall(PetscObjectGetClassId(obj, &id));
4763: if (id == PETSCFE_CLASSID) {
4764: PetscFE fe = (PetscFE)obj;
4766: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4767: PetscCall(PetscObjectReference((PetscObject)quads[f]));
4768: PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4769: }
4770: }
4771: }
4772: }
4773: if (useFVM) {
4774: PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4775: PetscCall(VecGetArrayRead(faceGeometryFVM, (const PetscScalar **)&fgeomFVM));
4776: PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
4777: /* Reconstruct and limit cell gradients */
4778: PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4779: if (dmGrad) {
4780: PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4781: PetscCall(DMGetGlobalVector(dmGrad, &grad));
4782: PetscCall(DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
4783: /* Communicate gradient values */
4784: PetscCall(DMGetLocalVector(dmGrad, &locGrad));
4785: PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
4786: PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
4787: PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
4788: }
4789: /* Handle non-essential (e.g. outflow) boundary values */
4790: PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
4791: }
4792: /* Loop over chunks */
4793: if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4794: numCells = cEnd - cStart;
4795: numChunks = 1;
4796: cellChunkSize = numCells / numChunks;
4797: faceChunkSize = (fEnd - fStart) / numChunks;
4798: numChunks = PetscMin(1, numCells);
4799: for (chunk = 0; chunk < numChunks; ++chunk) {
4800: PetscScalar *elemVec, *fluxL, *fluxR;
4801: PetscReal *vol;
4802: PetscFVFaceGeom *fgeom;
4803: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4804: PetscInt fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;
4806: /* Extract field coefficients */
4807: if (useFEM) {
4808: PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4809: PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4810: PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4811: PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4812: }
4813: if (useFVM) {
4814: PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4815: PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4816: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4817: PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4818: PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
4819: PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
4820: }
4821: /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4822: /* Loop over fields */
4823: for (f = 0; f < Nf; ++f) {
4824: PetscObject obj;
4825: PetscClassId id;
4826: PetscBool fimp;
4827: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;
4829: key.field = f;
4830: PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4831: if (isImplicit != fimp) continue;
4832: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4833: PetscCall(PetscObjectGetClassId(obj, &id));
4834: if (id == PETSCFE_CLASSID) {
4835: PetscFE fe = (PetscFE)obj;
4836: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
4837: PetscFEGeom *chunkGeom = NULL;
4838: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
4839: PetscInt Nq, Nb;
4841: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4842: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4843: PetscCall(PetscFEGetDimension(fe, &Nb));
4844: blockSize = Nb;
4845: batchSize = numBlocks * blockSize;
4846: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4847: numChunks = numCells / (numBatches * batchSize);
4848: Ne = numChunks * numBatches * batchSize;
4849: Nr = numCells % (numBatches * batchSize);
4850: offset = numCells - Nr;
4851: /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4852: /* 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) */
4853: PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4854: PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
4855: PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4856: PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4857: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4858: } else if (id == PETSCFV_CLASSID) {
4859: PetscFV fv = (PetscFV)obj;
4861: Ne = numFaces;
4862: /* Riemann solve over faces (need fields at face centroids) */
4863: /* We need to evaluate FE fields at those coordinates */
4864: PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4865: } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4866: }
4867: /* Loop over domain */
4868: if (useFEM) {
4869: /* Add elemVec to locX */
4870: for (c = cS; c < cE; ++c) {
4871: const PetscInt cell = cells ? cells[c] : c;
4872: const PetscInt cind = c - cStart;
4874: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
4875: if (ghostLabel) {
4876: PetscInt ghostVal;
4878: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4879: if (ghostVal > 0) continue;
4880: }
4881: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4882: }
4883: }
4884: if (useFVM) {
4885: PetscScalar *fa;
4886: PetscInt iface;
4888: PetscCall(VecGetArray(locF, &fa));
4889: for (f = 0; f < Nf; ++f) {
4890: PetscFV fv;
4891: PetscObject obj;
4892: PetscClassId id;
4893: PetscInt foff, pdim;
4895: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4896: PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
4897: PetscCall(PetscObjectGetClassId(obj, &id));
4898: if (id != PETSCFV_CLASSID) continue;
4899: fv = (PetscFV)obj;
4900: PetscCall(PetscFVGetNumComponents(fv, &pdim));
4901: /* Accumulate fluxes to cells */
4902: for (face = fS, iface = 0; face < fE; ++face) {
4903: const PetscInt *scells;
4904: PetscScalar *fL = NULL, *fR = NULL;
4905: PetscInt ghost, d, nsupp, nchild;
4907: PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4908: PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4909: PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4910: if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4911: PetscCall(DMPlexGetSupport(dm, face, &scells));
4912: PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
4913: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
4914: PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
4915: if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
4916: for (d = 0; d < pdim; ++d) {
4917: if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
4918: if (fR) fR[d] += fluxR[iface * totDim + foff + d];
4919: }
4920: ++iface;
4921: }
4922: }
4923: PetscCall(VecRestoreArray(locF, &fa));
4924: }
4925: /* Handle time derivative */
4926: if (locX_t) {
4927: PetscScalar *x_t, *fa;
4929: PetscCall(VecGetArray(locF, &fa));
4930: PetscCall(VecGetArray(locX_t, &x_t));
4931: for (f = 0; f < Nf; ++f) {
4932: PetscFV fv;
4933: PetscObject obj;
4934: PetscClassId id;
4935: PetscInt pdim, d;
4937: PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4938: PetscCall(PetscObjectGetClassId(obj, &id));
4939: if (id != PETSCFV_CLASSID) continue;
4940: fv = (PetscFV)obj;
4941: PetscCall(PetscFVGetNumComponents(fv, &pdim));
4942: for (c = cS; c < cE; ++c) {
4943: const PetscInt cell = cells ? cells[c] : c;
4944: PetscScalar *u_t, *r;
4946: if (ghostLabel) {
4947: PetscInt ghostVal;
4949: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4950: if (ghostVal > 0) continue;
4951: }
4952: PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4953: PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4954: for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4955: }
4956: }
4957: PetscCall(VecRestoreArray(locX_t, &x_t));
4958: PetscCall(VecRestoreArray(locF, &fa));
4959: }
4960: if (useFEM) {
4961: PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4962: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4963: }
4964: if (useFVM) {
4965: PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4966: PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4967: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4968: PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4969: if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
4970: }
4971: }
4972: if (useFEM) PetscCall(ISDestroy(&chunkIS));
4973: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4975: if (useFEM) {
4976: PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user));
4978: if (maxDegree <= 1) {
4979: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4980: PetscCall(PetscQuadratureDestroy(&affineQuad));
4981: } else {
4982: for (f = 0; f < Nf; ++f) {
4983: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4984: PetscCall(PetscQuadratureDestroy(&quads[f]));
4985: }
4986: PetscCall(PetscFree2(quads, geoms));
4987: }
4988: }
4990: /* FEM */
4991: /* 1: Get sizes from dm and dmAux */
4992: /* 2: Get geometric data */
4993: /* 3: Handle boundary values */
4994: /* 4: Loop over domain */
4995: /* Extract coefficients */
4996: /* Loop over fields */
4997: /* Set tiling for FE*/
4998: /* Integrate FE residual to get elemVec */
4999: /* Loop over subdomain */
5000: /* Loop over quad points */
5001: /* Transform coords to real space */
5002: /* Evaluate field and aux fields at point */
5003: /* Evaluate residual at point */
5004: /* Transform residual to real space */
5005: /* Add residual to elemVec */
5006: /* Loop over domain */
5007: /* Add elemVec to locX */
5009: /* FVM */
5010: /* Get geometric data */
5011: /* If using gradients */
5012: /* Compute gradient data */
5013: /* Loop over domain faces */
5014: /* Count computational faces */
5015: /* Reconstruct cell gradient */
5016: /* Loop over domain cells */
5017: /* Limit cell gradients */
5018: /* Handle boundary values */
5019: /* Loop over domain faces */
5020: /* Read out field, centroid, normal, volume for each side of face */
5021: /* Riemann solve over faces */
5022: /* Loop over domain faces */
5023: /* Accumulate fluxes to cells */
5024: /* TODO Change printFEM to printDisc here */
5025: if (mesh->printFEM) {
5026: Vec locFbc;
5027: PetscInt pStart, pEnd, p, maxDof;
5028: PetscScalar *zeroes;
5030: PetscCall(VecDuplicate(locF, &locFbc));
5031: PetscCall(VecCopy(locF, locFbc));
5032: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5033: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5034: PetscCall(PetscCalloc1(maxDof, &zeroes));
5035: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5036: PetscCall(PetscFree(zeroes));
5037: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5038: PetscCall(VecDestroy(&locFbc));
5039: }
5040: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5041: PetscFunctionReturn(PETSC_SUCCESS);
5042: }
5044: /*
5045: 1) Allow multiple kernels for BdResidual for hybrid DS
5047: DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux
5049: DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
5050: - I think I just need to replace a[] with the closure from each face
5052: 4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
5053: */
5054: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5055: {
5056: DM_Plex *mesh = (DM_Plex *)dm->data;
5057: const char *name = "Hybrid Residual";
5058: DM dmAux[3] = {NULL, NULL, NULL};
5059: DMLabel ghostLabel = NULL;
5060: PetscDS ds = NULL;
5061: PetscDS dsIn = NULL;
5062: PetscDS dsAux[3] = {NULL, NULL, NULL};
5063: Vec locA[3] = {NULL, NULL, NULL};
5064: DM dmScale[3] = {NULL, NULL, NULL};
5065: PetscDS dsScale[3] = {NULL, NULL, NULL};
5066: Vec locS[3] = {NULL, NULL, NULL};
5067: PetscSection section = NULL;
5068: DMField coordField = NULL;
5069: PetscScalar *a[3] = {NULL, NULL, NULL};
5070: PetscScalar *s[3] = {NULL, NULL, NULL};
5071: PetscScalar *u = NULL, *u_t;
5072: PetscScalar *elemVecNeg, *elemVecPos, *elemVecCoh;
5073: IS chunkIS;
5074: const PetscInt *cells;
5075: PetscInt *faces;
5076: PetscInt cStart, cEnd, numCells;
5077: PetscInt Nf, f, totDim, totDimIn, totDimAux[3], totDimScale, numChunks, cellChunkSize, chunk;
5078: PetscInt maxDegree = PETSC_MAX_INT;
5079: PetscQuadrature affineQuad = NULL, *quads = NULL;
5080: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
5082: PetscFunctionBegin;
5083: if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5084: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5085: PetscCall(ISGetLocalSize(cellIS, &numCells));
5086: if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5087: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5088: const char *name;
5089: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5090: 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);
5091: }
5092: PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5093: /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5094: /* FEM */
5095: /* 1: Get sizes from dm and dmAux */
5096: PetscCall(DMGetSection(dm, §ion));
5097: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5098: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5099: PetscCall(PetscDSGetNumFields(ds, &Nf));
5100: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5101: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5102: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5103: if (locA[2]) {
5104: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5106: PetscCall(VecGetDM(locA[2], &dmAux[2]));
5107: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5108: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5109: {
5110: const PetscInt *cone;
5111: PetscInt c;
5113: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5114: for (c = 0; c < 2; ++c) {
5115: const PetscInt *support;
5116: PetscInt ssize, s;
5118: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5119: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5120: 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);
5121: if (support[0] == cellStart) s = 1;
5122: else if (support[1] == cellStart) s = 0;
5123: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5124: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5125: 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);
5126: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5127: else dmAux[c] = dmAux[2];
5128: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5129: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5130: }
5131: }
5132: }
5133: /* Handle mass matrix scaling
5134: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5135: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5136: if (locS[2]) {
5137: PetscInt Nb, Nbs;
5139: PetscCall(VecGetDM(locS[2], &dmScale[2]));
5140: PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
5141: locS[1] = locS[0] = locS[2];
5142: dmScale[1] = dmScale[0] = dmScale[2];
5143: dsScale[1] = dsScale[0] = dsScale[2];
5144: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale));
5145: // BRAD: This is not set correctly
5146: key[2].field = 2;
5147: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5148: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5149: 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);
5150: }
5151: /* 2: Setup geometric data */
5152: PetscCall(DMGetCoordinateField(dm, &coordField));
5153: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5154: if (maxDegree > 1) {
5155: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5156: for (f = 0; f < Nf; ++f) {
5157: PetscFE fe;
5159: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5160: if (fe) {
5161: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5162: PetscCall(PetscObjectReference((PetscObject)quads[f]));
5163: }
5164: }
5165: }
5166: /* Loop over chunks */
5167: cellChunkSize = numCells;
5168: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5169: PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5170: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5171: /* Extract field coefficients */
5172: /* NOTE This needs the end cap faces to have identical orientations */
5173: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5174: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5175: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_FALSE, s));
5176: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5177: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5178: PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5179: for (chunk = 0; chunk < numChunks; ++chunk) {
5180: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5182: PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5183: PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5184: PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5185: /* Get faces */
5186: for (c = cS; c < cE; ++c) {
5187: const PetscInt cell = cells ? cells[c] : c;
5188: const PetscInt *cone;
5189: PetscCall(DMPlexGetCone(dm, cell, &cone));
5190: faces[(c - cS) * 2 + 0] = cone[0];
5191: faces[(c - cS) * 2 + 1] = cone[1];
5192: }
5193: PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5194: /* Get geometric data */
5195: if (maxDegree <= 1) {
5196: if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5197: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5198: } else {
5199: for (f = 0; f < Nf; ++f) {
5200: if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5201: }
5202: }
5203: /* Loop over fields */
5204: for (f = 0; f < Nf; ++f) {
5205: PetscFE fe;
5206: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f];
5207: PetscFEGeom *chunkGeom = NULL, *remGeom = NULL;
5208: PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5209: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5210: PetscBool isCohesiveField;
5212: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5213: if (!fe) continue;
5214: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5215: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5216: PetscCall(PetscFEGetDimension(fe, &Nb));
5217: blockSize = Nb;
5218: batchSize = numBlocks * blockSize;
5219: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5220: numChunks = numCells / (numBatches * batchSize);
5221: Ne = numChunks * numBatches * batchSize;
5222: Nr = numCells % (numBatches * batchSize);
5223: offset = numCells - Nr;
5224: PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5225: PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5226: PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5227: chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5228: key[0].field = f;
5229: key[1].field = f;
5230: key[2].field = f;
5231: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5232: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, &elemVecNeg[offset * totDim]));
5233: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
5234: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, &elemVecPos[offset * totDim]));
5235: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5236: PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, &elemVecCoh[offset * totDim]));
5237: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5238: PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5239: }
5240: /* Add elemVec to locX */
5241: for (c = cS; c < cE; ++c) {
5242: const PetscInt cell = cells ? cells[c] : c;
5243: const PetscInt cind = c - cStart;
5244: PetscInt i;
5246: /* Scale element values */
5247: if (locS[0]) {
5248: PetscInt Nb, off = cind * totDim, soff = cind * totDimScale;
5249: PetscBool cohesive;
5251: for (f = 0; f < Nf; ++f) {
5252: PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5253: PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5254: if (f == key[2].field) {
5255: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5256: // No cohesive scaling field is currently input
5257: for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5258: off += Nb;
5259: } else {
5260: const PetscInt N = cohesive ? Nb : Nb * 2;
5262: for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5263: off += N;
5264: }
5265: }
5266: } else {
5267: for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5268: }
5269: if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5270: if (ghostLabel) {
5271: PetscInt ghostVal;
5273: PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5274: if (ghostVal > 0) continue;
5275: }
5276: PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5277: }
5278: }
5279: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5280: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5281: PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_FALSE, s));
5282: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5283: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5284: PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5285: PetscCall(PetscFree(faces));
5286: PetscCall(ISDestroy(&chunkIS));
5287: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5288: if (maxDegree <= 1) {
5289: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5290: PetscCall(PetscQuadratureDestroy(&affineQuad));
5291: } else {
5292: for (f = 0; f < Nf; ++f) {
5293: if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5294: if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5295: }
5296: PetscCall(PetscFree2(quads, geoms));
5297: }
5298: if (mesh->printFEM) {
5299: Vec locFbc;
5300: PetscInt pStart, pEnd, p, maxDof;
5301: PetscScalar *zeroes;
5303: PetscCall(VecDuplicate(locF, &locFbc));
5304: PetscCall(VecCopy(locF, locFbc));
5305: PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5306: PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5307: PetscCall(PetscCalloc1(maxDof, &zeroes));
5308: for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5309: PetscCall(PetscFree(zeroes));
5310: PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5311: PetscCall(VecDestroy(&locFbc));
5312: }
5313: PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5314: PetscFunctionReturn(PETSC_SUCCESS);
5315: }
5317: 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)
5318: {
5319: DM_Plex *mesh = (DM_Plex *)dm->data;
5320: DM plex = NULL, plexA = NULL, tdm;
5321: DMEnclosureType encAux;
5322: PetscDS prob, probAux = NULL;
5323: PetscSection section, sectionAux = NULL;
5324: PetscSection globalSection;
5325: Vec locA = NULL, tv;
5326: PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5327: PetscInt v;
5328: PetscInt Nf, totDim, totDimAux = 0;
5329: PetscBool transform;
5331: PetscFunctionBegin;
5332: PetscCall(DMConvert(dm, DMPLEX, &plex));
5333: PetscCall(DMHasBasisTransform(dm, &transform));
5334: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5335: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5336: PetscCall(DMGetLocalSection(dm, §ion));
5337: PetscCall(DMGetDS(dm, &prob));
5338: PetscCall(PetscDSGetNumFields(prob, &Nf));
5339: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5340: PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5341: if (locA) {
5342: DM dmAux;
5344: PetscCall(VecGetDM(locA, &dmAux));
5345: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5346: PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5347: PetscCall(DMGetDS(plexA, &probAux));
5348: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5349: PetscCall(DMGetLocalSection(plexA, §ionAux));
5350: }
5352: PetscCall(DMGetGlobalSection(dm, &globalSection));
5353: for (v = 0; v < numValues; ++v) {
5354: PetscFEGeom *fgeom;
5355: PetscInt maxDegree;
5356: PetscQuadrature qGeom = NULL;
5357: IS pointIS;
5358: const PetscInt *points;
5359: PetscFormKey key;
5360: PetscInt numFaces, face, Nq;
5362: key.label = label;
5363: key.value = values[v];
5364: key.part = 0;
5365: PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5366: if (!pointIS) continue; /* No points with that id on this process */
5367: {
5368: IS isectIS;
5370: /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5371: PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5372: PetscCall(ISDestroy(&pointIS));
5373: pointIS = isectIS;
5374: }
5375: PetscCall(ISGetLocalSize(pointIS, &numFaces));
5376: PetscCall(ISGetIndices(pointIS, &points));
5377: PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a));
5378: PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5379: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5380: if (!qGeom) {
5381: PetscFE fe;
5383: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5384: PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5385: PetscCall(PetscObjectReference((PetscObject)qGeom));
5386: }
5387: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5388: PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5389: for (face = 0; face < numFaces; ++face) {
5390: const PetscInt point = points[face], *support;
5391: PetscScalar *x = NULL;
5392: PetscInt i;
5394: PetscCall(DMPlexGetSupport(dm, point, &support));
5395: PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5396: for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5397: PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5398: if (locX_t) {
5399: PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5400: for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5401: PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5402: }
5403: if (locA) {
5404: PetscInt subp;
5405: PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5406: PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5407: for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5408: PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5409: }
5410: }
5411: PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5412: {
5413: PetscFE fe;
5414: PetscInt Nb;
5415: /* Conforming batches */
5416: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5417: /* Remainder */
5418: PetscFEGeom *chunkGeom = NULL;
5419: PetscInt fieldJ, Nr, offset;
5421: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5422: PetscCall(PetscFEGetDimension(fe, &Nb));
5423: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5424: blockSize = Nb;
5425: batchSize = numBlocks * blockSize;
5426: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5427: numChunks = numFaces / (numBatches * batchSize);
5428: Ne = numChunks * numBatches * batchSize;
5429: Nr = numFaces % (numBatches * batchSize);
5430: offset = numFaces - Nr;
5431: PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5432: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5433: key.field = fieldI * Nf + fieldJ;
5434: PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5435: }
5436: PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5437: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5438: key.field = fieldI * Nf + fieldJ;
5439: PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim]));
5440: }
5441: PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5442: }
5443: for (face = 0; face < numFaces; ++face) {
5444: const PetscInt point = points[face], *support;
5446: /* Transform to global basis before insertion in Jacobian */
5447: PetscCall(DMPlexGetSupport(plex, point, &support));
5448: if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5449: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5450: PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5451: }
5452: PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5453: PetscCall(PetscQuadratureDestroy(&qGeom));
5454: PetscCall(ISRestoreIndices(pointIS, &points));
5455: PetscCall(ISDestroy(&pointIS));
5456: PetscCall(PetscFree4(u, u_t, elemMat, a));
5457: }
5458: if (plex) PetscCall(DMDestroy(&plex));
5459: if (plexA) PetscCall(DMDestroy(&plexA));
5460: PetscFunctionReturn(PETSC_SUCCESS);
5461: }
5463: 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)
5464: {
5465: DMField coordField;
5466: DMLabel depthLabel;
5467: IS facetIS;
5468: PetscInt dim;
5470: PetscFunctionBegin;
5471: PetscCall(DMGetDimension(dm, &dim));
5472: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5473: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5474: PetscCall(DMGetCoordinateField(dm, &coordField));
5475: PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5476: PetscCall(ISDestroy(&facetIS));
5477: PetscFunctionReturn(PETSC_SUCCESS);
5478: }
5480: PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5481: {
5482: PetscDS prob;
5483: PetscInt dim, numBd, bd;
5484: DMLabel depthLabel;
5485: DMField coordField = NULL;
5486: IS facetIS;
5488: PetscFunctionBegin;
5489: PetscCall(DMGetDS(dm, &prob));
5490: PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5491: PetscCall(DMGetDimension(dm, &dim));
5492: PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5493: PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5494: PetscCall(DMGetCoordinateField(dm, &coordField));
5495: for (bd = 0; bd < numBd; ++bd) {
5496: PetscWeakForm wf;
5497: DMBoundaryConditionType type;
5498: DMLabel label;
5499: const PetscInt *values;
5500: PetscInt fieldI, numValues;
5501: PetscObject obj;
5502: PetscClassId id;
5504: PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5505: if (type & DM_BC_ESSENTIAL) continue;
5506: PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5507: PetscCall(PetscObjectGetClassId(obj, &id));
5508: if (id != PETSCFE_CLASSID) continue;
5509: PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5510: }
5511: PetscCall(ISDestroy(&facetIS));
5512: PetscFunctionReturn(PETSC_SUCCESS);
5513: }
5515: 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)
5516: {
5517: DM_Plex *mesh = (DM_Plex *)dm->data;
5518: const char *name = "Jacobian";
5519: DM dmAux = NULL, plex, tdm;
5520: DMEnclosureType encAux;
5521: Vec A, tv;
5522: DMField coordField;
5523: PetscDS prob, probAux = NULL;
5524: PetscSection section, globalSection, sectionAux;
5525: PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5526: const PetscInt *cells;
5527: PetscInt Nf, fieldI, fieldJ;
5528: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5529: PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
5531: PetscFunctionBegin;
5532: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5533: if (!cellIS) goto end;
5534: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5535: PetscCall(ISGetLocalSize(cellIS, &numCells));
5536: if (cStart >= cEnd) goto end;
5537: PetscCall(DMHasBasisTransform(dm, &transform));
5538: PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5539: PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5540: PetscCall(DMGetLocalSection(dm, §ion));
5541: PetscCall(DMGetGlobalSection(dm, &globalSection));
5542: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
5543: PetscCall(PetscDSGetNumFields(prob, &Nf));
5544: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5545: PetscCall(PetscDSHasJacobian(prob, &hasJac));
5546: PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5547: /* user passed in the same matrix, avoid double contributions and
5548: only assemble the Jacobian */
5549: if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5550: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5551: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5552: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5553: if (A) {
5554: PetscCall(VecGetDM(A, &dmAux));
5555: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5556: PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5557: PetscCall(DMGetLocalSection(plex, §ionAux));
5558: PetscCall(DMGetDS(dmAux, &probAux));
5559: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5560: }
5561: PetscCall(PetscMalloc5(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, hasJac ? numCells * totDim * totDim : 0, &elemMat, hasPrec ? numCells * totDim * totDim : 0, &elemMatP, hasDyn ? numCells * totDim * totDim : 0, &elemMatD));
5562: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5563: PetscCall(DMGetCoordinateField(dm, &coordField));
5564: for (c = cStart; c < cEnd; ++c) {
5565: const PetscInt cell = cells ? cells[c] : c;
5566: const PetscInt cind = c - cStart;
5567: PetscScalar *x = NULL, *x_t = NULL;
5568: PetscInt i;
5570: PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5571: for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5572: PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5573: if (X_t) {
5574: PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5575: for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5576: PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5577: }
5578: if (dmAux) {
5579: PetscInt subcell;
5580: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5581: PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5582: for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5583: PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5584: }
5585: }
5586: if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5587: if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5588: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5589: for (fieldI = 0; fieldI < Nf; ++fieldI) {
5590: PetscClassId id;
5591: PetscFE fe;
5592: PetscQuadrature qGeom = NULL;
5593: PetscInt Nb;
5594: /* Conforming batches */
5595: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5596: /* Remainder */
5597: PetscInt Nr, offset, Nq;
5598: PetscInt maxDegree;
5599: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
5601: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5602: PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5603: if (id == PETSCFV_CLASSID) {
5604: hasFV = PETSC_TRUE;
5605: continue;
5606: }
5607: PetscCall(PetscFEGetDimension(fe, &Nb));
5608: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5609: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5610: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5611: if (!qGeom) {
5612: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5613: PetscCall(PetscObjectReference((PetscObject)qGeom));
5614: }
5615: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5616: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5617: blockSize = Nb;
5618: batchSize = numBlocks * blockSize;
5619: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5620: numChunks = numCells / (numBatches * batchSize);
5621: Ne = numChunks * numBatches * batchSize;
5622: Nr = numCells % (numBatches * batchSize);
5623: offset = numCells - Nr;
5624: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5625: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5626: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5627: key.field = fieldI * Nf + fieldJ;
5628: if (hasJac) {
5629: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5630: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMat[offset * totDim * totDim]));
5631: }
5632: if (hasPrec) {
5633: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5634: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatP[offset * totDim * totDim]));
5635: }
5636: if (hasDyn) {
5637: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5638: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
5639: }
5640: }
5641: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5642: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5643: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5644: PetscCall(PetscQuadratureDestroy(&qGeom));
5645: }
5646: /* Add contribution from X_t */
5647: if (hasDyn) {
5648: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5649: }
5650: if (hasFV) {
5651: PetscClassId id;
5652: PetscFV fv;
5653: PetscInt offsetI, NcI, NbI = 1, fc, f;
5655: for (fieldI = 0; fieldI < Nf; ++fieldI) {
5656: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5657: PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5658: PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5659: if (id != PETSCFV_CLASSID) continue;
5660: /* Put in the identity */
5661: PetscCall(PetscFVGetNumComponents(fv, &NcI));
5662: for (c = cStart; c < cEnd; ++c) {
5663: const PetscInt cind = c - cStart;
5664: const PetscInt eOffset = cind * totDim * totDim;
5665: for (fc = 0; fc < NcI; ++fc) {
5666: for (f = 0; f < NbI; ++f) {
5667: const PetscInt i = offsetI + f * NcI + fc;
5668: if (hasPrec) {
5669: if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5670: elemMatP[eOffset + i * totDim + i] = 1.0;
5671: } else {
5672: elemMat[eOffset + i * totDim + i] = 1.0;
5673: }
5674: }
5675: }
5676: }
5677: }
5678: /* No allocated space for FV stuff, so ignore the zero entries */
5679: PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5680: }
5681: /* Insert values into matrix */
5682: for (c = cStart; c < cEnd; ++c) {
5683: const PetscInt cell = cells ? cells[c] : c;
5684: const PetscInt cind = c - cStart;
5686: /* Transform to global basis before insertion in Jacobian */
5687: if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5688: if (hasPrec) {
5689: if (hasJac) {
5690: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5691: PetscCall(DMPlexMatSetClosure(dm, section, globalSection, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5692: }
5693: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5694: PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5695: } else {
5696: if (hasJac) {
5697: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5698: PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5699: }
5700: }
5701: }
5702: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5703: if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5704: PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5705: if (dmAux) {
5706: PetscCall(PetscFree(a));
5707: PetscCall(DMDestroy(&plex));
5708: }
5709: /* Compute boundary integrals */
5710: PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5711: /* Assemble matrix */
5712: end : {
5713: PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;
5715: PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5716: if (hasJac && hasPrec) {
5717: PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5718: PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5719: }
5720: }
5721: PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5722: PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5723: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5724: PetscFunctionReturn(PETSC_SUCCESS);
5725: }
5727: 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)
5728: {
5729: DM_Plex *mesh = (DM_Plex *)dm->data;
5730: const char *name = "Hybrid Jacobian";
5731: DM dmAux[3] = {NULL, NULL, NULL};
5732: DMLabel ghostLabel = NULL;
5733: DM plex = NULL;
5734: DM plexA = NULL;
5735: PetscDS ds = NULL;
5736: PetscDS dsIn = NULL;
5737: PetscDS dsAux[3] = {NULL, NULL, NULL};
5738: Vec locA[3] = {NULL, NULL, NULL};
5739: DM dmScale[3] = {NULL, NULL, NULL};
5740: PetscDS dsScale[3] = {NULL, NULL, NULL};
5741: Vec locS[3] = {NULL, NULL, NULL};
5742: PetscSection section = NULL;
5743: PetscSection sectionAux[3] = {NULL, NULL, NULL};
5744: DMField coordField = NULL;
5745: PetscScalar *a[3] = {NULL, NULL, NULL};
5746: PetscScalar *s[3] = {NULL, NULL, NULL};
5747: PetscScalar *u = NULL, *u_t;
5748: PetscScalar *elemMatNeg, *elemMatPos, *elemMatCoh;
5749: PetscScalar *elemMatNegP, *elemMatPosP, *elemMatCohP;
5750: PetscSection globalSection;
5751: IS chunkIS;
5752: const PetscInt *cells;
5753: PetscInt *faces;
5754: PetscInt cStart, cEnd, numCells;
5755: PetscInt Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale, numChunks, cellChunkSize, chunk;
5756: PetscInt maxDegree = PETSC_MAX_INT;
5757: PetscQuadrature affineQuad = NULL, *quads = NULL;
5758: PetscFEGeom *affineGeom = NULL, **geoms = NULL;
5759: PetscBool hasBdJac, hasBdPrec;
5761: PetscFunctionBegin;
5762: if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5763: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5764: PetscCall(ISGetLocalSize(cellIS, &numCells));
5765: if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5766: if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5767: const char *name;
5768: PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5769: 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);
5770: }
5771: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5772: PetscCall(DMConvert(dm, DMPLEX, &plex));
5773: PetscCall(DMGetSection(dm, §ion));
5774: PetscCall(DMGetGlobalSection(dm, &globalSection));
5775: PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5776: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5777: PetscCall(PetscDSGetNumFields(ds, &Nf));
5778: PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5779: PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5780: PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5781: PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5782: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5783: if (locA[2]) {
5784: const PetscInt cellStart = cells ? cells[cStart] : cStart;
5786: PetscCall(VecGetDM(locA[2], &dmAux[2]));
5787: PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5788: PetscCall(DMGetSection(dmAux[2], §ionAux[2]));
5789: PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5790: PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5791: {
5792: const PetscInt *cone;
5793: PetscInt c;
5795: PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5796: for (c = 0; c < 2; ++c) {
5797: const PetscInt *support;
5798: PetscInt ssize, s;
5800: PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5801: PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5802: 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);
5803: if (support[0] == cellStart) s = 1;
5804: else if (support[1] == cellStart) s = 0;
5805: else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5806: PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5807: if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5808: else dmAux[c] = dmAux[2];
5809: PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5810: PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5811: }
5812: }
5813: }
5814: /* Handle mass matrix scaling
5815: The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5816: PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5817: if (locS[2]) {
5818: PetscInt Nb, Nbs;
5820: PetscCall(VecGetDM(locS[2], &dmScale[2]));
5821: PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
5822: locS[1] = locS[0] = locS[2];
5823: dmScale[1] = dmScale[0] = dmScale[2];
5824: dsScale[1] = dsScale[0] = dsScale[2];
5825: PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale));
5826: // BRAD: This is not set correctly
5827: key[2].field = 2;
5828: PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5829: PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5830: 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);
5831: }
5832: /* 2: Setup geometric data */
5833: PetscCall(DMGetCoordinateField(dm, &coordField));
5834: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5835: if (maxDegree > 1) {
5836: PetscInt f;
5837: PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5838: for (f = 0; f < Nf; ++f) {
5839: PetscFE fe;
5841: PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5842: if (fe) {
5843: PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5844: PetscCall(PetscObjectReference((PetscObject)quads[f]));
5845: }
5846: }
5847: }
5848: /* Loop over chunks */
5849: cellChunkSize = numCells;
5850: numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5851: PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5852: PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5853: /* Extract field coefficients */
5854: /* NOTE This needs the end cap faces to have identical orientations */
5855: PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5856: PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5857: PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_FALSE, s));
5858: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
5859: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
5860: PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
5861: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
5862: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
5863: PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
5864: for (chunk = 0; chunk < numChunks; ++chunk) {
5865: PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
5867: if (hasBdJac) {
5868: PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
5869: PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
5870: PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
5871: }
5872: if (hasBdPrec) {
5873: PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
5874: PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
5875: PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
5876: }
5877: /* Get faces */
5878: for (c = cS; c < cE; ++c) {
5879: const PetscInt cell = cells ? cells[c] : c;
5880: const PetscInt *cone;
5881: PetscCall(DMPlexGetCone(plex, cell, &cone));
5882: faces[(c - cS) * 2 + 0] = cone[0];
5883: faces[(c - cS) * 2 + 1] = cone[1];
5884: }
5885: PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5886: if (maxDegree <= 1) {
5887: if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5888: if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5889: } else {
5890: PetscInt f;
5891: for (f = 0; f < Nf; ++f) {
5892: if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5893: }
5894: }
5896: for (fieldI = 0; fieldI < Nf; ++fieldI) {
5897: PetscFE feI;
5898: PetscFEGeom *geom = affineGeom ? affineGeom : geoms[fieldI];
5899: PetscFEGeom *chunkGeom = NULL, *remGeom = NULL;
5900: PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
5901: PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5902: PetscBool isCohesiveField;
5904: PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
5905: if (!feI) continue;
5906: PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
5907: PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5908: PetscCall(PetscFEGetDimension(feI, &Nb));
5909: blockSize = Nb;
5910: batchSize = numBlocks * blockSize;
5911: PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
5912: numChunks = numCells / (numBatches * batchSize);
5913: Ne = numChunks * numBatches * batchSize;
5914: Nr = numCells % (numBatches * batchSize);
5915: offset = numCells - Nr;
5916: PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5917: PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5918: PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
5919: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5920: PetscFE feJ;
5922: PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
5923: if (!feJ) continue;
5924: key[0].field = fieldI * Nf + fieldJ;
5925: key[1].field = fieldI * Nf + fieldJ;
5926: key[2].field = fieldI * Nf + fieldJ;
5927: if (hasBdJac) {
5928: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
5929: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
5930: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
5931: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPos[offset * totDim * totDim]));
5932: }
5933: if (hasBdPrec) {
5934: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
5935: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
5936: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
5937: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
5938: }
5939: if (hasBdJac) {
5940: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
5941: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
5942: }
5943: if (hasBdPrec) {
5944: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
5945: PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
5946: }
5947: }
5948: PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5949: PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5950: }
5951: /* Insert values into matrix */
5952: for (c = cS; c < cE; ++c) {
5953: const PetscInt cell = cells ? cells[c] : c;
5954: const PetscInt cind = c - cS, coff = cind * totDim * totDim;
5955: PetscInt i, j;
5957: /* Scale element values */
5958: if (locS[0]) {
5959: PetscInt Nb, soff = cind * totDimScale, off = 0;
5960: PetscBool cohesive;
5962: for (fieldI = 0; fieldI < Nf; ++fieldI) {
5963: PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
5964: PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));
5966: if (fieldI == key[2].field) {
5967: PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5968: // No cohesive scaling field is currently input
5969: for (i = 0; i < Nb; ++i)
5970: 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];
5971: off += Nb;
5972: } else {
5973: const PetscInt N = cohesive ? Nb : Nb * 2;
5975: for (i = 0; i < N; ++i)
5976: for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
5977: off += N;
5978: }
5979: }
5980: } else {
5981: for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
5982: }
5983: if (hasBdPrec) {
5984: if (hasBdJac) {
5985: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
5986: PetscCall(DMPlexMatSetClosure(plex, section, globalSection, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
5987: }
5988: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
5989: PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
5990: } else if (hasBdJac) {
5991: if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
5992: PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
5993: }
5994: }
5995: }
5996: PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5997: PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5998: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
5999: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6000: PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6001: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6002: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6003: PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6004: PetscCall(PetscFree(faces));
6005: PetscCall(ISDestroy(&chunkIS));
6006: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6007: if (maxDegree <= 1) {
6008: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
6009: PetscCall(PetscQuadratureDestroy(&affineQuad));
6010: } else {
6011: PetscInt f;
6012: for (f = 0; f < Nf; ++f) {
6013: if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
6014: if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
6015: }
6016: PetscCall(PetscFree2(quads, geoms));
6017: }
6018: if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6019: PetscCall(DMDestroy(&plex));
6020: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6021: PetscFunctionReturn(PETSC_SUCCESS);
6022: }
6024: /*
6025: 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.
6027: Input Parameters:
6028: + dm - The mesh
6029: . key - The PetscWeakFormKey indcating where integration should happen
6030: . cellIS - The cells to integrate over
6031: . t - The time
6032: . X_tShift - The multiplier for the Jacobian with repsect to X_t
6033: . X - Local solution vector
6034: . X_t - Time-derivative of the local solution vector
6035: . Y - Local input vector
6036: - user - the user context
6038: Output Parameter:
6039: . Z - Local output vector
6041: Note:
6042: We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
6043: like a GPU, or vectorize on a multicore machine.
6044: */
6045: 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)
6046: {
6047: DM_Plex *mesh = (DM_Plex *)dm->data;
6048: const char *name = "Jacobian";
6049: DM dmAux = NULL, plex, plexAux = NULL;
6050: DMEnclosureType encAux;
6051: Vec A;
6052: DMField coordField;
6053: PetscDS prob, probAux = NULL;
6054: PetscQuadrature quad;
6055: PetscSection section, globalSection, sectionAux;
6056: PetscScalar *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6057: const PetscInt *cells;
6058: PetscInt Nf, fieldI, fieldJ;
6059: PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6060: PetscBool hasDyn;
6062: PetscFunctionBegin;
6063: PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6064: PetscCall(DMConvert(dm, DMPLEX, &plex));
6065: if (!cellIS) {
6066: PetscInt depth;
6068: PetscCall(DMPlexGetDepth(plex, &depth));
6069: PetscCall(DMGetStratumIS(plex, "dim", depth, &cellIS));
6070: if (!cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, &cellIS));
6071: } else {
6072: PetscCall(PetscObjectReference((PetscObject)cellIS));
6073: }
6074: PetscCall(ISGetLocalSize(cellIS, &numCells));
6075: PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6076: PetscCall(DMGetLocalSection(dm, §ion));
6077: PetscCall(DMGetGlobalSection(dm, &globalSection));
6078: PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6079: PetscCall(PetscDSGetNumFields(prob, &Nf));
6080: PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6081: PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6082: hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6083: PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6084: if (A) {
6085: PetscCall(VecGetDM(A, &dmAux));
6086: PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6087: PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6088: PetscCall(DMGetLocalSection(plexAux, §ionAux));
6089: PetscCall(DMGetDS(dmAux, &probAux));
6090: PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6091: }
6092: PetscCall(VecSet(Z, 0.0));
6093: PetscCall(PetscMalloc6(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, numCells * totDim * totDim, &elemMat, hasDyn ? numCells * totDim * totDim : 0, &elemMatD, numCells * totDim, &y, totDim, &z));
6094: if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6095: PetscCall(DMGetCoordinateField(dm, &coordField));
6096: for (c = cStart; c < cEnd; ++c) {
6097: const PetscInt cell = cells ? cells[c] : c;
6098: const PetscInt cind = c - cStart;
6099: PetscScalar *x = NULL, *x_t = NULL;
6100: PetscInt i;
6102: PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
6103: for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6104: PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
6105: if (X_t) {
6106: PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
6107: for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6108: PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
6109: }
6110: if (dmAux) {
6111: PetscInt subcell;
6112: PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6113: PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6114: for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6115: PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6116: }
6117: PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
6118: for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6119: PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
6120: }
6121: PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6122: if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6123: for (fieldI = 0; fieldI < Nf; ++fieldI) {
6124: PetscFE fe;
6125: PetscInt Nb;
6126: /* Conforming batches */
6127: PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6128: /* Remainder */
6129: PetscInt Nr, offset, Nq;
6130: PetscQuadrature qGeom = NULL;
6131: PetscInt maxDegree;
6132: PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;
6134: PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6135: PetscCall(PetscFEGetQuadrature(fe, &quad));
6136: PetscCall(PetscFEGetDimension(fe, &Nb));
6137: PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6138: PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6139: if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6140: if (!qGeom) {
6141: PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6142: PetscCall(PetscObjectReference((PetscObject)qGeom));
6143: }
6144: PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6145: PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6146: blockSize = Nb;
6147: batchSize = numBlocks * blockSize;
6148: PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6149: numChunks = numCells / (numBatches * batchSize);
6150: Ne = numChunks * numBatches * batchSize;
6151: Nr = numCells % (numBatches * batchSize);
6152: offset = numCells - Nr;
6153: PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6154: PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6155: for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6156: key.field = fieldI * Nf + fieldJ;
6157: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6158: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMat[offset * totDim * totDim]));
6159: if (hasDyn) {
6160: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6161: PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
6162: }
6163: }
6164: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6165: PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6166: PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6167: PetscCall(PetscQuadratureDestroy(&qGeom));
6168: }
6169: if (hasDyn) {
6170: for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6171: }
6172: for (c = cStart; c < cEnd; ++c) {
6173: const PetscInt cell = cells ? cells[c] : c;
6174: const PetscInt cind = c - cStart;
6175: const PetscBLASInt M = totDim, one = 1;
6176: const PetscScalar a = 1.0, b = 0.0;
6178: PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6179: if (mesh->printFEM > 1) {
6180: PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6181: PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6182: PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6183: }
6184: PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
6185: }
6186: PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6187: if (mesh->printFEM) {
6188: PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
6189: PetscCall(VecView(Z, NULL));
6190: }
6191: PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6192: PetscCall(PetscFree(a));
6193: PetscCall(ISDestroy(&cellIS));
6194: PetscCall(DMDestroy(&plexAux));
6195: PetscCall(DMDestroy(&plex));
6196: PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6197: PetscFunctionReturn(PETSC_SUCCESS);
6198: }