Actual source code: dmmbfem.cxx

  1: #include <petscconf.h>
  2: #include <petscdt.h>
  3: #include <petsc/private/dmmbimpl.h>

  5: /* Utility functions */
  6: static inline PetscReal DMatrix_Determinant_2x2_Internal(const PetscReal inmat[2 * 2])
  7: {
  8:   return inmat[0] * inmat[3] - inmat[1] * inmat[2];
  9: }

 11: static inline PetscErrorCode DMatrix_Invert_2x2_Internal(const PetscReal *inmat, PetscReal *outmat, PetscReal *determinant)
 12: {
 13:   PetscReal det = DMatrix_Determinant_2x2_Internal(inmat);
 14:   if (outmat) {
 15:     outmat[0] = inmat[3] / det;
 16:     outmat[1] = -inmat[1] / det;
 17:     outmat[2] = -inmat[2] / det;
 18:     outmat[3] = inmat[0] / det;
 19:   }
 20:   if (determinant) *determinant = det;
 21:   return PETSC_SUCCESS;
 22: }

 24: static inline PetscReal DMatrix_Determinant_3x3_Internal(const PetscReal inmat[3 * 3])
 25: {
 26:   return inmat[0] * (inmat[8] * inmat[4] - inmat[7] * inmat[5]) - inmat[3] * (inmat[8] * inmat[1] - inmat[7] * inmat[2]) + inmat[6] * (inmat[5] * inmat[1] - inmat[4] * inmat[2]);
 27: }

 29: static inline PetscErrorCode DMatrix_Invert_3x3_Internal(const PetscReal *inmat, PetscReal *outmat, PetscReal *determinant)
 30: {
 31:   PetscReal det = DMatrix_Determinant_3x3_Internal(inmat);
 32:   if (outmat) {
 33:     outmat[0] = (inmat[8] * inmat[4] - inmat[7] * inmat[5]) / det;
 34:     outmat[1] = -(inmat[8] * inmat[1] - inmat[7] * inmat[2]) / det;
 35:     outmat[2] = (inmat[5] * inmat[1] - inmat[4] * inmat[2]) / det;
 36:     outmat[3] = -(inmat[8] * inmat[3] - inmat[6] * inmat[5]) / det;
 37:     outmat[4] = (inmat[8] * inmat[0] - inmat[6] * inmat[2]) / det;
 38:     outmat[5] = -(inmat[5] * inmat[0] - inmat[3] * inmat[2]) / det;
 39:     outmat[6] = (inmat[7] * inmat[3] - inmat[6] * inmat[4]) / det;
 40:     outmat[7] = -(inmat[7] * inmat[0] - inmat[6] * inmat[1]) / det;
 41:     outmat[8] = (inmat[4] * inmat[0] - inmat[3] * inmat[1]) / det;
 42:   }
 43:   if (determinant) *determinant = det;
 44:   return PETSC_SUCCESS;
 45: }

 47: static inline PetscReal DMatrix_Determinant_4x4_Internal(PetscReal inmat[4 * 4])
 48: {
 49:   return inmat[0 + 0 * 4] * (inmat[1 + 1 * 4] * (inmat[2 + 2 * 4] * inmat[3 + 3 * 4] - inmat[2 + 3 * 4] * inmat[3 + 2 * 4]) - inmat[1 + 2 * 4] * (inmat[2 + 1 * 4] * inmat[3 + 3 * 4] - inmat[2 + 3 * 4] * inmat[3 + 1 * 4]) + inmat[1 + 3 * 4] * (inmat[2 + 1 * 4] * inmat[3 + 2 * 4] - inmat[2 + 2 * 4] * inmat[3 + 1 * 4])) - inmat[0 + 1 * 4] * (inmat[1 + 0 * 4] * (inmat[2 + 2 * 4] * inmat[3 + 3 * 4] - inmat[2 + 3 * 4] * inmat[3 + 2 * 4]) - inmat[1 + 2 * 4] * (inmat[2 + 0 * 4] * inmat[3 + 3 * 4] - inmat[2 + 3 * 4] * inmat[3 + 0 * 4]) + inmat[1 + 3 * 4] * (inmat[2 + 0 * 4] * inmat[3 + 2 * 4] - inmat[2 + 2 * 4] * inmat[3 + 0 * 4])) + inmat[0 + 2 * 4] * (inmat[1 + 0 * 4] * (inmat[2 + 1 * 4] * inmat[3 + 3 * 4] - inmat[2 + 3 * 4] * inmat[3 + 1 * 4]) - inmat[1 + 1 * 4] * (inmat[2 + 0 * 4] * inmat[3 + 3 * 4] - inmat[2 + 3 * 4] * inmat[3 + 0 * 4]) + inmat[1 + 3 * 4] * (inmat[2 + 0 * 4] * inmat[3 + 1 * 4] - inmat[2 + 1 * 4] * inmat[3 + 0 * 4])) - inmat[0 + 3 * 4] * (inmat[1 + 0 * 4] * (inmat[2 + 1 * 4] * inmat[3 + 2 * 4] - inmat[2 + 2 * 4] * inmat[3 + 1 * 4]) - inmat[1 + 1 * 4] * (inmat[2 + 0 * 4] * inmat[3 + 2 * 4] - inmat[2 + 2 * 4] * inmat[3 + 0 * 4]) + inmat[1 + 2 * 4] * (inmat[2 + 0 * 4] * inmat[3 + 1 * 4] - inmat[2 + 1 * 4] * inmat[3 + 0 * 4]));
 50: }

 52: static inline PetscErrorCode DMatrix_Invert_4x4_Internal(PetscReal *inmat, PetscReal *outmat, PetscScalar *determinant)
 53: {
 54:   PetscReal det = DMatrix_Determinant_4x4_Internal(inmat);
 55:   if (outmat) {
 56:     outmat[0]  = (inmat[5] * inmat[10] * inmat[15] + inmat[6] * inmat[11] * inmat[13] + inmat[7] * inmat[9] * inmat[14] - inmat[5] * inmat[11] * inmat[14] - inmat[6] * inmat[9] * inmat[15] - inmat[7] * inmat[10] * inmat[13]) / det;
 57:     outmat[1]  = (inmat[1] * inmat[11] * inmat[14] + inmat[2] * inmat[9] * inmat[15] + inmat[3] * inmat[10] * inmat[13] - inmat[1] * inmat[10] * inmat[15] - inmat[2] * inmat[11] * inmat[13] - inmat[3] * inmat[9] * inmat[14]) / det;
 58:     outmat[2]  = (inmat[1] * inmat[6] * inmat[15] + inmat[2] * inmat[7] * inmat[13] + inmat[3] * inmat[5] * inmat[14] - inmat[1] * inmat[7] * inmat[14] - inmat[2] * inmat[5] * inmat[15] - inmat[3] * inmat[6] * inmat[13]) / det;
 59:     outmat[3]  = (inmat[1] * inmat[7] * inmat[10] + inmat[2] * inmat[5] * inmat[11] + inmat[3] * inmat[6] * inmat[9] - inmat[1] * inmat[6] * inmat[11] - inmat[2] * inmat[7] * inmat[9] - inmat[3] * inmat[5] * inmat[10]) / det;
 60:     outmat[4]  = (inmat[4] * inmat[11] * inmat[14] + inmat[6] * inmat[8] * inmat[15] + inmat[7] * inmat[10] * inmat[12] - inmat[4] * inmat[10] * inmat[15] - inmat[6] * inmat[11] * inmat[12] - inmat[7] * inmat[8] * inmat[14]) / det;
 61:     outmat[5]  = (inmat[0] * inmat[10] * inmat[15] + inmat[2] * inmat[11] * inmat[12] + inmat[3] * inmat[8] * inmat[14] - inmat[0] * inmat[11] * inmat[14] - inmat[2] * inmat[8] * inmat[15] - inmat[3] * inmat[10] * inmat[12]) / det;
 62:     outmat[6]  = (inmat[0] * inmat[7] * inmat[14] + inmat[2] * inmat[4] * inmat[15] + inmat[3] * inmat[6] * inmat[12] - inmat[0] * inmat[6] * inmat[15] - inmat[2] * inmat[7] * inmat[12] - inmat[3] * inmat[4] * inmat[14]) / det;
 63:     outmat[7]  = (inmat[0] * inmat[6] * inmat[11] + inmat[2] * inmat[7] * inmat[8] + inmat[3] * inmat[4] * inmat[10] - inmat[0] * inmat[7] * inmat[10] - inmat[2] * inmat[4] * inmat[11] - inmat[3] * inmat[6] * inmat[8]) / det;
 64:     outmat[8]  = (inmat[4] * inmat[9] * inmat[15] + inmat[5] * inmat[11] * inmat[12] + inmat[7] * inmat[8] * inmat[13] - inmat[4] * inmat[11] * inmat[13] - inmat[5] * inmat[8] * inmat[15] - inmat[7] * inmat[9] * inmat[12]) / det;
 65:     outmat[9]  = (inmat[0] * inmat[11] * inmat[13] + inmat[1] * inmat[8] * inmat[15] + inmat[3] * inmat[9] * inmat[12] - inmat[0] * inmat[9] * inmat[15] - inmat[1] * inmat[11] * inmat[12] - inmat[3] * inmat[8] * inmat[13]) / det;
 66:     outmat[10] = (inmat[0] * inmat[5] * inmat[15] + inmat[1] * inmat[7] * inmat[12] + inmat[3] * inmat[4] * inmat[13] - inmat[0] * inmat[7] * inmat[13] - inmat[1] * inmat[4] * inmat[15] - inmat[3] * inmat[5] * inmat[12]) / det;
 67:     outmat[11] = (inmat[0] * inmat[7] * inmat[9] + inmat[1] * inmat[4] * inmat[11] + inmat[3] * inmat[5] * inmat[8] - inmat[0] * inmat[5] * inmat[11] - inmat[1] * inmat[7] * inmat[8] - inmat[3] * inmat[4] * inmat[9]) / det;
 68:     outmat[12] = (inmat[4] * inmat[10] * inmat[13] + inmat[5] * inmat[8] * inmat[14] + inmat[6] * inmat[9] * inmat[12] - inmat[4] * inmat[9] * inmat[14] - inmat[5] * inmat[10] * inmat[12] - inmat[6] * inmat[8] * inmat[13]) / det;
 69:     outmat[13] = (inmat[0] * inmat[9] * inmat[14] + inmat[1] * inmat[10] * inmat[12] + inmat[2] * inmat[8] * inmat[13] - inmat[0] * inmat[10] * inmat[13] - inmat[1] * inmat[8] * inmat[14] - inmat[2] * inmat[9] * inmat[12]) / det;
 70:     outmat[14] = (inmat[0] * inmat[6] * inmat[13] + inmat[1] * inmat[4] * inmat[14] + inmat[2] * inmat[5] * inmat[12] - inmat[0] * inmat[5] * inmat[14] - inmat[1] * inmat[6] * inmat[12] - inmat[2] * inmat[4] * inmat[13]) / det;
 71:     outmat[15] = (inmat[0] * inmat[5] * inmat[10] + inmat[1] * inmat[6] * inmat[8] + inmat[2] * inmat[4] * inmat[9] - inmat[0] * inmat[6] * inmat[9] - inmat[1] * inmat[4] * inmat[10] - inmat[2] * inmat[5] * inmat[8]) / det;
 72:   }
 73:   if (determinant) *determinant = det;
 74:   return PETSC_SUCCESS;
 75: }

 77: /*
 78:   Compute_Lagrange_Basis_1D_Internal - Evaluate bases and derivatives at quadrature points for a EDGE2 or EDGE3 element.

 80:   The routine is given the coordinates of the vertices of a linear or quadratic edge element.

 82:   The routine evaluates the basis functions associated with each quadrature point provided,
 83:   and their derivatives with respect to X.

 85:   Notes:

 87:   Example Physical Element
 88: .vb
 89:     1-------2        1----3----2
 90:       EDGE2             EDGE3
 91: .ve

 93:   Input Parameters:
 94: +  PetscInt  nverts -          the number of element vertices
 95: .  PetscReal coords[3*nverts] - the physical coordinates of the vertices (in canonical numbering)
 96: .  PetscInt  npts -            the number of evaluation points (quadrature points)
 97: -  PetscReal quad[3*npts] -    the evaluation points (quadrature points) in the reference space

 99:   Output Parameters:
100: +  PetscReal phypts[3*npts] -  the evaluation points (quadrature points) transformed to the physical space
101: .  PetscReal jxw[npts] -       the jacobian determinant * quadrature weight necessary for assembling discrete contributions
102: .  PetscReal phi[npts] -       the bases evaluated at the specified quadrature points
103: .  PetscReal dphidx[npts] -    the derivative of the bases wrt X-direction evaluated at the specified quadrature points
104: . jacobian  - jacobian
105: . ijacobian - ijacobian
106: - volume    - volume

108:   Level: advanced
109: */
110: static PetscErrorCode Compute_Lagrange_Basis_1D_Internal(const PetscInt nverts, const PetscReal *coords, const PetscInt npts, const PetscReal *quad, PetscReal *phypts, PetscReal *jxw, PetscReal *phi, PetscReal *dphidx, PetscReal *jacobian, PetscReal *ijacobian, PetscReal *volume)
111: {
112:   int i, j;

114:   PetscFunctionBegin;
115:   PetscAssertPointer(jacobian, 9);
116:   PetscAssertPointer(ijacobian, 10);
117:   PetscAssertPointer(volume, 11);
118:   if (phypts) PetscCall(PetscArrayzero(phypts, npts * 3));
119:   if (dphidx) { /* Reset arrays. */
120:     PetscCall(PetscArrayzero(dphidx, npts * nverts));
121:   }
122:   if (nverts == 2) { /* Linear Edge */

124:     for (j = 0; j < npts; j++) {
125:       const PetscInt  offset = j * nverts;
126:       const PetscReal r      = quad[j];

128:       phi[0 + offset] = (1.0 - r);
129:       phi[1 + offset] = (r);

131:       const PetscReal dNi_dxi[2] = {-1.0, 1.0};

133:       jacobian[0] = ijacobian[0] = volume[0] = 0.0;
134:       for (i = 0; i < nverts; ++i) {
135:         const PetscReal *vertices = coords + i * 3;
136:         jacobian[0] += dNi_dxi[i] * vertices[0];
137:         if (phypts) phypts[3 * j + 0] += phi[i + offset] * vertices[0];
138:       }

140:       /* invert the jacobian */
141:       *volume      = jacobian[0];
142:       ijacobian[0] = 1.0 / jacobian[0];
143:       jxw[j] *= *volume;

145:       /*  Divide by element jacobian. */
146:       for (i = 0; i < nverts; i++) {
147:         if (dphidx) dphidx[i + offset] += dNi_dxi[i] * ijacobian[0];
148:       }
149:     }
150:   } else if (nverts == 3) { /* Quadratic Edge */

152:     for (j = 0; j < npts; j++) {
153:       const PetscInt  offset = j * nverts;
154:       const PetscReal r      = quad[j];

156:       phi[0 + offset] = 1.0 + r * (2.0 * r - 3.0);
157:       phi[1 + offset] = 4.0 * r * (1.0 - r);
158:       phi[2 + offset] = r * (2.0 * r - 1.0);

160:       const PetscReal dNi_dxi[3] = {4 * r - 3.0, 4 * (1.0 - 2.0 * r), 4.0 * r - 1.0};

162:       jacobian[0] = ijacobian[0] = volume[0] = 0.0;
163:       for (i = 0; i < nverts; ++i) {
164:         const PetscReal *vertices = coords + i * 3;
165:         jacobian[0] += dNi_dxi[i] * vertices[0];
166:         if (phypts) phypts[3 * j + 0] += phi[i + offset] * vertices[0];
167:       }

169:       /* invert the jacobian */
170:       *volume      = jacobian[0];
171:       ijacobian[0] = 1.0 / jacobian[0];
172:       if (jxw) jxw[j] *= *volume;

174:       /*  Divide by element jacobian. */
175:       for (i = 0; i < nverts; i++) {
176:         if (dphidx) dphidx[i + offset] += dNi_dxi[i] * ijacobian[0];
177:       }
178:     }
179:   } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "The number of entity vertices are invalid. Currently only support EDGE2 and EDGE3 basis evaluations in 1-D : %" PetscInt_FMT, nverts);
180:   PetscFunctionReturn(PETSC_SUCCESS);
181: }

