#include /*I "petscdmplex.h" I*/ #include /*@ DMPlexGetActivePoint - Get the point on which projection is currently working Not Collective Input Parameter: . dm - the `DM` Output Parameter: . point - The mesh point involved in the current projection Level: developer .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetActivePoint()` @*/ PetscErrorCode DMPlexGetActivePoint(DM dm, PetscInt *point) { PetscFunctionBeginHot; *point = ((DM_Plex *)dm->data)->activePoint; PetscFunctionReturn(PETSC_SUCCESS); } /*@ DMPlexSetActivePoint - Set the point on which projection is currently working Not Collective Input Parameters: + dm - the `DM` - point - The mesh point involved in the current projection Level: developer .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetActivePoint()` @*/ PetscErrorCode DMPlexSetActivePoint(DM dm, PetscInt point) { PetscFunctionBeginHot; ((DM_Plex *)dm->data)->activePoint = point; PetscFunctionReturn(PETSC_SUCCESS); } /* DMProjectPoint_Func_Private - Interpolate the given function in the output basis on the given point Input Parameters: + dm - The output `DM` . ds - The output `DS` . dmIn - The input `DM` . dsIn - The input `DS` . time - The time for this evaluation . fegeom - The FE geometry for this point . fvgeom - The FV geometry for this point . isFE - Flag indicating whether each output field has an FE discretization . sp - The output `PetscDualSpace` for each field . funcs - The evaluation function for each field - ctxs - The user context for each field Output Parameter: . values - The value for each dual basis vector in the output dual space Level: developer .seealso:[](ch_unstructured), `DM`, `DMPLEX`, `PetscDS`, `PetscFEGeom`, `PetscFVCellGeom`, `PetscDualSpace` */ static PetscErrorCode DMProjectPoint_Func_Private(DM dm, PetscDS ds, DM dmIn, PetscDS dsIn, PetscReal time, PetscFEGeom *fegeom, PetscFVCellGeom *fvgeom, PetscBool isFE[], PetscDualSpace sp[], PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, PetscScalar values[]) { PetscInt debug = ((DM_Plex *)dm->data)->printProject; PetscInt coordDim, Nf, *Nc, f, spDim, d, v, tp; PetscBool isAffine, isCohesive, transform; PetscFunctionBeginHot; PetscCall(DMGetCoordinateDim(dmIn, &coordDim)); PetscCall(DMHasBasisTransform(dmIn, &transform)); PetscCall(PetscDSGetNumFields(ds, &Nf)); PetscCall(PetscDSGetComponents(ds, &Nc)); PetscCall(PetscDSIsCohesive(ds, &isCohesive)); /* Get values for closure */ isAffine = fegeom->isAffine; for (f = 0, v = 0, tp = 0; f < Nf; ++f) { void *const ctx = ctxs ? ctxs[f] : NULL; PetscBool cohesive; if (!sp[f]) continue; PetscCall(PetscDSGetCohesive(ds, f, &cohesive)); PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim)); if (funcs[f]) { if (isFE[f]) { PetscQuadrature allPoints; PetscInt q, dim, numPoints; const PetscReal *points; PetscScalar *pointEval; PetscReal *x; DM rdm; PetscCall(PetscDualSpaceGetDM(sp[f], &rdm)); PetscCall(PetscDualSpaceGetAllData(sp[f], &allPoints, NULL)); PetscCall(PetscQuadratureGetData(allPoints, &dim, NULL, &numPoints, &points, NULL)); PetscCall(DMGetWorkArray(rdm, numPoints * Nc[f], MPIU_SCALAR, &pointEval)); PetscCall(DMGetWorkArray(rdm, coordDim, MPIU_REAL, &x)); PetscCall(PetscArrayzero(pointEval, numPoints * Nc[f])); for (q = 0; q < numPoints; q++, tp++) { const PetscReal *v0; if (isAffine) { const PetscReal *refpoint = &points[q * dim]; PetscReal injpoint[3] = {0., 0., 0.}; if (dim != fegeom->dim) { if (isCohesive) { /* We just need to inject into the higher dimensional space assuming the last dimension is collapsed */ for (d = 0; d < dim; ++d) injpoint[d] = refpoint[d]; refpoint = injpoint; } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Reference spatial dimension %" PetscInt_FMT " != %" PetscInt_FMT " dual basis spatial dimension", fegeom->dim, dim); } CoordinatesRefToReal(coordDim, fegeom->dim, fegeom->xi, fegeom->v, fegeom->J, refpoint, x); v0 = x; } else { v0 = &fegeom->v[tp * coordDim]; } if (transform) { PetscCall(DMPlexBasisTransformApplyReal_Internal(dmIn, v0, PETSC_TRUE, coordDim, v0, x, dm->transformCtx)); v0 = x; } if (debug > 3) { PetscInt ap; PetscCall(DMPlexGetActivePoint(dm, &ap)); PetscCall(PetscPrintf(PETSC_COMM_SELF, "Project point %" PetscInt_FMT ", analytic: ref (", ap)); for (PetscInt d = 0; d < dim; ++d) { if (d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", ")); PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)points[q * dim + d])); } PetscCall(PetscPrintf(PETSC_COMM_SELF, ") real (")); for (PetscInt d = 0; d < dim; ++d) { if (d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", ")); PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)v0[d])); } PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n")); } PetscCall((*funcs[f])(coordDim, time, v0, Nc[f], &pointEval[Nc[f] * q], ctx)); } /* Transform point evaluations pointEval[q,c] */ PetscCall(PetscDualSpacePullback(sp[f], fegeom, numPoints, Nc[f], pointEval)); PetscCall(PetscDualSpaceApplyAll(sp[f], pointEval, &values[v])); PetscCall(DMRestoreWorkArray(rdm, coordDim, MPIU_REAL, &x)); PetscCall(DMRestoreWorkArray(rdm, numPoints * Nc[f], MPIU_SCALAR, &pointEval)); v += spDim; if (isCohesive && !cohesive) { for (d = 0; d < spDim; d++, v++) values[v] = values[v - spDim]; } } else { for (d = 0; d < spDim; ++d, ++v) PetscCall(PetscDualSpaceApplyFVM(sp[f], d, time, fvgeom, Nc[f], funcs[f], ctx, &values[v])); } } else { for (d = 0; d < spDim; d++, v++) values[v] = 0.; if (isCohesive && !cohesive) { for (d = 0; d < spDim; d++, v++) values[v] = 0.; } } } PetscFunctionReturn(PETSC_SUCCESS); } /* DMProjectPoint_Field_Private - Interpolate a function of the given field, in the input basis, using the output basis on the given point Input Parameters: + dm - The output DM . ds - The output DS . dmIn - The input DM . dsIn - The input DS . dmAux - The auxiliary DM, which is always for the input space . dsAux - The auxiliary DS, which is always for the input space . time - The time for this evaluation . localU - The local solution . localA - The local auziliary fields . cgeom - The FE geometry for this point . sp - The output PetscDualSpace for each field . p - The point in the output DM . T - Input basis and derivatives for each field tabulated on the quadrature points . TAux - Auxiliary basis and derivatives for each aux field tabulated on the quadrature points . funcs - The evaluation function for each field - ctxs - The user context for each field Output Parameter: . values - The value for each dual basis vector in the output dual space Level: developer Note: Not supported for FV .seealso: `DMProjectPoint_Field_Private()` */ static PetscErrorCode DMProjectPoint_Field_Private(DM dm, PetscDS ds, DM dmIn, DMEnclosureType encIn, PetscDS dsIn, DM dmAux, DMEnclosureType encAux, PetscDS dsAux, PetscReal time, Vec localU, Vec localA, PetscFEGeom *cgeom, PetscDualSpace sp[], PetscInt p, PetscTabulation *T, PetscTabulation *TAux, 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[]), void **ctxs, PetscScalar values[]) { PetscSection section, sectionAux = NULL; PetscScalar *u, *u_t = NULL, *u_x, *a = NULL, *a_t = NULL, *a_x = NULL, *bc; PetscScalar *coefficients = NULL, *coefficientsAux = NULL; PetscScalar *coefficients_t = NULL, *coefficientsAux_t = NULL; const PetscScalar *constants; PetscReal *x; PetscInt *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL, *Nc, face[2]; PetscFEGeom fegeom, fgeomN[2]; const PetscInt dE = cgeom->dimEmbed, *cone, *ornt; PetscInt numConstants, Nf, NfIn, NfAux = 0, f, spDim, d, v, inp, tp = 0; PetscBool isAffine, isCohesive, isCohesiveIn, transform; DMPolytopeType qct; PetscFunctionBeginHot; PetscCall(PetscDSGetNumFields(ds, &Nf)); PetscCall(PetscDSGetComponents(ds, &Nc)); PetscCall(PetscDSIsCohesive(ds, &isCohesive)); PetscCall(PetscDSGetNumFields(dsIn, &NfIn)); PetscCall(PetscDSIsCohesive(dsIn, &isCohesiveIn)); PetscCall(PetscDSGetComponentOffsets(dsIn, &uOff)); PetscCall(PetscDSGetComponentDerivativeOffsets(dsIn, &uOff_x)); PetscCall(PetscDSGetEvaluationArrays(dsIn, &u, &bc /*&u_t*/, &u_x)); PetscCall(PetscDSGetWorkspace(dsIn, &x, NULL, NULL, NULL, NULL)); PetscCall(PetscDSGetConstants(dsIn, &numConstants, &constants)); PetscCall(DMHasBasisTransform(dmIn, &transform)); PetscCall(DMGetLocalSection(dmIn, §ion)); PetscCall(DMGetEnclosurePoint(dmIn, dm, encIn, p, &inp)); // Get cohesive cell hanging off face if (isCohesiveIn) { PetscCall(DMPlexGetCellType(dmIn, inp, &qct)); if ((qct != DM_POLYTOPE_POINT_PRISM_TENSOR) && (qct != DM_POLYTOPE_SEG_PRISM_TENSOR) && (qct != DM_POLYTOPE_TRI_PRISM_TENSOR) && (qct != DM_POLYTOPE_QUAD_PRISM_TENSOR)) { DMPolytopeType ct; const PetscInt *support; PetscInt Ns, s; PetscCall(DMPlexGetSupport(dmIn, inp, &support)); PetscCall(DMPlexGetSupportSize(dmIn, inp, &Ns)); for (s = 0; s < Ns; ++s) { PetscCall(DMPlexGetCellType(dmIn, support[s], &ct)); if ((ct == DM_POLYTOPE_POINT_PRISM_TENSOR) || (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) || (ct == DM_POLYTOPE_TRI_PRISM_TENSOR) || (ct == DM_POLYTOPE_QUAD_PRISM_TENSOR)) { inp = support[s]; break; } } PetscCheck(s < Ns, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cohesive cell not found from face %" PetscInt_FMT, inp); PetscCall(PetscDSGetComponentOffsetsCohesive(dsIn, 2, &uOff)); PetscCall(DMPlexGetOrientedCone(dmIn, inp, &cone, &ornt)); face[0] = 0; face[1] = 0; } } if (localU) PetscCall(DMPlexVecGetClosure(dmIn, section, localU, inp, NULL, &coefficients)); if (dmAux) { PetscInt subp; PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, p, &subp)); PetscCall(PetscDSGetNumFields(dsAux, &NfAux)); PetscCall(DMGetLocalSection(dmAux, §ionAux)); PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff)); PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x)); PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL /*&a_t*/, &a_x)); PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, localA, subp, NULL, &coefficientsAux)); } /* Get values for closure */ isAffine = cgeom->isAffine; fegeom.dim = cgeom->dim; fegeom.dimEmbed = cgeom->dimEmbed; if (isCohesiveIn) { fgeomN[0].dim = cgeom->dim; fgeomN[0].dimEmbed = cgeom->dimEmbed; fgeomN[1].dim = cgeom->dim; fgeomN[1].dimEmbed = cgeom->dimEmbed; } if (isAffine) { fegeom.v = x; fegeom.xi = cgeom->xi; fegeom.J = cgeom->J; fegeom.invJ = cgeom->invJ; fegeom.detJ = cgeom->detJ; if (isCohesiveIn) { fgeomN[0].J = cgeom->suppJ[0]; fgeomN[0].invJ = cgeom->suppInvJ[0]; fgeomN[0].detJ = cgeom->suppDetJ[0]; fgeomN[1].J = cgeom->suppJ[1]; fgeomN[1].invJ = cgeom->suppInvJ[1]; fgeomN[1].detJ = cgeom->suppDetJ[1]; } } for (f = 0, v = 0; f < Nf; ++f) { PetscQuadrature allPoints; PetscInt q, dim, numPoints; const PetscReal *points; PetscScalar *pointEval; PetscBool cohesive; DM dm; if (!sp[f]) continue; PetscCall(PetscDSGetCohesive(ds, f, &cohesive)); PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim)); if (!funcs[f]) { for (d = 0; d < spDim; d++, v++) values[v] = 0.; if (isCohesive && !cohesive) { for (d = 0; d < spDim; d++, v++) values[v] = 0.; } continue; } const PetscInt ***perms; PetscCall(PetscDualSpaceGetDM(sp[f], &dm)); PetscCall(PetscDualSpaceGetSymmetries(sp[f], &perms, NULL)); PetscCall(PetscDualSpaceGetAllData(sp[f], &allPoints, NULL)); PetscCall(PetscQuadratureGetData(allPoints, &dim, NULL, &numPoints, &points, NULL)); PetscCall(DMGetWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval)); for (q = 0; q < numPoints; ++q, ++tp) { PetscInt qpt[2]; if (isCohesiveIn) { qpt[0] = perms ? perms[0][ornt[0]][q] : q; qpt[1] = perms ? perms[0][DMPolytopeTypeComposeOrientationInv(qct, ornt[1], 0)][q] : q; } if (isAffine) { CoordinatesRefToReal(dE, cgeom->dim, fegeom.xi, cgeom->v, fegeom.J, &points[q * dim], x); } else { fegeom.v = &cgeom->v[tp * dE]; fegeom.J = &cgeom->J[tp * dE * dE]; fegeom.invJ = &cgeom->invJ[tp * dE * dE]; fegeom.detJ = &cgeom->detJ[tp]; if (isCohesiveIn) { fgeomN[0].J = &cgeom->suppJ[0][tp * dE * dE]; fgeomN[0].invJ = &cgeom->suppInvJ[0][tp * dE * dE]; fgeomN[0].detJ = &cgeom->suppDetJ[0][tp]; fgeomN[1].J = &cgeom->suppJ[1][tp * dE * dE]; fgeomN[1].invJ = &cgeom->suppInvJ[1][tp * dE * dE]; fgeomN[1].detJ = &cgeom->suppDetJ[1][tp]; } } if (coefficients) { if (isCohesiveIn) PetscCall(PetscFEEvaluateFieldJets_Hybrid_Internal(dsIn, NfIn, 0, tp, T, face, qpt, T, &fegeom, fgeomN, coefficients, coefficients_t, u, u_x, u_t)); else PetscCall(PetscFEEvaluateFieldJets_Internal(dsIn, NfIn, 0, tp, T, &fegeom, coefficients, coefficients_t, u, u_x, u_t)); } if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, tp, TAux, &fegeom, coefficientsAux, coefficientsAux_t, a, a_x, a_t)); if (transform) PetscCall(DMPlexBasisTransformApplyReal_Internal(dmIn, fegeom.v, PETSC_TRUE, dE, fegeom.v, fegeom.v, dm->transformCtx)); (*funcs[f])(dE, NfIn, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, a_t, a_x, time, fegeom.v, numConstants, constants, &pointEval[Nc[f] * q]); } PetscCall(PetscDualSpaceApplyAll(sp[f], pointEval, &values[v])); PetscCall(DMRestoreWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval)); v += spDim; /* TODO: For now, set both sides equal, but this should use info from other support cell */ if (isCohesive && !cohesive) { for (d = 0; d < spDim; d++, v++) values[v] = values[v - spDim]; } } if (localU) PetscCall(DMPlexVecRestoreClosure(dmIn, section, localU, inp, NULL, &coefficients)); if (dmAux) PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, localA, p, NULL, &coefficientsAux)); if (isCohesiveIn) PetscCall(DMPlexRestoreOrientedCone(dmIn, inp, &cone, &ornt)); PetscFunctionReturn(PETSC_SUCCESS); } static PetscErrorCode DMProjectPoint_BdField_Private(DM dm, PetscDS ds, DM dmIn, DMEnclosureType encIn, PetscDS dsIn, DM dmAux, DMEnclosureType encAux, PetscDS dsAux, PetscReal time, Vec localU, Vec localA, PetscFEGeom *fgeom, PetscDualSpace sp[], PetscInt p, PetscTabulation *T, PetscTabulation *TAux, 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[]), void **ctxs, PetscScalar values[]) { PetscSection section, sectionAux = NULL; PetscScalar *u, *u_t = NULL, *u_x, *a = NULL, *a_t = NULL, *a_x = NULL, *bc; PetscScalar *coefficients = NULL, *coefficientsAux = NULL; PetscScalar *coefficients_t = NULL, *coefficientsAux_t = NULL; const PetscScalar *constants; PetscReal *x; PetscInt *uOff, *uOff_x, *aOff = NULL, *aOff_x = NULL, *Nc, face[2]; PetscFEGeom fegeom, cgeom, fgeomN[2]; const PetscInt dE = fgeom->dimEmbed, *cone, *ornt; PetscInt numConstants, Nf, NfIn, NfAux = 0, f, spDim, d, v, inp, tp = 0; PetscBool isAffine, isCohesive, isCohesiveIn, transform; DMPolytopeType qct; PetscFunctionBeginHot; PetscCall(PetscDSGetNumFields(ds, &Nf)); PetscCall(PetscDSGetComponents(ds, &Nc)); PetscCall(PetscDSIsCohesive(ds, &isCohesive)); PetscCall(PetscDSGetNumFields(dsIn, &NfIn)); PetscCall(PetscDSIsCohesive(dsIn, &isCohesiveIn)); PetscCall(PetscDSGetComponentOffsets(dsIn, &uOff)); PetscCall(PetscDSGetComponentDerivativeOffsets(dsIn, &uOff_x)); PetscCall(PetscDSGetEvaluationArrays(dsIn, &u, &bc /*&u_t*/, &u_x)); PetscCall(PetscDSGetWorkspace(dsIn, &x, NULL, NULL, NULL, NULL)); PetscCall(PetscDSGetConstants(dsIn, &numConstants, &constants)); PetscCall(DMHasBasisTransform(dmIn, &transform)); PetscCall(DMGetLocalSection(dmIn, §ion)); PetscCall(DMGetEnclosurePoint(dmIn, dm, encIn, p, &inp)); // Get cohesive cell hanging off face if (isCohesiveIn) { PetscCall(DMPlexGetCellType(dmIn, inp, &qct)); if ((qct != DM_POLYTOPE_POINT_PRISM_TENSOR) && (qct != DM_POLYTOPE_SEG_PRISM_TENSOR) && (qct != DM_POLYTOPE_TRI_PRISM_TENSOR) && (qct != DM_POLYTOPE_QUAD_PRISM_TENSOR)) { DMPolytopeType ct; const PetscInt *support; PetscInt Ns, s; PetscCall(DMPlexGetSupport(dmIn, inp, &support)); PetscCall(DMPlexGetSupportSize(dmIn, inp, &Ns)); for (s = 0; s < Ns; ++s) { PetscCall(DMPlexGetCellType(dmIn, support[s], &ct)); if ((ct == DM_POLYTOPE_POINT_PRISM_TENSOR) || (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) || (ct == DM_POLYTOPE_TRI_PRISM_TENSOR) || (ct == DM_POLYTOPE_QUAD_PRISM_TENSOR)) { inp = support[s]; break; } } PetscCheck(s < Ns, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cohesive cell not found from face %" PetscInt_FMT, inp); PetscCall(PetscDSGetComponentOffsetsCohesive(dsIn, 2, &uOff)); PetscCall(DMPlexGetOrientedCone(dmIn, inp, &cone, &ornt)); face[0] = 0; face[1] = 0; } } if (localU) PetscCall(DMPlexVecGetClosure(dmIn, section, localU, inp, NULL, &coefficients)); if (dmAux) { PetscInt subp; PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, p, &subp)); PetscCall(PetscDSGetNumFields(dsAux, &NfAux)); PetscCall(DMGetLocalSection(dmAux, §ionAux)); PetscCall(PetscDSGetComponentOffsets(dsAux, &aOff)); PetscCall(PetscDSGetComponentDerivativeOffsets(dsAux, &aOff_x)); PetscCall(PetscDSGetEvaluationArrays(dsAux, &a, NULL /*&a_t*/, &a_x)); PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, localA, subp, NULL, &coefficientsAux)); } /* Get values for closure */ isAffine = fgeom->isAffine; fegeom.dim = fgeom->dim; fegeom.dimEmbed = fgeom->dimEmbed; fegeom.n = NULL; fegeom.J = NULL; fegeom.v = NULL; fegeom.xi = NULL; cgeom.dim = fgeom->dim; cgeom.dimEmbed = fgeom->dimEmbed; if (isCohesiveIn) { fgeomN[0].dim = fgeom->dim; fgeomN[0].dimEmbed = fgeom->dimEmbed; fgeomN[1].dim = fgeom->dim; fgeomN[1].dimEmbed = fgeom->dimEmbed; } if (isAffine) { fegeom.v = x; fegeom.xi = fgeom->xi; fegeom.J = fgeom->J; fegeom.invJ = fgeom->invJ; fegeom.detJ = fgeom->detJ; fegeom.n = fgeom->n; cgeom.J = fgeom->suppJ[0]; cgeom.invJ = fgeom->suppInvJ[0]; cgeom.detJ = fgeom->suppDetJ[0]; if (isCohesiveIn) { fgeomN[0].J = fgeom->suppJ[0]; fgeomN[0].invJ = fgeom->suppInvJ[0]; fgeomN[0].detJ = fgeom->suppDetJ[0]; fgeomN[1].J = fgeom->suppJ[1]; fgeomN[1].invJ = fgeom->suppInvJ[1]; fgeomN[1].detJ = fgeom->suppDetJ[1]; } } for (f = 0, v = 0; f < Nf; ++f) { PetscQuadrature allPoints; PetscInt q, dim, numPoints; const PetscReal *points; PetscScalar *pointEval; PetscBool cohesive; DM dm; if (!sp[f]) continue; PetscCall(PetscDSGetCohesive(ds, f, &cohesive)); PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim)); if (!funcs[f]) { for (d = 0; d < spDim; d++, v++) values[v] = 0.; if (isCohesive && !cohesive) { for (d = 0; d < spDim; d++, v++) values[v] = 0.; } continue; } PetscCall(PetscDualSpaceGetDM(sp[f], &dm)); PetscCall(PetscDualSpaceGetAllData(sp[f], &allPoints, NULL)); PetscCall(PetscQuadratureGetData(allPoints, &dim, NULL, &numPoints, &points, NULL)); PetscCall(DMGetWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval)); for (q = 0; q < numPoints; ++q, ++tp) { PetscInt qpt[2]; if (isCohesiveIn) { // These points are not integration quadratures, but dual space quadratures // If they had multiple points we should match them from both sides, similar to hybrid residual eval qpt[0] = qpt[1] = q; } if (isAffine) { CoordinatesRefToReal(dE, fgeom->dim, fegeom.xi, fgeom->v, fegeom.J, &points[q * dim], x); } else { fegeom.v = &fgeom->v[tp * dE]; fegeom.J = &fgeom->J[tp * dE * dE]; fegeom.invJ = &fgeom->invJ[tp * dE * dE]; fegeom.detJ = &fgeom->detJ[tp]; fegeom.n = &fgeom->n[tp * dE]; cgeom.J = &fgeom->suppJ[0][tp * dE * dE]; cgeom.invJ = &fgeom->suppInvJ[0][tp * dE * dE]; cgeom.detJ = &fgeom->suppDetJ[0][tp]; if (isCohesiveIn) { fgeomN[0].J = &fgeom->suppJ[0][tp * dE * dE]; fgeomN[0].invJ = &fgeom->suppInvJ[0][tp * dE * dE]; fgeomN[0].detJ = &fgeom->suppDetJ[0][tp]; fgeomN[1].J = &fgeom->suppJ[1][tp * dE * dE]; fgeomN[1].invJ = &fgeom->suppInvJ[1][tp * dE * dE]; fgeomN[1].detJ = &fgeom->suppDetJ[1][tp]; } } /* TODO We should use cgeom here, instead of fegeom, however the geometry coming in through fgeom does not have the support cell geometry */ if (coefficients) { if (isCohesiveIn) PetscCall(PetscFEEvaluateFieldJets_Hybrid_Internal(dsIn, NfIn, 0, tp, T, face, qpt, T, &fegeom, fgeomN, coefficients, coefficients_t, u, u_x, u_t)); else PetscCall(PetscFEEvaluateFieldJets_Internal(dsIn, NfIn, 0, tp, T, &cgeom, coefficients, coefficients_t, u, u_x, u_t)); } if (dsAux) PetscCall(PetscFEEvaluateFieldJets_Internal(dsAux, NfAux, 0, tp, TAux, &cgeom, coefficientsAux, coefficientsAux_t, a, a_x, a_t)); if (transform) PetscCall(DMPlexBasisTransformApplyReal_Internal(dmIn, fegeom.v, PETSC_TRUE, dE, fegeom.v, fegeom.v, dm->transformCtx)); (*funcs[f])(dE, NfIn, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, a_t, a_x, time, fegeom.v, fegeom.n, numConstants, constants, &pointEval[Nc[f] * q]); } PetscCall(PetscDualSpaceApplyAll(sp[f], pointEval, &values[v])); PetscCall(DMRestoreWorkArray(dm, numPoints * Nc[f], MPIU_SCALAR, &pointEval)); v += spDim; /* TODO: For now, set both sides equal, but this should use info from other support cell */ if (isCohesive && !cohesive) { for (d = 0; d < spDim; d++, v++) values[v] = values[v - spDim]; } } if (localU) PetscCall(DMPlexVecRestoreClosure(dmIn, section, localU, inp, NULL, &coefficients)); if (dmAux) PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, localA, p, NULL, &coefficientsAux)); if (isCohesiveIn) PetscCall(DMPlexRestoreOrientedCone(dmIn, inp, &cone, &ornt)); PetscFunctionReturn(PETSC_SUCCESS); } static PetscErrorCode DMProjectPoint_Private(DM dm, PetscDS ds, DM dmIn, DMEnclosureType encIn, PetscDS dsIn, DM dmAux, DMEnclosureType encAux, PetscDS dsAux, PetscFEGeom *fegeom, PetscInt effectiveHeight, PetscReal time, Vec localU, Vec localA, PetscBool hasFE, PetscBool hasFV, PetscBool isFE[], PetscDualSpace sp[], PetscInt p, PetscTabulation *T, PetscTabulation *TAux, DMBoundaryConditionType type, void (**funcs)(void), void **ctxs, PetscBool fieldActive[], PetscScalar values[]) { PetscFVCellGeom fvgeom; PetscInt dim, dimEmbed; PetscFunctionBeginHot; PetscCall(DMGetDimension(dm, &dim)); PetscCall(DMGetCoordinateDim(dm, &dimEmbed)); if (hasFV) PetscCall(DMPlexComputeCellGeometryFVM(dm, p, &fvgeom.volume, fvgeom.centroid, NULL)); switch (type) { case DM_BC_ESSENTIAL: case DM_BC_NATURAL: PetscCall(DMProjectPoint_Func_Private(dm, ds, dmIn, dsIn, time, fegeom, &fvgeom, isFE, sp, (PetscErrorCode (**)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *))funcs, ctxs, values)); break; case DM_BC_ESSENTIAL_FIELD: case DM_BC_NATURAL_FIELD: PetscCall(DMProjectPoint_Field_Private(dm, ds, dmIn, encIn, dsIn, dmAux, encAux, dsAux, time, localU, localA, fegeom, sp, p, T, TAux, (void (**)(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[]))funcs, ctxs, values)); break; case DM_BC_ESSENTIAL_BD_FIELD: PetscCall(DMProjectPoint_BdField_Private(dm, ds, dmIn, encIn, dsIn, dmAux, encAux, dsAux, time, localU, localA, fegeom, sp, p, T, TAux, (void (**)(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[]))funcs, ctxs, values)); break; default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown boundary condition type: %d", (int)type); } PetscFunctionReturn(PETSC_SUCCESS); } static PetscErrorCode PetscDualSpaceGetAllPointsUnion(PetscInt Nf, PetscDualSpace *sp, PetscInt dim, void (**funcs)(void), PetscQuadrature *allPoints) { PetscReal *points; PetscInt f, numPoints; PetscFunctionBegin; if (!dim) { PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, allPoints)); PetscFunctionReturn(PETSC_SUCCESS); } numPoints = 0; for (f = 0; f < Nf; ++f) { if (funcs[f]) { PetscQuadrature fAllPoints; PetscInt fNumPoints; PetscCall(PetscDualSpaceGetAllData(sp[f], &fAllPoints, NULL)); PetscCall(PetscQuadratureGetData(fAllPoints, NULL, NULL, &fNumPoints, NULL, NULL)); numPoints += fNumPoints; } } PetscCall(PetscMalloc1(dim * numPoints, &points)); numPoints = 0; for (f = 0; f < Nf; ++f) { if (funcs[f]) { PetscQuadrature fAllPoints; PetscInt qdim, fNumPoints, q; const PetscReal *fPoints; PetscCall(PetscDualSpaceGetAllData(sp[f], &fAllPoints, NULL)); PetscCall(PetscQuadratureGetData(fAllPoints, &qdim, NULL, &fNumPoints, &fPoints, NULL)); PetscCheck(qdim == dim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Spatial dimension %" PetscInt_FMT " for dual basis does not match input dimension %" PetscInt_FMT, qdim, dim); for (q = 0; q < fNumPoints * dim; ++q) points[numPoints * dim + q] = fPoints[q]; numPoints += fNumPoints; } } PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, allPoints)); PetscCall(PetscQuadratureSetData(*allPoints, dim, 0, numPoints, points, NULL)); PetscFunctionReturn(PETSC_SUCCESS); } /*@C DMGetFirstLabeledPoint - Find first labeled `point` in `odm` such that the corresponding point in `dm` has the specified `height`. Return `point` and the corresponding `ds`. Input Parameters: + dm - the `DM` . odm - the enclosing `DM` . label - label for `DM` domain, or `NULL` for whole domain . numIds - the number of `ids` . ids - An array of the label ids in sequence for the domain - height - Height of target cells in `DMPLEX` topology Output Parameters: + point - the first labeled point - ds - the `PetscDS` corresponding to the first labeled point Level: developer .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetActivePoint()`, `DMLabel`, `PetscDS` @*/ PetscErrorCode DMGetFirstLabeledPoint(DM dm, DM odm, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt height, PetscInt *point, PetscDS *ds) { DM plex; DMEnclosureType enc; PetscInt ls = -1; PetscFunctionBegin; if (point) *point = -1; if (!label) PetscFunctionReturn(PETSC_SUCCESS); PetscCall(DMGetEnclosureRelation(dm, odm, &enc)); PetscCall(DMConvert(dm, DMPLEX, &plex)); for (PetscInt i = 0; i < numIds; ++i) { IS labelIS; PetscInt num_points, pStart, pEnd; PetscCall(DMLabelGetStratumIS(label, ids[i], &labelIS)); if (!labelIS) continue; /* No points with that id on this process */ PetscCall(DMPlexGetHeightStratum(plex, height, &pStart, &pEnd)); PetscCall(ISGetSize(labelIS, &num_points)); if (num_points) { const PetscInt *points; PetscCall(ISGetIndices(labelIS, &points)); for (PetscInt i = 0; i < num_points; i++) { PetscInt point; PetscCall(DMGetEnclosurePoint(dm, odm, enc, points[i], &point)); if (pStart <= point && point < pEnd) { ls = point; if (ds) { // If this is a face of a cohesive cell, then prefer that DS if (height == 1) { const PetscInt *supp; PetscInt suppSize; DMPolytopeType ct; PetscCall(DMPlexGetSupport(dm, ls, &supp)); PetscCall(DMPlexGetSupportSize(dm, ls, &suppSize)); for (PetscInt s = 0; s < suppSize; ++s) { PetscCall(DMPlexGetCellType(dm, supp[s], &ct)); if ((ct == DM_POLYTOPE_POINT_PRISM_TENSOR) || (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) || (ct == DM_POLYTOPE_TRI_PRISM_TENSOR) || (ct == DM_POLYTOPE_QUAD_PRISM_TENSOR)) { ls = supp[s]; break; } } } PetscCall(DMGetCellDS(dm, ls, ds, NULL)); } if (ls >= 0) break; } } PetscCall(ISRestoreIndices(labelIS, &points)); } PetscCall(ISDestroy(&labelIS)); if (ls >= 0) break; } if (point) *point = ls; PetscCall(DMDestroy(&plex)); PetscFunctionReturn(PETSC_SUCCESS); } /* This function iterates over a manifold, and interpolates the input function/field using the basis provided by the DS in our DM There are several different scenarios: 1) Volumetric mesh with volumetric auxiliary data Here minHeight=0 since we loop over cells. 2) Boundary mesh with boundary auxiliary data Here minHeight=1 since we loop over faces. This normally happens since we hang cells off of our boundary meshes to facilitate computation. 3) Volumetric mesh with boundary auxiliary data Here minHeight=1 and auxbd=PETSC_TRUE since we loop over faces and use data only supported on those faces. This is common when imposing Dirichlet boundary conditions. 4) Volumetric input mesh with boundary output mesh Here we must get a subspace for the input DS The maxHeight is used to support enforcement of constraints in DMForest. If localU is given and not equal to localX, we call DMPlexInsertBoundaryValues() to complete it. If we are using an input field (DM_BC_ESSENTIAL_FIELD or DM_BC_NATURAL_FIELD), we need to evaluate it at all the quadrature points of the dual basis functionals. - We use effectiveHeight to mean the height above our incoming DS. For example, if the DS is for a submesh then the effective height is zero, whereas if the DS is for the volumetric mesh, but we are iterating over a surface, then the effective height is nonzero. When the effective height is nonzero, we need to extract dual spaces for the boundary from our input spaces. - After extracting all quadrature points, we tabulate the input fields and auxiliary fields on them. We check that the #dof(closure(p)) == #dual basis functionals(p) for a representative p in the iteration If we have a label, we iterate over those points. This will probably break the maxHeight functionality since we do not check the height of those points. */ static PetscErrorCode DMProjectLocal_Generic_Plex(DM dm, PetscReal time, Vec localU, PetscInt Ncc, const PetscInt comps[], DMLabel label, PetscInt numIds, const PetscInt ids[], DMBoundaryConditionType type, void (**funcs)(void), void **ctxs, InsertMode mode, Vec localX) { DM plex, dmIn, plexIn, dmAux = NULL, plexAux = NULL, tdm; DMEnclosureType encIn, encAux; PetscDS ds = NULL, dsIn = NULL, dsAux = NULL; Vec localA = NULL, tv; IS fieldIS; PetscSection section; PetscDualSpace *sp, *cellsp, *spIn, *cellspIn; PetscTabulation *T = NULL, *TAux = NULL; PetscInt *Nc; PetscInt dim, dimEmbed, depth, pStart, pEnd, lStart = PETSC_DETERMINE, htInc = 0, htIncIn = 0, htIncAux = 0, minHeight, maxHeight, minHeightIn, minHeightAux, h, regionNum, Nf, NfIn, NfAux = 0, NfTot, f; PetscBool *isFE, hasFE = PETSC_FALSE, hasFV = PETSC_FALSE, isCohesive = PETSC_FALSE, isCohesiveIn = PETSC_FALSE, transform; DMField coordField; DMLabel depthLabel; PetscQuadrature allPoints = NULL; PetscFunctionBegin; if (localU) PetscCall(VecGetDM(localU, &dmIn)); else dmIn = dm; PetscCall(DMGetAuxiliaryVec(dm, label, numIds ? ids[0] : 0, 0, &localA)); if (localA) PetscCall(VecGetDM(localA, &dmAux)); else dmAux = NULL; PetscCall(DMConvert(dm, DMPLEX, &plex)); PetscCall(DMConvert(dmIn, DMPLEX, &plexIn)); PetscCall(DMGetEnclosureRelation(dmIn, dm, &encIn)); PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); PetscCall(DMGetDimension(dm, &dim)); PetscCall(DMPlexGetVTKCellHeight(plex, &minHeight)); PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); PetscCall(DMHasBasisTransform(dm, &transform)); /* Auxiliary information can only be used with interpolation of field functions */ if (dmAux) { PetscCall(DMConvert(dmAux, DMPLEX, &plexAux)); if (type == DM_BC_ESSENTIAL_FIELD || type == DM_BC_ESSENTIAL_BD_FIELD || type == DM_BC_NATURAL_FIELD) PetscCheck(localA, PETSC_COMM_SELF, PETSC_ERR_USER, "Missing localA vector"); } if (localU && localU != localX) PetscCall(DMPlexInsertBoundaryValues(plexIn, PETSC_TRUE, localU, time, NULL, NULL, NULL)); PetscCall(DMGetCoordinateField(dm, &coordField)); PetscCheck(coordField, PETSC_COMM_SELF, PETSC_ERR_USER, "DM must have a coordinate field"); /**** No collective calls below this point ****/ /* Determine height for iteration of all meshes */ { DMPolytopeType ct, ctIn, ctAux; PetscInt p, pStartIn, pStartAux, pEndAux; PetscInt dim = -1, dimIn = -1, dimAux = -1; PetscCall(DMPlexGetSimplexOrBoxCells(plex, minHeight, &pStart, &pEnd)); if (pEnd > pStart) { PetscCall(DMGetFirstLabeledPoint(dm, dm, label, numIds, ids, minHeight, &lStart, NULL)); p = lStart < 0 ? pStart : lStart; PetscCall(DMPlexGetCellType(plex, p, &ct)); dim = DMPolytopeTypeGetDim(ct); PetscCall(DMPlexGetVTKCellHeight(plexIn, &minHeightIn)); PetscCall(DMPlexGetSimplexOrBoxCells(plexIn, minHeightIn, &pStartIn, NULL)); PetscCall(DMPlexGetCellType(plexIn, pStartIn, &ctIn)); dimIn = DMPolytopeTypeGetDim(ctIn); if (dmAux) { PetscCall(DMPlexGetVTKCellHeight(plexAux, &minHeightAux)); PetscCall(DMPlexGetSimplexOrBoxCells(plexAux, minHeightAux, &pStartAux, &pEndAux)); if (pStartAux < pEndAux) { PetscCall(DMPlexGetCellType(plexAux, pStartAux, &ctAux)); dimAux = DMPolytopeTypeGetDim(ctAux); } } else dimAux = dim; } else { PetscCall(DMDestroy(&plex)); PetscCall(DMDestroy(&plexIn)); if (dmAux) PetscCall(DMDestroy(&plexAux)); PetscFunctionReturn(PETSC_SUCCESS); } if (dim < 0) { DMLabel spmap = NULL, spmapIn = NULL, spmapAux = NULL; /* Fall back to determination based on being a submesh */ PetscCall(DMPlexGetSubpointMap(plex, &spmap)); PetscCall(DMPlexGetSubpointMap(plexIn, &spmapIn)); if (plexAux) PetscCall(DMPlexGetSubpointMap(plexAux, &spmapAux)); dim = spmap ? 1 : 0; dimIn = spmapIn ? 1 : 0; dimAux = spmapAux ? 1 : 0; } { PetscInt dimProj = PetscMin(PetscMin(dim, dimIn), dimAux < 0 ? PETSC_INT_MAX : dimAux); PetscInt dimAuxEff = dimAux < 0 ? dimProj : dimAux; PetscCheck(PetscAbsInt(dimProj - dim) <= 1 && PetscAbsInt(dimProj - dimIn) <= 1 && PetscAbsInt(dimProj - dimAuxEff) <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Do not currently support differences of more than 1 in dimension"); if (dimProj < dim) minHeight = 1; htInc = dim - dimProj; htIncIn = dimIn - dimProj; htIncAux = dimAuxEff - dimProj; } } PetscCall(DMPlexGetDepth(plex, &depth)); PetscCall(DMPlexGetDepthLabel(plex, &depthLabel)); PetscCall(DMPlexGetMaxProjectionHeight(plex, &maxHeight)); maxHeight = PetscMax(maxHeight, minHeight); PetscCheck(maxHeight >= 0 && maxHeight <= dim, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Maximum projection height %" PetscInt_FMT " not in [0, %" PetscInt_FMT ")", maxHeight, dim); PetscCall(DMGetFirstLabeledPoint(dm, dm, label, numIds, ids, minHeight, NULL, &ds)); if (!ds) PetscCall(DMGetDS(dm, &ds)); PetscCall(DMGetFirstLabeledPoint(dmIn, dm, label, numIds, ids, minHeight, NULL, &dsIn)); if (!dsIn) PetscCall(DMGetDS(dmIn, &dsIn)); if (!dsIn) { if (encIn == DM_ENC_SUPERMESH) { PetscInt p = pStart, pIn; PetscCall(DMGetEnclosurePoint(dmIn, dm, encIn, lStart < 0 ? p : lStart, &pIn)); // If the input mesh is higher dimensional than the output mesh, get a cell from the output mesh if (htIncIn) PetscCall(DMPlexGetSimplexOrBoxCells(plex, 0, &p, NULL)); PetscCall(DMGetEnclosurePoint(dmIn, dm, encIn, lStart < 0 ? p : lStart, &pIn)); PetscCall(DMGetCellDS(dmIn, pIn, &dsIn, NULL)); } else PetscCall(DMGetDS(dmIn, &dsIn)); } PetscCall(PetscDSGetNumFields(ds, &Nf)); PetscCall(PetscDSGetNumFields(dsIn, &NfIn)); PetscCall(PetscDSIsCohesive(dsIn, &isCohesiveIn)); if (isCohesiveIn) --htIncIn; // Should be rearranged PetscCall(DMGetNumFields(dm, &NfTot)); PetscCall(DMFindRegionNum(dm, ds, ®ionNum)); PetscCall(DMGetRegionNumDS(dm, regionNum, NULL, &fieldIS, NULL, NULL)); PetscCall(PetscDSIsCohesive(ds, &isCohesive)); PetscCall(DMGetCoordinateDim(dm, &dimEmbed)); PetscCall(DMGetLocalSection(dm, §ion)); if (dmAux) { if (encAux == DM_ENC_SUPERMESH) { PetscInt p = pStart, pAux; // If the auxiliary mesh is higher dimensional than the output mesh, get a cell from the output mesh if (htIncAux) PetscCall(DMPlexGetSimplexOrBoxCells(plex, 0, &p, NULL)); PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, lStart < 0 ? p : lStart, &pAux)); PetscCall(DMGetCellDS(dmAux, pAux, &dsAux, NULL)); } else PetscCall(DMGetDS(dmAux, &dsAux)); PetscCall(PetscDSGetNumFields(dsAux, &NfAux)); } PetscCall(PetscDSGetComponents(ds, &Nc)); PetscCall(PetscMalloc3(Nf, &isFE, Nf, &sp, NfIn, &spIn)); if (maxHeight > 0) PetscCall(PetscMalloc2(Nf, &cellsp, NfIn, &cellspIn)); else { cellsp = sp; cellspIn = spIn; } /* Get cell dual spaces */ for (f = 0; f < Nf; ++f) { PetscDiscType disctype; PetscCall(PetscDSGetDiscType_Internal(ds, f, &disctype)); if (disctype == PETSC_DISC_FE) { PetscFE fe; isFE[f] = PETSC_TRUE; hasFE = PETSC_TRUE; PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe)); PetscCall(PetscFEGetDualSpace(fe, &cellsp[f])); } else if (disctype == PETSC_DISC_FV) { PetscFV fv; isFE[f] = PETSC_FALSE; hasFV = PETSC_TRUE; PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fv)); PetscCall(PetscFVGetDualSpace(fv, &cellsp[f])); } else { isFE[f] = PETSC_FALSE; cellsp[f] = NULL; } } for (f = 0; f < NfIn; ++f) { PetscDiscType disctype; PetscCall(PetscDSGetDiscType_Internal(dsIn, f, &disctype)); if (disctype == PETSC_DISC_FE) { PetscFE fe; PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fe)); PetscCall(PetscFEGetDualSpace(fe, &cellspIn[f])); } else if (disctype == PETSC_DISC_FV) { PetscFV fv; PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fv)); PetscCall(PetscFVGetDualSpace(fv, &cellspIn[f])); } else { cellspIn[f] = NULL; } } for (f = 0; f < Nf; ++f) { if (!htInc) { sp[f] = cellsp[f]; } else PetscCall(PetscDualSpaceGetHeightSubspace(cellsp[f], htInc, &sp[f])); } if (type == DM_BC_ESSENTIAL_FIELD || type == DM_BC_ESSENTIAL_BD_FIELD || type == DM_BC_NATURAL_FIELD) { PetscFE fem, subfem; PetscDiscType disctype; const PetscReal *points; PetscInt numPoints, k; PetscCheck(maxHeight <= minHeight, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Field projection not supported for face interpolation"); PetscCall(PetscDualSpaceGetAllPointsUnion(Nf, sp, dim - htInc, funcs, &allPoints)); PetscCall(PetscQuadratureGetData(allPoints, NULL, NULL, &numPoints, &points, NULL)); PetscCall(PetscMalloc2(NfIn, &T, NfAux, &TAux)); for (f = 0; f < NfIn; ++f) { if (!htIncIn) { spIn[f] = cellspIn[f]; } else PetscCall(PetscDualSpaceGetHeightSubspace(cellspIn[f], htIncIn, &spIn[f])); PetscCall(PetscDSGetDiscType_Internal(dsIn, f, &disctype)); if (disctype != PETSC_DISC_FE) continue; PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fem)); if (!htIncIn) { subfem = fem; } else PetscCall(PetscFEGetHeightSubspace(fem, htIncIn, &subfem)); PetscCall(PetscDSGetJetDegree(dsIn, f, &k)); PetscCall(PetscFECreateTabulation(subfem, 1, numPoints, points, k, &T[f])); } for (f = 0; f < NfAux; ++f) { PetscCall(PetscDSGetDiscType_Internal(dsAux, f, &disctype)); if (disctype != PETSC_DISC_FE) continue; PetscCall(PetscDSGetDiscretization(dsAux, f, (PetscObject *)&fem)); if (!htIncAux) { subfem = fem; } else PetscCall(PetscFEGetHeightSubspace(fem, htIncAux, &subfem)); PetscCall(PetscDSGetJetDegree(dsAux, f, &k)); PetscCall(PetscFECreateTabulation(subfem, 1, numPoints, points, k, &TAux[f])); } } /* Note: We make no attempt to optimize for height. Higher height things just overwrite the lower height results. */ for (h = minHeight; h <= maxHeight; h++) { PetscInt hEff = h - minHeight + htInc; PetscInt hEffIn = h - minHeight + htIncIn; PetscInt hEffAux = h - minHeight + htIncAux; PetscDS dsEff = ds; PetscDS dsEffIn = dsIn; PetscDS dsEffAux = dsAux; PetscScalar *values; PetscBool *fieldActive; PetscInt maxDegree; PetscInt p, spDim, totDim, numValues; IS heightIS; if (h > minHeight) { for (f = 0; f < Nf; ++f) PetscCall(PetscDualSpaceGetHeightSubspace(cellsp[f], hEff, &sp[f])); } PetscCall(DMPlexGetSimplexOrBoxCells(plex, h, &pStart, &pEnd)); PetscCall(DMGetFirstLabeledPoint(dm, dm, label, numIds, ids, h, &lStart, NULL)); PetscCall(DMLabelGetStratumIS(depthLabel, depth - h, &heightIS)); if (pEnd <= pStart) { PetscCall(ISDestroy(&heightIS)); continue; } /* Compute totDim, the number of dofs in the closure of a point at this height */ totDim = 0; for (f = 0; f < Nf; ++f) { PetscBool cohesive; if (!sp[f]) continue; PetscCall(PetscDSGetCohesive(ds, f, &cohesive)); PetscCall(PetscDualSpaceGetDimension(sp[f], &spDim)); totDim += spDim; if (isCohesive && !cohesive) totDim += spDim; } p = lStart < 0 ? pStart : lStart; PetscCall(DMPlexVecGetClosure(plex, section, localX, p, &numValues, NULL)); PetscCheck(numValues == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The output section point (%" PetscInt_FMT ") closure size %" PetscInt_FMT " != dual space dimension %" PetscInt_FMT " at height %" PetscInt_FMT " in [%" PetscInt_FMT ", %" PetscInt_FMT "]", p, numValues, totDim, h, minHeight, maxHeight); if (!totDim) { PetscCall(ISDestroy(&heightIS)); continue; } if (htInc) PetscCall(PetscDSGetHeightSubspace(ds, hEff, &dsEff)); /* Compute totDimIn, the number of dofs in the closure of a point at this height */ if (localU) { PetscInt totDimIn, pIn, numValuesIn; totDimIn = 0; for (f = 0; f < NfIn; ++f) { PetscBool cohesive; if (!spIn[f]) continue; PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive)); PetscCall(PetscDualSpaceGetDimension(spIn[f], &spDim)); totDimIn += spDim; if (isCohesiveIn && !cohesive) totDimIn += spDim; } PetscCall(DMGetEnclosurePoint(dmIn, dm, encIn, lStart < 0 ? pStart : lStart, &pIn)); PetscCall(DMPlexVecGetClosure(plexIn, NULL, localU, pIn, &numValuesIn, NULL)); // TODO We could check that pIn is a cohesive cell for this check PetscCheck(isCohesiveIn || (numValuesIn == totDimIn), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The input section point (%" PetscInt_FMT ") closure size %" PetscInt_FMT " != dual space dimension %" PetscInt_FMT " at height %" PetscInt_FMT, pIn, numValuesIn, totDimIn, htIncIn); if (htIncIn) PetscCall(PetscDSGetHeightSubspace(dsIn, hEffIn, &dsEffIn)); } if (htIncAux) PetscCall(PetscDSGetHeightSubspace(dsAux, hEffAux, &dsEffAux)); /* Loop over points at this height */ PetscCall(DMGetWorkArray(dm, numValues, MPIU_SCALAR, &values)); PetscCall(DMGetWorkArray(dm, NfTot, MPI_INT, &fieldActive)); { const PetscInt *fields; PetscCall(ISGetIndices(fieldIS, &fields)); for (f = 0; f < NfTot; ++f) fieldActive[f] = PETSC_FALSE; for (f = 0; f < Nf; ++f) fieldActive[fields[f]] = (funcs[f] && sp[f]) ? PETSC_TRUE : PETSC_FALSE; PetscCall(ISRestoreIndices(fieldIS, &fields)); } if (label) { PetscInt i; for (i = 0; i < numIds; ++i) { IS pointIS, isectIS; const PetscInt *points; PetscInt n; PetscFEGeom *fegeom = NULL, *chunkgeom = NULL; PetscQuadrature quad = NULL; PetscCall(DMLabelGetStratumIS(label, ids[i], &pointIS)); if (!pointIS) continue; /* No points with that id on this process */ PetscCall(ISIntersect(pointIS, heightIS, &isectIS)); PetscCall(ISDestroy(&pointIS)); if (!isectIS) continue; PetscCall(ISGetLocalSize(isectIS, &n)); PetscCall(ISGetIndices(isectIS, &points)); PetscCall(DMFieldGetDegree(coordField, isectIS, NULL, &maxDegree)); if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, isectIS, &quad)); if (!quad) { if (!h && allPoints) { quad = allPoints; allPoints = NULL; } else { PetscCall(PetscDualSpaceGetAllPointsUnion(Nf, sp, isCohesive ? dim - htInc - 1 : dim - htInc, funcs, &quad)); } } PetscFEGeomMode geommode = htInc && h == minHeight ? PETSC_FEGEOM_BOUNDARY : PETSC_FEGEOM_BASIC; if (n) { PetscInt depth, dep; PetscCall(DMPlexGetDepth(dm, &depth)); PetscCall(DMPlexGetPointDepth(dm, points[0], &dep)); if (dep < depth && h == minHeight) geommode = PETSC_FEGEOM_BOUNDARY; } PetscCall(DMFieldCreateFEGeom(coordField, isectIS, quad, geommode, &fegeom)); for (p = 0; p < n; ++p) { const PetscInt point = points[p]; PetscCall(PetscArrayzero(values, numValues)); PetscCall(PetscFEGeomGetChunk(fegeom, p, p + 1, &chunkgeom)); PetscCall(DMPlexSetActivePoint(dm, point)); PetscCall(DMProjectPoint_Private(dm, dsEff, plexIn, encIn, dsEffIn, plexAux, encAux, dsEffAux, chunkgeom, htInc, time, localU, localA, hasFE, hasFV, isFE, sp, point, T, TAux, type, funcs, ctxs, fieldActive, values)); if (transform) PetscCall(DMPlexBasisTransformPoint_Internal(plex, tdm, tv, point, fieldActive, PETSC_FALSE, values)); PetscCall(DMPlexVecSetFieldClosure_Internal(plex, section, localX, fieldActive, point, Ncc, comps, label, ids[i], values, mode)); } PetscCall(PetscFEGeomRestoreChunk(fegeom, p, p + 1, &chunkgeom)); PetscCall(PetscFEGeomDestroy(&fegeom)); PetscCall(PetscQuadratureDestroy(&quad)); PetscCall(ISRestoreIndices(isectIS, &points)); PetscCall(ISDestroy(&isectIS)); } } else { PetscFEGeom *fegeom = NULL, *chunkgeom = NULL; PetscQuadrature quad = NULL; IS pointIS; PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &pointIS)); PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree)); if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &quad)); if (!quad) { if (!h && allPoints) { quad = allPoints; allPoints = NULL; } else { PetscCall(PetscDualSpaceGetAllPointsUnion(Nf, sp, dim - htInc, funcs, &quad)); } } PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, (htInc && h == minHeight) ? PETSC_FEGEOM_BOUNDARY : PETSC_FEGEOM_BASIC, &fegeom)); for (p = pStart; p < pEnd; ++p) { PetscCall(PetscArrayzero(values, numValues)); PetscCall(PetscFEGeomGetChunk(fegeom, p - pStart, p - pStart + 1, &chunkgeom)); PetscCall(DMPlexSetActivePoint(dm, p)); PetscCall(DMProjectPoint_Private(dm, dsEff, plexIn, encIn, dsEffIn, plexAux, encAux, dsEffAux, chunkgeom, htInc, time, localU, localA, hasFE, hasFV, isFE, sp, p, T, TAux, type, funcs, ctxs, fieldActive, values)); if (transform) PetscCall(DMPlexBasisTransformPoint_Internal(plex, tdm, tv, p, fieldActive, PETSC_FALSE, values)); PetscCall(DMPlexVecSetFieldClosure_Internal(plex, section, localX, fieldActive, p, Ncc, comps, NULL, -1, values, mode)); } PetscCall(PetscFEGeomRestoreChunk(fegeom, p - pStart, pStart - p + 1, &chunkgeom)); PetscCall(PetscFEGeomDestroy(&fegeom)); PetscCall(PetscQuadratureDestroy(&quad)); PetscCall(ISDestroy(&pointIS)); } PetscCall(ISDestroy(&heightIS)); PetscCall(DMRestoreWorkArray(dm, numValues, MPIU_SCALAR, &values)); PetscCall(DMRestoreWorkArray(dm, Nf, MPI_INT, &fieldActive)); } /* Cleanup */ if (type == DM_BC_ESSENTIAL_FIELD || type == DM_BC_ESSENTIAL_BD_FIELD || type == DM_BC_NATURAL_FIELD) { for (f = 0; f < NfIn; ++f) PetscCall(PetscTabulationDestroy(&T[f])); for (f = 0; f < NfAux; ++f) PetscCall(PetscTabulationDestroy(&TAux[f])); PetscCall(PetscFree2(T, TAux)); } PetscCall(PetscQuadratureDestroy(&allPoints)); PetscCall(PetscFree3(isFE, sp, spIn)); if (maxHeight > 0) PetscCall(PetscFree2(cellsp, cellspIn)); PetscCall(DMDestroy(&plex)); PetscCall(DMDestroy(&plexIn)); if (dmAux) PetscCall(DMDestroy(&plexAux)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode DMProjectFunctionLocal_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, InsertMode mode, Vec localX) { PetscFunctionBegin; PetscCall(DMProjectLocal_Generic_Plex(dm, time, NULL, 0, NULL, NULL, 0, NULL, DM_BC_ESSENTIAL, (void (**)(void))funcs, ctxs, mode, localX)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode DMProjectFunctionLabelLocal_Plex(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Ncc, const PetscInt comps[], PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, InsertMode mode, Vec localX) { PetscFunctionBegin; PetscCall(DMProjectLocal_Generic_Plex(dm, time, NULL, Ncc, comps, label, numIds, ids, DM_BC_ESSENTIAL, (void (**)(void))funcs, ctxs, mode, localX)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode DMProjectFieldLocal_Plex(DM dm, PetscReal time, Vec localU, 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[]), InsertMode mode, Vec localX) { PetscFunctionBegin; PetscCall(DMProjectLocal_Generic_Plex(dm, time, localU, 0, NULL, NULL, 0, NULL, DM_BC_ESSENTIAL_FIELD, (void (**)(void))funcs, NULL, mode, localX)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode DMProjectFieldLabelLocal_Plex(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Ncc, const PetscInt comps[], Vec localU, 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[]), InsertMode mode, Vec localX) { PetscFunctionBegin; PetscCall(DMProjectLocal_Generic_Plex(dm, time, localU, Ncc, comps, label, numIds, ids, DM_BC_ESSENTIAL_FIELD, (void (**)(void))funcs, NULL, mode, localX)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode DMProjectBdFieldLabelLocal_Plex(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Ncc, const PetscInt comps[], Vec localU, 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[]), InsertMode mode, Vec localX) { PetscFunctionBegin; PetscCall(DMProjectLocal_Generic_Plex(dm, time, localU, Ncc, comps, label, numIds, ids, DM_BC_ESSENTIAL_BD_FIELD, (void (**)(void))funcs, NULL, mode, localX)); PetscFunctionReturn(PETSC_SUCCESS); }