183: /*
184:   Compute_Lagrange_Basis_2D_Internal - Evaluate bases and derivatives at quadrature points for a QUAD4 or TRI3 element.

186:   The routine is given the coordinates of the vertices of a quadrangle or triangle.

188:   The routine evaluates the basis functions associated with each quadrature point provided,
189:   and their derivatives with respect to X and Y.

191:   Notes:

193:   Example Physical Element (QUAD4)
194: .vb
195:     4------3        s
196:     |      |        |
197:     |      |        |
198:     |      |        |
199:     1------2        0-------r
200: .ve

202:   Input Parameters:
203: +  PetscInt  nverts -          the number of element vertices
204: .  PetscReal coords[3*nverts] - the physical coordinates of the vertices (in canonical numbering)
205: .  PetscInt  npts -            the number of evaluation points (quadrature points)
206: -  PetscReal quad[3*npts] -    the evaluation points (quadrature points) in the reference space

208:   Output Parameters:
209: +  PetscReal phypts[3*npts] -  the evaluation points (quadrature points) transformed to the physical space
210: .  PetscReal jxw[npts] -       the jacobian determinant * quadrature weight necessary for assembling discrete contributions
211: .  PetscReal phi[npts] -       the bases evaluated at the specified quadrature points
212: .  PetscReal dphidx[npts] -    the derivative of the bases wrt X-direction evaluated at the specified quadrature points
213: .  PetscReal dphidy[npts] -    the derivative of the bases wrt Y-direction evaluated at the specified quadrature points
214: . jacobian  - jacobian
215: . ijacobian - ijacobian
216: - volume    - volume

218:   Level: advanced
219: */
220: static PetscErrorCode Compute_Lagrange_Basis_2D_Internal(const PetscInt nverts, const PetscReal *coords, const PetscInt npts, const PetscReal *quad, PetscReal *phypts, PetscReal *jxw, PetscReal *phi, PetscReal *dphidx, PetscReal *dphidy, PetscReal *jacobian, PetscReal *ijacobian, PetscReal *volume)
221: {
222:   PetscInt i, j, k;

224:   PetscFunctionBegin;
225:   PetscAssertPointer(jacobian, 10);
226:   PetscAssertPointer(ijacobian, 11);
227:   PetscAssertPointer(volume, 12);
228:   PetscCall(PetscArrayzero(phi, npts));
229:   if (phypts) PetscCall(PetscArrayzero(phypts, npts * 3));
230:   if (dphidx) { /* Reset arrays. */
231:     PetscCall(PetscArrayzero(dphidx, npts * nverts));
232:     PetscCall(PetscArrayzero(dphidy, npts * nverts));
233:   }
234:   if (nverts == 4) { /* Linear Quadrangle */

236:     for (j = 0; j < npts; j++) {
237:       const PetscInt  offset = j * nverts;
238:       const PetscReal r      = quad[0 + j * 2];
239:       const PetscReal s      = quad[1 + j * 2];

241:       phi[0 + offset] = (1.0 - r) * (1.0 - s);
242:       phi[1 + offset] = r * (1.0 - s);
243:       phi[2 + offset] = r * s;
244:       phi[3 + offset] = (1.0 - r) * s;

246:       const PetscReal dNi_dxi[4]  = {-1.0 + s, 1.0 - s, s, -s};
247:       const PetscReal dNi_deta[4] = {-1.0 + r, -r, r, 1.0 - r};

249:       PetscCall(PetscArrayzero(jacobian, 4));
250:       PetscCall(PetscArrayzero(ijacobian, 4));
251:       for (i = 0; i < nverts; ++i) {
252:         const PetscReal *vertices = coords + i * 3;
253:         jacobian[0] += dNi_dxi[i] * vertices[0];
254:         jacobian[2] += dNi_dxi[i] * vertices[1];
255:         jacobian[1] += dNi_deta[i] * vertices[0];
256:         jacobian[3] += dNi_deta[i] * vertices[1];
257:         if (phypts) {
258:           phypts[3 * j + 0] += phi[i + offset] * vertices[0];
259:           phypts[3 * j + 1] += phi[i + offset] * vertices[1];
260:           phypts[3 * j + 2] += phi[i + offset] * vertices[2];
261:         }
262:       }

264:       /* invert the jacobian */
265:       PetscCall(DMatrix_Invert_2x2_Internal(jacobian, ijacobian, volume));
266:       PetscCheck(*volume >= 1e-12, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Quadrangular element has zero volume: %g. Degenerate element or invalid connectivity", *volume);

268:       if (jxw) jxw[j] *= *volume;

270:       /*  Let us compute the bases derivatives by scaling with inverse jacobian. */
271:       if (dphidx) {
272:         for (i = 0; i < nverts; i++) {
273:           for (k = 0; k < 2; ++k) {
274:             if (dphidx) dphidx[i + offset] += dNi_dxi[i] * ijacobian[k * 2 + 0];
275:             if (dphidy) dphidy[i + offset] += dNi_deta[i] * ijacobian[k * 2 + 1];
276:           } /* for k=[0..2) */
277:         } /* for i=[0..nverts) */
278:       } /* if (dphidx) */
279:     }
280:   } else if (nverts == 3) { /* Linear triangle */
281:     const PetscReal x2 = coords[2 * 3 + 0], y2 = coords[2 * 3 + 1];

283:     PetscCall(PetscArrayzero(jacobian, 4));
284:     PetscCall(PetscArrayzero(ijacobian, 4));

286:     /* Jacobian is constant */
287:     jacobian[0] = (coords[0 * 3 + 0] - x2);
288:     jacobian[1] = (coords[1 * 3 + 0] - x2);
289:     jacobian[2] = (coords[0 * 3 + 1] - y2);
290:     jacobian[3] = (coords[1 * 3 + 1] - y2);

292:     /* invert the jacobian */
293:     PetscCall(DMatrix_Invert_2x2_Internal(jacobian, ijacobian, volume));
294:     PetscCheck(*volume >= 1e-12, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Triangular element has zero volume: %g. Degenerate element or invalid connectivity", (double)*volume);

296:     const PetscReal Dx[3] = {ijacobian[0], ijacobian[2], -ijacobian[0] - ijacobian[2]};
297:     const PetscReal Dy[3] = {ijacobian[1], ijacobian[3], -ijacobian[1] - ijacobian[3]};

299:     for (j = 0; j < npts; j++) {
300:       const PetscInt  offset = j * nverts;
301:       const PetscReal r      = quad[0 + j * 2];
302:       const PetscReal s      = quad[1 + j * 2];

304:       if (jxw) jxw[j] *= 0.5;

306:       /* const PetscReal Ni[3]  = { r, s, 1.0 - r - s }; */
307:       const PetscReal phipts_x = coords[2 * 3 + 0] + jacobian[0] * r + jacobian[1] * s;
308:       const PetscReal phipts_y = coords[2 * 3 + 1] + jacobian[2] * r + jacobian[3] * s;
309:       if (phypts) {
310:         phypts[offset + 0] = phipts_x;
311:         phypts[offset + 1] = phipts_y;
312:       }

314:       /* \phi_0 = (b.y - c.y) x + (b.x - c.x) y + c.x b.y - b.x c.y */
315:       phi[0 + offset] = (ijacobian[0] * (phipts_x - x2) + ijacobian[1] * (phipts_y - y2));
316:       /* \phi_1 = (c.y - a.y) x + (a.x - c.x) y + c.x a.y - a.x c.y */
317:       phi[1 + offset] = (ijacobian[2] * (phipts_x - x2) + ijacobian[3] * (phipts_y - y2));
318:       phi[2 + offset] = 1.0 - phi[0 + offset] - phi[1 + offset];

320:       if (dphidx) {
321:         dphidx[0 + offset] = Dx[0];
322:         dphidx[1 + offset] = Dx[1];
323:         dphidx[2 + offset] = Dx[2];
324:       }

326:       if (dphidy) {
327:         dphidy[0 + offset] = Dy[0];
328:         dphidy[1 + offset] = Dy[1];
329:         dphidy[2 + offset] = Dy[2];
330:       }
331:     }
332:   } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "The number of entity vertices are invalid. Currently only support QUAD4 and TRI3 basis evaluations in 2-D : %" PetscInt_FMT, nverts);
333:   PetscFunctionReturn(PETSC_SUCCESS);
334: }

336: /*
337:   Compute_Lagrange_Basis_3D_Internal - Evaluate bases and derivatives at quadrature points for a HEX8 or TET4 element.

339:   The routine is given the coordinates of the vertices of a hexahedra or tetrahedra.

341:   The routine evaluates the basis functions associated with each quadrature point provided,
342:   and their derivatives with respect to X, Y and Z.

344:   Notes:

346:   Example Physical Element (HEX8)
347: .vb
348:       8------7
349:      /|     /|        t  s
350:     5------6 |        | /
351:     | |    | |        |/
352:     | 4----|-3        0-------r
353:     |/     |/
354:     1------2
355: .ve

357:   Input Parameters:
358: +  PetscInt  nverts -          the number of element vertices
359: .  PetscReal coords[3*nverts] - the physical coordinates of the vertices (in canonical numbering)
360: .  PetscInt  npts -            the number of evaluation points (quadrature points)
361: -  PetscReal quad[3*npts] -    the evaluation points (quadrature points) in the reference space

363:   Output Parameters:
364: +  PetscReal phypts[3*npts] -  the evaluation points (quadrature points) transformed to the physical space
365: .  PetscReal jxw[npts] -       the jacobian determinant * quadrature weight necessary for assembling discrete contributions
366: .  PetscReal phi[npts] -       the bases evaluated at the specified quadrature points
367: .  PetscReal dphidx[npts] -    the derivative of the bases wrt X-direction evaluated at the specified quadrature points
368: .  PetscReal dphidy[npts] -    the derivative of the bases wrt Y-direction evaluated at the specified quadrature points
369: .  PetscReal dphidz[npts] -    the derivative of the bases wrt Z-direction evaluated at the specified quadrature points
370: . jacobian  - jacobian
371: . ijacobian - ijacobian
372: - volume    - volume

374:   Level: advanced
375: */
376: static PetscErrorCode Compute_Lagrange_Basis_3D_Internal(const PetscInt nverts, const PetscReal *coords, const PetscInt npts, const PetscReal *quad, PetscReal *phypts, PetscReal *jxw, PetscReal *phi, PetscReal *dphidx, PetscReal *dphidy, PetscReal *dphidz, PetscReal *jacobian, PetscReal *ijacobian, PetscReal *volume)
377: {
378:   PetscInt i, j, k;

380:   PetscFunctionBegin;
381:   PetscAssertPointer(jacobian, 11);
382:   PetscAssertPointer(ijacobian, 12);
383:   PetscAssertPointer(volume, 13);

385:   PetscCall(PetscArrayzero(phi, npts));
386:   if (phypts) PetscCall(PetscArrayzero(phypts, npts * 3));
387:   if (dphidx) {
388:     PetscCall(PetscArrayzero(dphidx, npts * nverts));
389:     PetscCall(PetscArrayzero(dphidy, npts * nverts));
390:     PetscCall(PetscArrayzero(dphidz, npts * nverts));
391:   }

393:   if (nverts == 8) { /* Linear Hexahedra */

395:     for (j = 0; j < npts; j++) {
396:       const PetscInt   offset = j * nverts;
397:       const PetscReal &r      = quad[j * 3 + 0];
398:       const PetscReal &s      = quad[j * 3 + 1];
399:       const PetscReal &t      = quad[j * 3 + 2];

401:       phi[offset + 0] = (1.0 - r) * (1.0 - s) * (1.0 - t); /* 0,0,0 */
402:       phi[offset + 1] = (r) * (1.0 - s) * (1.0 - t);       /* 1,0,0 */
403:       phi[offset + 2] = (r) * (s) * (1.0 - t);             /* 1,1,0 */
404:       phi[offset + 3] = (1.0 - r) * (s) * (1.0 - t);       /* 0,1,0 */
405:       phi[offset + 4] = (1.0 - r) * (1.0 - s) * (t);       /* 0,0,1 */
406:       phi[offset + 5] = (r) * (1.0 - s) * (t);             /* 1,0,1 */
407:       phi[offset + 6] = (r) * (s) * (t);                   /* 1,1,1 */
408:       phi[offset + 7] = (1.0 - r) * (s) * (t);             /* 0,1,1 */

410:       const PetscReal dNi_dxi[8] = {-(1.0 - s) * (1.0 - t), (1.0 - s) * (1.0 - t), (s) * (1.0 - t), -(s) * (1.0 - t), -(1.0 - s) * (t), (1.0 - s) * (t), (s) * (t), -(s) * (t)};

412:       const PetscReal dNi_deta[8] = {-(1.0 - r) * (1.0 - t), -(r) * (1.0 - t), (r) * (1.0 - t), (1.0 - r) * (1.0 - t), -(1.0 - r) * (t), -(r) * (t), (r) * (t), (1.0 - r) * (t)};

414:       const PetscReal dNi_dzeta[8] = {-(1.0 - r) * (1.0 - s), -(r) * (1.0 - s), -(r) * (s), -(1.0 - r) * (s), (1.0 - r) * (1.0 - s), (r) * (1.0 - s), (r) * (s), (1.0 - r) * (s)};

416:       PetscCall(PetscArrayzero(jacobian, 9));
417:       PetscCall(PetscArrayzero(ijacobian, 9));
418:       for (i = 0; i < nverts; ++i) {
419:         const PetscReal *vertex = coords + i * 3;
420:         jacobian[0] += dNi_dxi[i] * vertex[0];
421:         jacobian[3] += dNi_dxi[i] * vertex[1];
422:         jacobian[6] += dNi_dxi[i] * vertex[2];
423:         jacobian[1] += dNi_deta[i] * vertex[0];
424:         jacobian[4] += dNi_deta[i] * vertex[1];
425:         jacobian[7] += dNi_deta[i] * vertex[2];
426:         jacobian[2] += dNi_dzeta[i] * vertex[0];
427:         jacobian[5] += dNi_dzeta[i] * vertex[1];
428:         jacobian[8] += dNi_dzeta[i] * vertex[2];
429:         if (phypts) {
430:           phypts[3 * j + 0] += phi[i + offset] * vertex[0];
431:           phypts[3 * j + 1] += phi[i + offset] * vertex[1];
432:           phypts[3 * j + 2] += phi[i + offset] * vertex[2];
433:         }
434:       }

436:       /* invert the jacobian */
437:       PetscCall(DMatrix_Invert_3x3_Internal(jacobian, ijacobian, volume));
438:       PetscCheck(*volume >= 1e-8, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Hexahedral element has zero volume: %g. Degenerate element or invalid connectivity", *volume);

440:       if (jxw) jxw[j] *= (*volume);

442:       /*  Divide by element jacobian. */
443:       for (i = 0; i < nverts; ++i) {
444:         for (k = 0; k < 3; ++k) {
445:           if (dphidx) dphidx[i + offset] += dNi_dxi[i] * ijacobian[0 * 3 + k];
446:           if (dphidy) dphidy[i + offset] += dNi_deta[i] * ijacobian[1 * 3 + k];
447:           if (dphidz) dphidz[i + offset] += dNi_dzeta[i] * ijacobian[2 * 3 + k];
448:         }
449:       }
450:     }
451:   } else if (nverts == 4) { /* Linear Tetrahedra */
452:     PetscReal       Dx[4] = {0, 0, 0, 0}, Dy[4] = {0, 0, 0, 0}, Dz[4] = {0, 0, 0, 0};
453:     const PetscReal x0 = coords[/*0 * 3 +*/ 0], y0 = coords[/*0 * 3 +*/ 1], z0 = coords[/*0 * 3 +*/ 2];

455:     PetscCall(PetscArrayzero(jacobian, 9));
456:     PetscCall(PetscArrayzero(ijacobian, 9));

458:     /* compute the jacobian */
459:     jacobian[0] = coords[1 * 3 + 0] - x0;
460:     jacobian[1] = coords[2 * 3 + 0] - x0;
461:     jacobian[2] = coords[3 * 3 + 0] - x0;
462:     jacobian[3] = coords[1 * 3 + 1] - y0;
463:     jacobian[4] = coords[2 * 3 + 1] - y0;
464:     jacobian[5] = coords[3 * 3 + 1] - y0;
465:     jacobian[6] = coords[1 * 3 + 2] - z0;
466:     jacobian[7] = coords[2 * 3 + 2] - z0;
467:     jacobian[8] = coords[3 * 3 + 2] - z0;

469:     /* invert the jacobian */
470:     PetscCall(DMatrix_Invert_3x3_Internal(jacobian, ijacobian, volume));
471:     PetscCheck(*volume >= 1e-8, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Tetrahedral element has zero volume: %g. Degenerate element or invalid connectivity", (double)*volume);

473:     if (dphidx) {
474:       Dx[0] = (coords[1 + 2 * 3] * (coords[2 + 1 * 3] - coords[2 + 3 * 3]) - coords[1 + 1 * 3] * (coords[2 + 2 * 3] - coords[2 + 3 * 3]) - coords[1 + 3 * 3] * (coords[2 + 1 * 3] - coords[2 + 2 * 3])) / *volume;
475:       Dx[1] = -(coords[1 + 2 * 3] * (coords[2 + 0 * 3] - coords[2 + 3 * 3]) - coords[1 + 0 * 3] * (coords[2 + 2 * 3] - coords[2 + 3 * 3]) - coords[1 + 3 * 3] * (coords[2 + 0 * 3] - coords[2 + 2 * 3])) / *volume;
476:       Dx[2] = (coords[1 + 1 * 3] * (coords[2 + 0 * 3] - coords[2 + 3 * 3]) - coords[1 + 0 * 3] * (coords[2 + 1 * 3] - coords[2 + 3 * 3]) - coords[1 + 3 * 3] * (coords[2 + 0 * 3] - coords[2 + 1 * 3])) / *volume;
477:       Dx[3] = -(Dx[0] + Dx[1] + Dx[2]);
478:       Dy[0] = (coords[0 + 1 * 3] * (coords[2 + 2 * 3] - coords[2 + 3 * 3]) - coords[0 + 2 * 3] * (coords[2 + 1 * 3] - coords[2 + 3 * 3]) + coords[0 + 3 * 3] * (coords[2 + 1 * 3] - coords[2 + 2 * 3])) / *volume;
479:       Dy[1] = -(coords[0 + 0 * 3] * (coords[2 + 2 * 3] - coords[2 + 3 * 3]) - coords[0 + 2 * 3] * (coords[2 + 0 * 3] - coords[2 + 3 * 3]) + coords[0 + 3 * 3] * (coords[2 + 0 * 3] - coords[2 + 2 * 3])) / *volume;
480:       Dy[2] = (coords[0 + 0 * 3] * (coords[2 + 1 * 3] - coords[2 + 3 * 3]) - coords[0 + 1 * 3] * (coords[2 + 0 * 3] - coords[2 + 3 * 3]) + coords[0 + 3 * 3] * (coords[2 + 0 * 3] - coords[2 + 1 * 3])) / *volume;
481:       Dy[3] = -(Dy[0] + Dy[1] + Dy[2]);
482:       Dz[0] = (coords[0 + 1 * 3] * (coords[1 + 3 * 3] - coords[1 + 2 * 3]) - coords[0 + 2 * 3] * (coords[1 + 3 * 3] - coords[1 + 1 * 3]) + coords[0 + 3 * 3] * (coords[1 + 2 * 3] - coords[1 + 1 * 3])) / *volume;
483:       Dz[1] = -(coords[0 + 0 * 3] * (coords[1 + 3 * 3] - coords[1 + 2 * 3]) + coords[0 + 2 * 3] * (coords[1 + 0 * 3] - coords[1 + 3 * 3]) - coords[0 + 3 * 3] * (coords[1 + 0 * 3] - coords[1 + 2 * 3])) / *volume;
484:       Dz[2] = (coords[0 + 0 * 3] * (coords[1 + 3 * 3] - coords[1 + 1 * 3]) + coords[0 + 1 * 3] * (coords[1 + 0 * 3] - coords[1 + 3 * 3]) - coords[0 + 3 * 3] * (coords[1 + 0 * 3] - coords[1 + 1 * 3])) / *volume;
485:       Dz[3] = -(Dz[0] + Dz[1] + Dz[2]);
486:     }

488:     for (j = 0; j < npts; j++) {
489:       const PetscInt   offset = j * nverts;
490:       const PetscReal &r      = quad[j * 3 + 0];
491:       const PetscReal &s      = quad[j * 3 + 1];
492:       const PetscReal &t      = quad[j * 3 + 2];

494:       if (jxw) jxw[j] *= *volume;

496:       phi[offset + 0] = 1.0 - r - s - t;
497:       phi[offset + 1] = r;
498:       phi[offset + 2] = s;
499:       phi[offset + 3] = t;

501:       if (phypts) {
502:         for (i = 0; i < nverts; ++i) {
503:           const PetscReal *vertices = coords + i * 3;
504:           phypts[3 * j + 0] += phi[i + offset] * vertices[0];
505:           phypts[3 * j + 1] += phi[i + offset] * vertices[1];
506:           phypts[3 * j + 2] += phi[i + offset] * vertices[2];
507:         }
508:       }

510:       /* Now set the derivatives */
511:       if (dphidx) {
512:         dphidx[0 + offset] = Dx[0];
513:         dphidx[1 + offset] = Dx[1];
514:         dphidx[2 + offset] = Dx[2];
515:         dphidx[3 + offset] = Dx[3];

517:         dphidy[0 + offset] = Dy[0];
518:         dphidy[1 + offset] = Dy[1];
519:         dphidy[2 + offset] = Dy[2];
520:         dphidy[3 + offset] = Dy[3];

522:         dphidz[0 + offset] = Dz[0];
523:         dphidz[1 + offset] = Dz[1];
524:         dphidz[2 + offset] = Dz[2];
525:         dphidz[3 + offset] = Dz[3];
526:       }

528:     } /* Tetrahedra -- ends */
529:   } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "The number of entity vertices are invalid. Currently only support HEX8 and TET4 basis evaluations in 3-D : %" PetscInt_FMT, nverts);
530:   PetscFunctionReturn(PETSC_SUCCESS);
531: }

533: /*@C
534:   DMMoabFEMComputeBasis - Evaluate bases and derivatives at quadrature points for a linear EDGE/QUAD/TRI/HEX/TET element.

536:   The routine takes the coordinates of the vertices of an element and computes basis functions associated with
537:   each quadrature point provided, and their derivatives with respect to X, Y and Z as appropriate.

539:   Input Parameters:
540: + dim         - the dimension
541: . nverts      - the number of element vertices
542: . coordinates - the physical coordinates of the vertices (in canonical numbering)
543: - quadrature  - the evaluation points (quadrature points) in the reference space

545:   Output Parameters:
546: + phypts                             - the evaluation points (quadrature points) transformed to the physical space
547: . jacobian_quadrature_weight_product - the jacobian determinant * quadrature weight necessary for assembling discrete contributions
548: . fe_basis                           - the bases values evaluated at the specified quadrature points
549: - fe_basis_derivatives               - the derivative of the bases wrt (X,Y,Z)-directions (depending on the dimension) evaluated at the specified quadrature points

551:   Level: advanced

553: .seealso: `DMMoabCreate()`
554: @*/
555: PetscErrorCode DMMoabFEMComputeBasis(const PetscInt dim, const PetscInt nverts, const PetscReal *coordinates, const PetscQuadrature quadrature, PetscReal *phypts, PetscReal *jacobian_quadrature_weight_product, PetscReal *fe_basis, PetscReal **fe_basis_derivatives)
556: {
557:   PetscInt         npoints, idim;
558:   bool             compute_der;
559:   const PetscReal *quadpts, *quadwts;
560:   PetscReal        jacobian[9], ijacobian[9], volume;

562:   PetscFunctionBegin;
563:   PetscAssertPointer(coordinates, 3);
565:   PetscAssertPointer(fe_basis, 7);
566:   compute_der = (fe_basis_derivatives != NULL);

568:   /* Get the quadrature points and weights for the given quadrature rule */
569:   PetscCall(PetscQuadratureGetData(quadrature, &idim, NULL, &npoints, &quadpts, &quadwts));
570:   PetscCheck(idim == dim, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "Dimension mismatch: provided (%" PetscInt_FMT ") vs quadrature (%" PetscInt_FMT ")", idim, dim);
571:   if (jacobian_quadrature_weight_product) PetscCall(PetscArraycpy(jacobian_quadrature_weight_product, quadwts, npoints));

573:   switch (dim) {
574:   case 1:
575:     PetscCall(Compute_Lagrange_Basis_1D_Internal(nverts, coordinates, npoints, quadpts, phypts, jacobian_quadrature_weight_product, fe_basis, (compute_der ? fe_basis_derivatives[0] : NULL), jacobian, ijacobian, &volume));
576:     break;
577:   case 2:
578:     PetscCall(Compute_Lagrange_Basis_2D_Internal(nverts, coordinates, npoints, quadpts, phypts, jacobian_quadrature_weight_product, fe_basis, (compute_der ? fe_basis_derivatives[0] : NULL), (compute_der ? fe_basis_derivatives[1] : NULL), jacobian, ijacobian, &volume));
579:     break;
580:   case 3:
581:     PetscCall(Compute_Lagrange_Basis_3D_Internal(nverts, coordinates, npoints, quadpts, phypts, jacobian_quadrature_weight_product, fe_basis, (compute_der ? fe_basis_derivatives[0] : NULL), (compute_der ? fe_basis_derivatives[1] : NULL), (compute_der ? fe_basis_derivatives[2] : NULL), jacobian, ijacobian, &volume));
582:     break;
583:   default:
584:     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension; should be in [1,3] : %" PetscInt_FMT, dim);
585:   }
586:   PetscFunctionReturn(PETSC_SUCCESS);
587: }

589: /*@C
590:   DMMoabFEMCreateQuadratureDefault - Create default quadrature rules for integration over an element with a given
591:   dimension and polynomial order (deciphered from number of element vertices).

593:   Input Parameters:
594: + dim    - the element dimension (1=EDGE, 2=QUAD/TRI, 3=HEX/TET)
595: - nverts - the number of vertices in the physical element

597:   Output Parameter:
598: . quadrature - the quadrature object with default settings to integrate polynomials defined over the element

600:   Level: advanced

602: .seealso: `DMMoabCreate()`
603: @*/
604: PetscErrorCode DMMoabFEMCreateQuadratureDefault(const PetscInt dim, const PetscInt nverts, PetscQuadrature *quadrature)
605: {
606:   PetscReal *w, *x;
607:   PetscInt   nc = 1;

609:   PetscFunctionBegin;
610:   /* Create an appropriate quadrature rule to sample basis */
611:   switch (dim) {
612:   case 1:
613:     /* Create Gauss quadrature rules with <order = nverts> in the span [-1, 1] */
614:     PetscCall(PetscDTStroudConicalQuadrature(1, nc, nverts, 0, 1.0, quadrature));
615:     break;
616:   case 2:
617:     /* Create Gauss quadrature rules with <order = nverts> in the span [-1, 1] */
618:     if (nverts == 3) {
619:       const PetscInt order   = 2;
620:       const PetscInt npoints = (order == 2 ? 3 : 6);
621:       PetscCall(PetscMalloc2(npoints * 2, &x, npoints, &w));
622:       if (npoints == 3) {
623:         x[0] = x[1] = x[2] = x[5] = 1.0 / 6.0;
624:         x[3] = x[4] = 2.0 / 3.0;
625:         w[0] = w[1] = w[2] = 1.0 / 3.0;
626:       } else if (npoints == 6) {
627:         x[0] = x[1] = x[2] = 0.44594849091597;
628:         x[3] = x[4] = 0.10810301816807;
629:         x[5]        = 0.44594849091597;
630:         x[6] = x[7] = x[8] = 0.09157621350977;
631:         x[9] = x[10] = 0.81684757298046;
632:         x[11]        = 0.09157621350977;
633:         w[0] = w[1] = w[2] = 0.22338158967801;
634:         w[3] = w[4] = w[5] = 0.10995174365532;
635:       } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Triangle quadrature rules for points 3 and 6 supported; npoints : %" PetscInt_FMT, npoints);
636:       PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, quadrature));
637:       PetscCall(PetscQuadratureSetOrder(*quadrature, order));
638:       PetscCall(PetscQuadratureSetData(*quadrature, dim, nc, npoints, x, w));
639:       /* PetscCall(PetscDTStroudConicalQuadrature(dim, nc, nverts, 0.0, 1.0, quadrature)); */
640:     } else PetscCall(PetscDTGaussTensorQuadrature(dim, nc, nverts, 0.0, 1.0, quadrature));
641:     break;
642:   case 3:
643:     /* Create Gauss quadrature rules with <order = nverts> in the span [-1, 1] */
644:     if (nverts == 4) {
645:       PetscInt       order;
646:       const PetscInt npoints = 4; // Choose between 4 and 10
647:       PetscCall(PetscMalloc2(npoints * 3, &x, npoints, &w));
648:       if (npoints == 4) { /*  KEAST1, K1,  N=4, O=4 */
649:         const PetscReal x_4[12] = {0.5854101966249685, 0.1381966011250105, 0.1381966011250105, 0.1381966011250105, 0.1381966011250105, 0.1381966011250105,
650:                                    0.1381966011250105, 0.1381966011250105, 0.5854101966249685, 0.1381966011250105, 0.5854101966249685, 0.1381966011250105};
651:         PetscCall(PetscArraycpy(x, x_4, 12));

653:         w[0] = w[1] = w[2] = w[3] = 1.0 / 24.0;
654:         order                     = 4;
655:       } else if (npoints == 10) { /*  KEAST3, K3  N=10, O=10 */
656:         const PetscReal x_10[30] = {0.5684305841968444, 0.1438564719343852, 0.1438564719343852, 0.1438564719343852, 0.1438564719343852, 0.1438564719343852, 0.1438564719343852, 0.1438564719343852, 0.5684305841968444, 0.1438564719343852,
657:                                     0.5684305841968444, 0.1438564719343852, 0.0000000000000000, 0.5000000000000000, 0.5000000000000000, 0.5000000000000000, 0.0000000000000000, 0.5000000000000000, 0.5000000000000000, 0.5000000000000000,
658:                                     0.0000000000000000, 0.5000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.5000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.5000000000000000};
659:         PetscCall(PetscArraycpy(x, x_10, 30));

661:         w[0] = w[1] = w[2] = w[3] = 0.2177650698804054;
662:         w[4] = w[5] = w[6] = w[7] = w[8] = w[9] = 0.0214899534130631;
663:         order                                   = 10;
664:       } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Tetrahedral quadrature rules for points 4 and 10 supported; npoints : %" PetscInt_FMT, npoints);
665:       PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, quadrature));
666:       PetscCall(PetscQuadratureSetOrder(*quadrature, order));
667:       PetscCall(PetscQuadratureSetData(*quadrature, dim, nc, npoints, x, w));
668:       /* PetscCall(PetscDTStroudConicalQuadrature(dim, nc, nverts, 0.0, 1.0, quadrature)); */
669:     } else PetscCall(PetscDTGaussTensorQuadrature(dim, nc, nverts, 0.0, 1.0, quadrature));
670:     break;
671:   default:
672:     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension; should be in [1,3] : %" PetscInt_FMT, dim);
673:   }
674:   PetscFunctionReturn(PETSC_SUCCESS);
675: }

677: /* Compute Jacobians */
678: static PetscErrorCode FEMComputeBasis_JandF(const PetscInt dim, const PetscInt nverts, const PetscReal *coordinates, const PetscReal *quadrature, PetscReal *phypts, PetscReal *phibasis, PetscReal *jacobian, PetscReal *ijacobian, PetscReal *volume)
679: {
680:   PetscFunctionBegin;
681:   switch (dim) {
682:   case 1:
683:     PetscCall(Compute_Lagrange_Basis_1D_Internal(nverts, coordinates, 1, quadrature, phypts, NULL, phibasis, NULL, jacobian, ijacobian, volume));
684:     break;
685:   case 2:
686:     PetscCall(Compute_Lagrange_Basis_2D_Internal(nverts, coordinates, 1, quadrature, phypts, NULL, phibasis, NULL, NULL, jacobian, ijacobian, volume));
687:     break;
688:   case 3:
689:     PetscCall(Compute_Lagrange_Basis_3D_Internal(nverts, coordinates, 1, quadrature, phypts, NULL, phibasis, NULL, NULL, NULL, jacobian, ijacobian, volume));
690:     break;
691:   default:
692:     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension; should be in [1,3] : %" PetscInt_FMT, dim);
693:   }
694:   PetscFunctionReturn(PETSC_SUCCESS);
695: }

697: /*@C
698:   DMMoabPToRMapping - Compute the mapping from the physical coordinate system for a given element to the
699:   canonical reference element.

701:   Input Parameters:
702: + dim         - the element dimension (1=EDGE, 2=QUAD/TRI, 3=HEX/TET)
703: . nverts      - the number of vertices in the physical element
704: . coordinates - the coordinates of vertices in the physical element
705: - xphy        - the coordinates of physical point for which natural coordinates (in reference frame) are sought

707:   Output Parameters:
708: + natparam - the natural coordinates (in reference frame) corresponding to xphy
709: - phi      - the basis functions evaluated at the natural coordinates (natparam)

711:   Level: advanced

713:   Notes:
714:   In addition to finding the inverse mapping evaluation through Newton iteration, the basis
715:   function at the parametric point is also evaluated optionally.

717: .seealso: `DMMoabCreate()`
718: @*/
719: PetscErrorCode DMMoabPToRMapping(const PetscInt dim, const PetscInt nverts, const PetscReal *coordinates, const PetscReal *xphy, PetscReal *natparam, PetscReal *phi)
720: {
721:   /* Perform inverse evaluation for the mapping with use of Newton Raphson iteration */
722:   const PetscReal tol            = 1.0e-10;
723:   const PetscInt  max_iterations = 10;
724:   const PetscReal error_tol_sqr  = tol * tol;
725:   PetscReal       phibasis[8], jacobian[9], ijacobian[9], volume;
726:   PetscReal       phypts[3] = {0.0, 0.0, 0.0};
727:   PetscReal       delta[3]  = {0.0, 0.0, 0.0};
728:   PetscInt        iters     = 0;
729:   PetscReal       error     = 1.0;

731:   PetscFunctionBegin;
732:   PetscAssertPointer(coordinates, 3);
733:   PetscAssertPointer(xphy, 4);
734:   PetscAssertPointer(natparam, 5);

736:   PetscCall(PetscArrayzero(jacobian, dim * dim));
737:   PetscCall(PetscArrayzero(ijacobian, dim * dim));
738:   PetscCall(PetscArrayzero(phibasis, nverts));

740:   /* zero initial guess */
741:   natparam[0] = natparam[1] = natparam[2] = 0.0;

743:   /* Compute delta = evaluate( xi) - x */
744:   PetscCall(FEMComputeBasis_JandF(dim, nverts, coordinates, natparam, phypts, phibasis, jacobian, ijacobian, &volume));

746:   error = 0.0;
747:   switch (dim) {
748:   case 3:
749:     delta[2] = phypts[2] - xphy[2];
750:     error += (delta[2] * delta[2]);
751:   case 2:
752:     delta[1] = phypts[1] - xphy[1];
753:     error += (delta[1] * delta[1]);
754:   case 1:
755:     delta[0] = phypts[0] - xphy[0];
756:     error += (delta[0] * delta[0]);
757:     break;
758:   }

760:   while (error > error_tol_sqr) {
761:     PetscCheck(++iters <= max_iterations, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Maximum Newton iterations (10) reached. Current point in reference space : (%g, %g, %g)", natparam[0], natparam[1], natparam[2]);

763:     /* Compute natparam -= J.inverse() * delta */
764:     switch (dim) {
765:     case 1:
766:       natparam[0] -= ijacobian[0] * delta[0];
767:       break;
768:     case 2:
769:       natparam[0] -= ijacobian[0] * delta[0] + ijacobian[1] * delta[1];
770:       natparam[1] -= ijacobian[2] * delta[0] + ijacobian[3] * delta[1];
771:       break;
772:     case 3:
773:       natparam[0] -= ijacobian[0] * delta[0] + ijacobian[1] * delta[1] + ijacobian[2] * delta[2];
774:       natparam[1] -= ijacobian[3] * delta[0] + ijacobian[4] * delta[1] + ijacobian[5] * delta[2];
775:       natparam[2] -= ijacobian[6] * delta[0] + ijacobian[7] * delta[1] + ijacobian[8] * delta[2];
776:       break;
777:     }

779:     /* Compute delta = evaluate( xi) - x */
780:     PetscCall(FEMComputeBasis_JandF(dim, nverts, coordinates, natparam, phypts, phibasis, jacobian, ijacobian, &volume));

782:     error = 0.0;
783:     switch (dim) {
784:     case 3:
785:       delta[2] = phypts[2] - xphy[2];
786:       error += (delta[2] * delta[2]);
787:     case 2:
788:       delta[1] = phypts[1] - xphy[1];
789:       error += (delta[1] * delta[1]);
790:     case 1:
791:       delta[0] = phypts[0] - xphy[0];
792:       error += (delta[0] * delta[0]);
793:       break;
794:     }
795:   }
796:   if (phi) PetscCall(PetscArraycpy(phi, phibasis, nverts));
797:   PetscFunctionReturn(PETSC_SUCCESS);
798: }