1 /* Discretization tools */
2
3 #include <petscdt.h> /*I "petscdt.h" I*/
4 #include <petscblaslapack.h>
5 #include <petsc/private/petscimpl.h>
6 #include <petsc/private/dtimpl.h>
7 #include <petsc/private/petscfeimpl.h> /* For CoordinatesRefToReal() */
8 #include <petscviewer.h>
9 #include <petscdmplex.h>
10 #include <petscdmshell.h>
11
12 #if defined(PETSC_HAVE_MPFR)
13 #include <mpfr.h>
14 #endif
15
16 const char *const PetscDTNodeTypes_shifted[] = {"default", "gaussjacobi", "equispaced", "tanhsinh", "PetscDTNodeType", "PETSCDTNODES_", NULL};
17 const char *const *const PetscDTNodeTypes = PetscDTNodeTypes_shifted + 1;
18
19 const char *const PetscDTSimplexQuadratureTypes_shifted[] = {"default", "conic", "minsym", "diagsym", "PetscDTSimplexQuadratureType", "PETSCDTSIMPLEXQUAD_", NULL};
20 const char *const *const PetscDTSimplexQuadratureTypes = PetscDTSimplexQuadratureTypes_shifted + 1;
21
22 static PetscBool GolubWelschCite = PETSC_FALSE;
23 const char GolubWelschCitation[] = "@article{GolubWelsch1969,\n"
24 " author = {Golub and Welsch},\n"
25 " title = {Calculation of Quadrature Rules},\n"
26 " journal = {Math. Comp.},\n"
27 " volume = {23},\n"
28 " number = {106},\n"
29 " pages = {221--230},\n"
30 " year = {1969}\n}\n";
31
32 /* Numerical tests in src/dm/dt/tests/ex1.c show that when computing the nodes and weights of Gauss-Jacobi
33 quadrature rules:
34
35 - in double precision, Newton's method and Golub & Welsch both work for moderate degrees (< 100),
36 - in single precision, Newton's method starts producing incorrect roots around n = 15, but
37 the weights from Golub & Welsch become a problem before then: they produces errors
38 in computing the Jacobi-polynomial Gram matrix around n = 6.
39
40 So we default to Newton's method (required fewer dependencies) */
41 PetscBool PetscDTGaussQuadratureNewton_Internal = PETSC_TRUE;
42
43 PetscClassId PETSCQUADRATURE_CLASSID = 0;
44
45 /*@
46 PetscQuadratureCreate - Create a `PetscQuadrature` object
47
48 Collective
49
50 Input Parameter:
51 . comm - The communicator for the `PetscQuadrature` object
52
53 Output Parameter:
54 . q - The `PetscQuadrature` object
55
56 Level: beginner
57
58 .seealso: `PetscQuadrature`, `Petscquadraturedestroy()`, `PetscQuadratureGetData()`
59 @*/
PetscQuadratureCreate(MPI_Comm comm,PetscQuadrature * q)60 PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q)
61 {
62 PetscFunctionBegin;
63 PetscAssertPointer(q, 2);
64 PetscCall(DMInitializePackage());
65 PetscCall(PetscHeaderCreate(*q, PETSCQUADRATURE_CLASSID, "PetscQuadrature", "Quadrature", "DT", comm, PetscQuadratureDestroy, PetscQuadratureView));
66 (*q)->ct = DM_POLYTOPE_UNKNOWN;
67 (*q)->dim = -1;
68 (*q)->Nc = 1;
69 (*q)->order = -1;
70 (*q)->numPoints = 0;
71 (*q)->points = NULL;
72 (*q)->weights = NULL;
73 PetscFunctionReturn(PETSC_SUCCESS);
74 }
75
76 /*@
77 PetscQuadratureDuplicate - Create a deep copy of the `PetscQuadrature` object
78
79 Collective
80
81 Input Parameter:
82 . q - The `PetscQuadrature` object
83
84 Output Parameter:
85 . r - The new `PetscQuadrature` object
86
87 Level: beginner
88
89 .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureDestroy()`, `PetscQuadratureGetData()`
90 @*/
PetscQuadratureDuplicate(PetscQuadrature q,PetscQuadrature * r)91 PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r)
92 {
93 DMPolytopeType ct;
94 PetscInt order, dim, Nc, Nq;
95 const PetscReal *points, *weights;
96 PetscReal *p, *w;
97
98 PetscFunctionBegin;
99 PetscAssertPointer(q, 1);
100 PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)q), r));
101 PetscCall(PetscQuadratureGetCellType(q, &ct));
102 PetscCall(PetscQuadratureSetCellType(*r, ct));
103 PetscCall(PetscQuadratureGetOrder(q, &order));
104 PetscCall(PetscQuadratureSetOrder(*r, order));
105 PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights));
106 PetscCall(PetscMalloc1(Nq * dim, &p));
107 PetscCall(PetscMalloc1(Nq * Nc, &w));
108 PetscCall(PetscArraycpy(p, points, Nq * dim));
109 PetscCall(PetscArraycpy(w, weights, Nc * Nq));
110 PetscCall(PetscQuadratureSetData(*r, dim, Nc, Nq, p, w));
111 PetscFunctionReturn(PETSC_SUCCESS);
112 }
113
114 /*@
115 PetscQuadratureDestroy - Destroys a `PetscQuadrature` object
116
117 Collective
118
119 Input Parameter:
120 . q - The `PetscQuadrature` object
121
122 Level: beginner
123
124 .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
125 @*/
PetscQuadratureDestroy(PetscQuadrature * q)126 PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q)
127 {
128 PetscFunctionBegin;
129 if (!*q) PetscFunctionReturn(PETSC_SUCCESS);
130 PetscValidHeaderSpecific(*q, PETSCQUADRATURE_CLASSID, 1);
131 if (--((PetscObject)*q)->refct > 0) {
132 *q = NULL;
133 PetscFunctionReturn(PETSC_SUCCESS);
134 }
135 PetscCall(PetscFree((*q)->points));
136 PetscCall(PetscFree((*q)->weights));
137 PetscCall(PetscHeaderDestroy(q));
138 PetscFunctionReturn(PETSC_SUCCESS);
139 }
140
141 /*@
142 PetscQuadratureGetCellType - Return the cell type of the integration domain
143
144 Not Collective
145
146 Input Parameter:
147 . q - The `PetscQuadrature` object
148
149 Output Parameter:
150 . ct - The cell type of the integration domain
151
152 Level: intermediate
153
154 .seealso: `PetscQuadrature`, `PetscQuadratureSetCellType()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
155 @*/
PetscQuadratureGetCellType(PetscQuadrature q,DMPolytopeType * ct)156 PetscErrorCode PetscQuadratureGetCellType(PetscQuadrature q, DMPolytopeType *ct)
157 {
158 PetscFunctionBegin;
159 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
160 PetscAssertPointer(ct, 2);
161 *ct = q->ct;
162 PetscFunctionReturn(PETSC_SUCCESS);
163 }
164
165 /*@
166 PetscQuadratureSetCellType - Set the cell type of the integration domain
167
168 Not Collective
169
170 Input Parameters:
171 + q - The `PetscQuadrature` object
172 - ct - The cell type of the integration domain
173
174 Level: intermediate
175
176 .seealso: `PetscQuadrature`, `PetscQuadratureGetCellType()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
177 @*/
PetscQuadratureSetCellType(PetscQuadrature q,DMPolytopeType ct)178 PetscErrorCode PetscQuadratureSetCellType(PetscQuadrature q, DMPolytopeType ct)
179 {
180 PetscFunctionBegin;
181 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
182 q->ct = ct;
183 PetscFunctionReturn(PETSC_SUCCESS);
184 }
185
186 /*@
187 PetscQuadratureGetOrder - Return the order of the method in the `PetscQuadrature`
188
189 Not Collective
190
191 Input Parameter:
192 . q - The `PetscQuadrature` object
193
194 Output Parameter:
195 . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
196
197 Level: intermediate
198
199 .seealso: `PetscQuadrature`, `PetscQuadratureSetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
200 @*/
PetscQuadratureGetOrder(PetscQuadrature q,PetscInt * order)201 PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order)
202 {
203 PetscFunctionBegin;
204 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
205 PetscAssertPointer(order, 2);
206 *order = q->order;
207 PetscFunctionReturn(PETSC_SUCCESS);
208 }
209
210 /*@
211 PetscQuadratureSetOrder - Set the order of the method in the `PetscQuadrature`
212
213 Not Collective
214
215 Input Parameters:
216 + q - The `PetscQuadrature` object
217 - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
218
219 Level: intermediate
220
221 .seealso: `PetscQuadrature`, `PetscQuadratureGetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
222 @*/
PetscQuadratureSetOrder(PetscQuadrature q,PetscInt order)223 PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order)
224 {
225 PetscFunctionBegin;
226 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
227 q->order = order;
228 PetscFunctionReturn(PETSC_SUCCESS);
229 }
230
231 /*@
232 PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated
233
234 Not Collective
235
236 Input Parameter:
237 . q - The `PetscQuadrature` object
238
239 Output Parameter:
240 . Nc - The number of components
241
242 Level: intermediate
243
244 Note:
245 We are performing an integral $\int f(x) w(x) dx$, where both $f$ and $w$ (the weight) have `Nc` components.
246
247 .seealso: `PetscQuadrature`, `PetscQuadratureSetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
248 @*/
PetscQuadratureGetNumComponents(PetscQuadrature q,PetscInt * Nc)249 PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc)
250 {
251 PetscFunctionBegin;
252 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
253 PetscAssertPointer(Nc, 2);
254 *Nc = q->Nc;
255 PetscFunctionReturn(PETSC_SUCCESS);
256 }
257
258 /*@
259 PetscQuadratureSetNumComponents - Sets the number of components for functions to be integrated
260
261 Not Collective
262
263 Input Parameters:
264 + q - The `PetscQuadrature` object
265 - Nc - The number of components
266
267 Level: intermediate
268
269 Note:
270 We are performing an integral $\int f(x) w(x) dx$, where both $f$ and $w$ (the weight) have `Nc` components.
271
272 .seealso: `PetscQuadrature`, `PetscQuadratureGetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
273 @*/
PetscQuadratureSetNumComponents(PetscQuadrature q,PetscInt Nc)274 PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc)
275 {
276 PetscFunctionBegin;
277 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
278 q->Nc = Nc;
279 PetscFunctionReturn(PETSC_SUCCESS);
280 }
281
282 /*@C
283 PetscQuadratureGetData - Returns the data defining the `PetscQuadrature`
284
285 Not Collective
286
287 Input Parameter:
288 . q - The `PetscQuadrature` object
289
290 Output Parameters:
291 + dim - The spatial dimension
292 . Nc - The number of components
293 . npoints - The number of quadrature points
294 . points - The coordinates of each quadrature point
295 - weights - The weight of each quadrature point
296
297 Level: intermediate
298
299 Note:
300 All output arguments are optional, pass `NULL` for any argument not required
301
302 Fortran Note:
303 Call `PetscQuadratureRestoreData()` when you are done with the data
304
305 .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureSetData()`
306 @*/
PetscQuadratureGetData(PetscQuadrature q,PeOp PetscInt * dim,PeOp PetscInt * Nc,PeOp PetscInt * npoints,PeOp const PetscReal * points[],PeOp const PetscReal * weights[])307 PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PeOp PetscInt *dim, PeOp PetscInt *Nc, PeOp PetscInt *npoints, PeOp const PetscReal *points[], PeOp const PetscReal *weights[])
308 {
309 PetscFunctionBegin;
310 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
311 if (dim) {
312 PetscAssertPointer(dim, 2);
313 *dim = q->dim;
314 }
315 if (Nc) {
316 PetscAssertPointer(Nc, 3);
317 *Nc = q->Nc;
318 }
319 if (npoints) {
320 PetscAssertPointer(npoints, 4);
321 *npoints = q->numPoints;
322 }
323 if (points) {
324 PetscAssertPointer(points, 5);
325 *points = q->points;
326 }
327 if (weights) {
328 PetscAssertPointer(weights, 6);
329 *weights = q->weights;
330 }
331 PetscFunctionReturn(PETSC_SUCCESS);
332 }
333
334 /*@
335 PetscQuadratureEqual - determine whether two quadratures are equivalent
336
337 Input Parameters:
338 + A - A `PetscQuadrature` object
339 - B - Another `PetscQuadrature` object
340
341 Output Parameter:
342 . equal - `PETSC_TRUE` if the quadratures are the same
343
344 Level: intermediate
345
346 .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`
347 @*/
PetscQuadratureEqual(PetscQuadrature A,PetscQuadrature B,PetscBool * equal)348 PetscErrorCode PetscQuadratureEqual(PetscQuadrature A, PetscQuadrature B, PetscBool *equal)
349 {
350 PetscFunctionBegin;
351 PetscValidHeaderSpecific(A, PETSCQUADRATURE_CLASSID, 1);
352 PetscValidHeaderSpecific(B, PETSCQUADRATURE_CLASSID, 2);
353 PetscAssertPointer(equal, 3);
354 *equal = PETSC_FALSE;
355 if (A->ct != B->ct || A->dim != B->dim || A->Nc != B->Nc || A->order != B->order || A->numPoints != B->numPoints) PetscFunctionReturn(PETSC_SUCCESS);
356 for (PetscInt i = 0; i < A->numPoints * A->dim; i++) {
357 if (PetscAbsReal(A->points[i] - B->points[i]) > PETSC_SMALL) PetscFunctionReturn(PETSC_SUCCESS);
358 }
359 if (!A->weights && !B->weights) {
360 *equal = PETSC_TRUE;
361 PetscFunctionReturn(PETSC_SUCCESS);
362 }
363 if (A->weights && B->weights) {
364 for (PetscInt i = 0; i < A->numPoints; i++) {
365 if (PetscAbsReal(A->weights[i] - B->weights[i]) > PETSC_SMALL) PetscFunctionReturn(PETSC_SUCCESS);
366 }
367 *equal = PETSC_TRUE;
368 }
369 PetscFunctionReturn(PETSC_SUCCESS);
370 }
371
PetscDTJacobianInverse_Internal(PetscInt m,PetscInt n,const PetscReal J[],PetscReal Jinv[])372 static PetscErrorCode PetscDTJacobianInverse_Internal(PetscInt m, PetscInt n, const PetscReal J[], PetscReal Jinv[])
373 {
374 PetscScalar *Js, *Jinvs;
375 PetscInt i, j, k;
376 PetscBLASInt bm, bn, info;
377
378 PetscFunctionBegin;
379 if (!m || !n) PetscFunctionReturn(PETSC_SUCCESS);
380 PetscCall(PetscBLASIntCast(m, &bm));
381 PetscCall(PetscBLASIntCast(n, &bn));
382 #if defined(PETSC_USE_COMPLEX)
383 PetscCall(PetscMalloc2(m * n, &Js, m * n, &Jinvs));
384 for (i = 0; i < m * n; i++) Js[i] = J[i];
385 #else
386 Js = (PetscReal *)J;
387 Jinvs = Jinv;
388 #endif
389 if (m == n) {
390 PetscBLASInt *pivots;
391 PetscScalar *W;
392
393 PetscCall(PetscMalloc2(m, &pivots, m, &W));
394
395 PetscCall(PetscArraycpy(Jinvs, Js, m * m));
396 PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, Jinvs, &bm, pivots, &info));
397 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscBLASInt_FMT, info);
398 PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, Jinvs, &bm, pivots, W, &bm, &info));
399 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscBLASInt_FMT, info);
400 PetscCall(PetscFree2(pivots, W));
401 } else if (m < n) {
402 PetscScalar *JJT;
403 PetscBLASInt *pivots;
404 PetscScalar *W;
405
406 PetscCall(PetscMalloc1(m * m, &JJT));
407 PetscCall(PetscMalloc2(m, &pivots, m, &W));
408 for (i = 0; i < m; i++) {
409 for (j = 0; j < m; j++) {
410 PetscScalar val = 0.;
411
412 for (k = 0; k < n; k++) val += Js[i * n + k] * Js[j * n + k];
413 JJT[i * m + j] = val;
414 }
415 }
416
417 PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, JJT, &bm, pivots, &info));
418 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscBLASInt_FMT, info);
419 PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, JJT, &bm, pivots, W, &bm, &info));
420 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscBLASInt_FMT, info);
421 for (i = 0; i < n; i++) {
422 for (j = 0; j < m; j++) {
423 PetscScalar val = 0.;
424
425 for (k = 0; k < m; k++) val += Js[k * n + i] * JJT[k * m + j];
426 Jinvs[i * m + j] = val;
427 }
428 }
429 PetscCall(PetscFree2(pivots, W));
430 PetscCall(PetscFree(JJT));
431 } else {
432 PetscScalar *JTJ;
433 PetscBLASInt *pivots;
434 PetscScalar *W;
435
436 PetscCall(PetscMalloc1(n * n, &JTJ));
437 PetscCall(PetscMalloc2(n, &pivots, n, &W));
438 for (i = 0; i < n; i++) {
439 for (j = 0; j < n; j++) {
440 PetscScalar val = 0.;
441
442 for (k = 0; k < m; k++) val += Js[k * n + i] * Js[k * n + j];
443 JTJ[i * n + j] = val;
444 }
445 }
446
447 PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bn, &bn, JTJ, &bn, pivots, &info));
448 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscBLASInt_FMT, info);
449 PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bn, JTJ, &bn, pivots, W, &bn, &info));
450 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscBLASInt_FMT, info);
451 for (i = 0; i < n; i++) {
452 for (j = 0; j < m; j++) {
453 PetscScalar val = 0.;
454
455 for (k = 0; k < n; k++) val += JTJ[i * n + k] * Js[j * n + k];
456 Jinvs[i * m + j] = val;
457 }
458 }
459 PetscCall(PetscFree2(pivots, W));
460 PetscCall(PetscFree(JTJ));
461 }
462 #if defined(PETSC_USE_COMPLEX)
463 for (i = 0; i < m * n; i++) Jinv[i] = PetscRealPart(Jinvs[i]);
464 PetscCall(PetscFree2(Js, Jinvs));
465 #endif
466 PetscFunctionReturn(PETSC_SUCCESS);
467 }
468
469 /*@
470 PetscQuadraturePushForward - Push forward a quadrature functional under an affine transformation.
471
472 Collective
473
474 Input Parameters:
475 + q - the quadrature functional
476 . imageDim - the dimension of the image of the transformation
477 . origin - a point in the original space
478 . originImage - the image of the origin under the transformation
479 . J - the Jacobian of the image: an [imageDim x dim] matrix in row major order
480 - formDegree - transform the quadrature weights as k-forms of this form degree (if the number of components is a multiple of (dim choose `formDegree`),
481 it is assumed that they represent multiple k-forms) [see `PetscDTAltVPullback()` for interpretation of `formDegree`]
482
483 Output Parameter:
484 . Jinvstarq - a quadrature rule where each point is the image of a point in the original quadrature rule, and where the k-form weights have
485 been pulled-back by the pseudoinverse of `J` to the k-form weights in the image space.
486
487 Level: intermediate
488
489 Note:
490 The new quadrature rule will have a different number of components if spaces have different dimensions.
491 For example, pushing a 2-form forward from a two dimensional space to a three dimensional space changes the number of components from 1 to 3.
492
493 .seealso: `PetscQuadrature`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
494 @*/
PetscQuadraturePushForward(PetscQuadrature q,PetscInt imageDim,const PetscReal origin[],const PetscReal originImage[],const PetscReal J[],PetscInt formDegree,PetscQuadrature * Jinvstarq)495 PetscErrorCode PetscQuadraturePushForward(PetscQuadrature q, PetscInt imageDim, const PetscReal origin[], const PetscReal originImage[], const PetscReal J[], PetscInt formDegree, PetscQuadrature *Jinvstarq)
496 {
497 PetscInt dim, Nc, imageNc, formSize, Ncopies, imageFormSize, Npoints, pt, i, j, c;
498 const PetscReal *points;
499 const PetscReal *weights;
500 PetscReal *imagePoints, *imageWeights;
501 PetscReal *Jinv;
502 PetscReal *Jinvstar;
503
504 PetscFunctionBegin;
505 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
506 PetscCheck(imageDim >= PetscAbsInt(formDegree), PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Cannot represent a %" PetscInt_FMT "-form in %" PetscInt_FMT " dimensions", PetscAbsInt(formDegree), imageDim);
507 PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Npoints, &points, &weights));
508 PetscCall(PetscDTBinomialInt(dim, PetscAbsInt(formDegree), &formSize));
509 PetscCheck(Nc % formSize == 0, PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Number of components %" PetscInt_FMT " is not a multiple of formSize %" PetscInt_FMT, Nc, formSize);
510 Ncopies = Nc / formSize;
511 PetscCall(PetscDTBinomialInt(imageDim, PetscAbsInt(formDegree), &imageFormSize));
512 imageNc = Ncopies * imageFormSize;
513 PetscCall(PetscMalloc1(Npoints * imageDim, &imagePoints));
514 PetscCall(PetscMalloc1(Npoints * imageNc, &imageWeights));
515 PetscCall(PetscMalloc2(imageDim * dim, &Jinv, formSize * imageFormSize, &Jinvstar));
516 PetscCall(PetscDTJacobianInverse_Internal(imageDim, dim, J, Jinv));
517 PetscCall(PetscDTAltVPullbackMatrix(imageDim, dim, Jinv, formDegree, Jinvstar));
518 for (pt = 0; pt < Npoints; pt++) {
519 const PetscReal *point = PetscSafePointerPlusOffset(points, pt * dim);
520 PetscReal *imagePoint = &imagePoints[pt * imageDim];
521
522 for (i = 0; i < imageDim; i++) {
523 PetscReal val = originImage[i];
524
525 for (j = 0; j < dim; j++) val += J[i * dim + j] * (point[j] - origin[j]);
526 imagePoint[i] = val;
527 }
528 for (c = 0; c < Ncopies; c++) {
529 const PetscReal *form = &weights[pt * Nc + c * formSize];
530 PetscReal *imageForm = &imageWeights[pt * imageNc + c * imageFormSize];
531
532 for (i = 0; i < imageFormSize; i++) {
533 PetscReal val = 0.;
534
535 for (j = 0; j < formSize; j++) val += Jinvstar[i * formSize + j] * form[j];
536 imageForm[i] = val;
537 }
538 }
539 }
540 PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)q), Jinvstarq));
541 PetscCall(PetscQuadratureSetData(*Jinvstarq, imageDim, imageNc, Npoints, imagePoints, imageWeights));
542 PetscCall(PetscFree2(Jinv, Jinvstar));
543 PetscFunctionReturn(PETSC_SUCCESS);
544 }
545
546 /*@C
547 PetscQuadratureSetData - Sets the data defining the quadrature
548
549 Not Collective
550
551 Input Parameters:
552 + q - The `PetscQuadrature` object
553 . dim - The spatial dimension
554 . Nc - The number of components
555 . npoints - The number of quadrature points
556 . points - The coordinates of each quadrature point
557 - weights - The weight of each quadrature point
558
559 Level: intermediate
560
561 Note:
562 `q` owns the references to points and weights, so they must be allocated using `PetscMalloc()` and the user should not free them.
563
564 .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
565 @*/
PetscQuadratureSetData(PetscQuadrature q,PetscInt dim,PetscInt Nc,PetscInt npoints,const PetscReal points[],const PetscReal weights[])566 PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[]) PeNSS
567 {
568 PetscFunctionBegin;
569 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
570 if (dim >= 0) q->dim = dim;
571 if (Nc >= 0) q->Nc = Nc;
572 if (npoints >= 0) q->numPoints = npoints;
573 if (points) {
574 PetscAssertPointer(points, 5);
575 q->points = points;
576 }
577 if (weights) {
578 PetscAssertPointer(weights, 6);
579 q->weights = weights;
580 }
581 PetscFunctionReturn(PETSC_SUCCESS);
582 }
583
PetscQuadratureView_Ascii(PetscQuadrature quad,PetscViewer v)584 static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v)
585 {
586 PetscInt q, d, c;
587 PetscViewerFormat format;
588
589 PetscFunctionBegin;
590 if (quad->Nc > 1)
591 PetscCall(PetscViewerASCIIPrintf(v, "Quadrature on a %s of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ") with %" PetscInt_FMT " components\n", DMPolytopeTypes[quad->ct], quad->order, quad->numPoints, quad->dim, quad->Nc));
592 else PetscCall(PetscViewerASCIIPrintf(v, "Quadrature on a %s of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ")\n", DMPolytopeTypes[quad->ct], quad->order, quad->numPoints, quad->dim));
593 PetscCall(PetscViewerGetFormat(v, &format));
594 if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) PetscFunctionReturn(PETSC_SUCCESS);
595 for (q = 0; q < quad->numPoints; ++q) {
596 PetscCall(PetscViewerASCIIPrintf(v, "p%" PetscInt_FMT " (", q));
597 PetscCall(PetscViewerASCIIUseTabs(v, PETSC_FALSE));
598 for (d = 0; d < quad->dim; ++d) {
599 if (d) PetscCall(PetscViewerASCIIPrintf(v, ", "));
600 PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q * quad->dim + d]));
601 }
602 PetscCall(PetscViewerASCIIPrintf(v, ") "));
603 if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, "w%" PetscInt_FMT " (", q));
604 for (c = 0; c < quad->Nc; ++c) {
605 if (c) PetscCall(PetscViewerASCIIPrintf(v, ", "));
606 PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q * quad->Nc + c]));
607 }
608 if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, ")"));
609 PetscCall(PetscViewerASCIIPrintf(v, "\n"));
610 PetscCall(PetscViewerASCIIUseTabs(v, PETSC_TRUE));
611 }
612 PetscFunctionReturn(PETSC_SUCCESS);
613 }
614
615 /*@
616 PetscQuadratureView - View a `PetscQuadrature` object
617
618 Collective
619
620 Input Parameters:
621 + quad - The `PetscQuadrature` object
622 - viewer - The `PetscViewer` object
623
624 Level: beginner
625
626 .seealso: `PetscQuadrature`, `PetscViewer`, `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
627 @*/
PetscQuadratureView(PetscQuadrature quad,PetscViewer viewer)628 PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer)
629 {
630 PetscBool isascii;
631
632 PetscFunctionBegin;
633 PetscValidHeader(quad, 1);
634 if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
635 if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)quad), &viewer));
636 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
637 PetscCall(PetscViewerASCIIPushTab(viewer));
638 if (isascii) PetscCall(PetscQuadratureView_Ascii(quad, viewer));
639 PetscCall(PetscViewerASCIIPopTab(viewer));
640 PetscFunctionReturn(PETSC_SUCCESS);
641 }
642
643 /*@C
644 PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement
645
646 Not Collective; No Fortran Support
647
648 Input Parameters:
649 + q - The original `PetscQuadrature`
650 . numSubelements - The number of subelements the original element is divided into
651 . v0 - An array of the initial points for each subelement
652 - jac - An array of the Jacobian mappings from the reference to each subelement
653
654 Output Parameter:
655 . qref - The dimension
656
657 Level: intermediate
658
659 Note:
660 Together `v0` and `jac` define an affine mapping from the original reference element to each subelement
661
662 .seealso: `PetscQuadrature`, `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
663 @*/
PetscQuadratureExpandComposite(PetscQuadrature q,PetscInt numSubelements,const PetscReal v0[],const PetscReal jac[],PetscQuadrature * qref)664 PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref)
665 {
666 DMPolytopeType ct;
667 const PetscReal *points, *weights;
668 PetscReal *pointsRef, *weightsRef;
669 PetscInt dim, Nc, order, npoints, npointsRef, c, p, cp, d, e;
670
671 PetscFunctionBegin;
672 PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
673 PetscAssertPointer(v0, 3);
674 PetscAssertPointer(jac, 4);
675 PetscAssertPointer(qref, 5);
676 PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, qref));
677 PetscCall(PetscQuadratureGetCellType(q, &ct));
678 PetscCall(PetscQuadratureGetOrder(q, &order));
679 PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights));
680 npointsRef = npoints * numSubelements;
681 PetscCall(PetscMalloc1(npointsRef * dim, &pointsRef));
682 PetscCall(PetscMalloc1(npointsRef * Nc, &weightsRef));
683 for (c = 0; c < numSubelements; ++c) {
684 for (p = 0; p < npoints; ++p) {
685 for (d = 0; d < dim; ++d) {
686 pointsRef[(c * npoints + p) * dim + d] = v0[c * dim + d];
687 for (e = 0; e < dim; ++e) pointsRef[(c * npoints + p) * dim + d] += jac[(c * dim + d) * dim + e] * (points[p * dim + e] + 1.0);
688 }
689 /* Could also use detJ here */
690 for (cp = 0; cp < Nc; ++cp) weightsRef[(c * npoints + p) * Nc + cp] = weights[p * Nc + cp] / numSubelements;
691 }
692 }
693 PetscCall(PetscQuadratureSetCellType(*qref, ct));
694 PetscCall(PetscQuadratureSetOrder(*qref, order));
695 PetscCall(PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef));
696 PetscFunctionReturn(PETSC_SUCCESS);
697 }
698
699 /* Compute the coefficients for the Jacobi polynomial recurrence,
700
701 J^{a,b}_n(x) = (cnm1 + cnm1x * x) * J^{a,b}_{n-1}(x) - cnm2 * J^{a,b}_{n-2}(x).
702 */
703 #define PetscDTJacobiRecurrence_Internal(n, a, b, cnm1, cnm1x, cnm2) \
704 do { \
705 PetscReal _a = (a); \
706 PetscReal _b = (b); \
707 PetscReal _n = (n); \
708 if (n == 1) { \
709 (cnm1) = (_a - _b) * 0.5; \
710 (cnm1x) = (_a + _b + 2.) * 0.5; \
711 (cnm2) = 0.; \
712 } else { \
713 PetscReal _2n = _n + _n; \
714 PetscReal _d = (_2n * (_n + _a + _b) * (_2n + _a + _b - 2)); \
715 PetscReal _n1 = (_2n + _a + _b - 1.) * (_a * _a - _b * _b); \
716 PetscReal _n1x = (_2n + _a + _b - 1.) * (_2n + _a + _b) * (_2n + _a + _b - 2); \
717 PetscReal _n2 = 2. * ((_n + _a - 1.) * (_n + _b - 1.) * (_2n + _a + _b)); \
718 (cnm1) = _n1 / _d; \
719 (cnm1x) = _n1x / _d; \
720 (cnm2) = _n2 / _d; \
721 } \
722 } while (0)
723
724 /*@
725 PetscDTJacobiNorm - Compute the weighted L2 norm of a Jacobi polynomial.
726
727 $\| P^{\alpha,\beta}_n \|_{\alpha,\beta}^2 = \int_{-1}^1 (1 + x)^{\alpha} (1 - x)^{\beta} P^{\alpha,\beta}_n (x)^2 dx.$
728
729 Input Parameters:
730 + alpha - the left exponent > -1
731 . beta - the right exponent > -1
732 - n - the polynomial degree
733
734 Output Parameter:
735 . norm - the weighted L2 norm
736
737 Level: beginner
738
739 .seealso: `PetscQuadrature`, `PetscDTJacobiEval()`
740 @*/
PetscDTJacobiNorm(PetscReal alpha,PetscReal beta,PetscInt n,PetscReal * norm)741 PetscErrorCode PetscDTJacobiNorm(PetscReal alpha, PetscReal beta, PetscInt n, PetscReal *norm)
742 {
743 PetscReal twoab1;
744 PetscReal gr;
745
746 PetscFunctionBegin;
747 PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent alpha %g <= -1. invalid", (double)alpha);
748 PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent beta %g <= -1. invalid", (double)beta);
749 PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "n %" PetscInt_FMT " < 0 invalid", n);
750 twoab1 = PetscPowReal(2., alpha + beta + 1.);
751 #if defined(PETSC_HAVE_LGAMMA)
752 if (!n) {
753 gr = PetscExpReal(PetscLGamma(alpha + 1.) + PetscLGamma(beta + 1.) - PetscLGamma(alpha + beta + 2.));
754 } else {
755 gr = PetscExpReal(PetscLGamma(n + alpha + 1.) + PetscLGamma(n + beta + 1.) - (PetscLGamma(n + 1.) + PetscLGamma(n + alpha + beta + 1.))) / (n + n + alpha + beta + 1.);
756 }
757 #else
758 {
759 PetscInt alphai = (PetscInt)alpha;
760 PetscInt betai = (PetscInt)beta;
761 PetscInt i;
762
763 gr = n ? (1. / (n + n + alpha + beta + 1.)) : 1.;
764 if ((PetscReal)alphai == alpha) {
765 if (!n) {
766 for (i = 0; i < alphai; i++) gr *= (i + 1.) / (beta + i + 1.);
767 gr /= (alpha + beta + 1.);
768 } else {
769 for (i = 0; i < alphai; i++) gr *= (n + i + 1.) / (n + beta + i + 1.);
770 }
771 } else if ((PetscReal)betai == beta) {
772 if (!n) {
773 for (i = 0; i < betai; i++) gr *= (i + 1.) / (alpha + i + 2.);
774 gr /= (alpha + beta + 1.);
775 } else {
776 for (i = 0; i < betai; i++) gr *= (n + i + 1.) / (n + alpha + i + 1.);
777 }
778 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
779 }
780 #endif
781 *norm = PetscSqrtReal(twoab1 * gr);
782 PetscFunctionReturn(PETSC_SUCCESS);
783 }
784
PetscDTJacobiEval_Internal(PetscInt npoints,PetscReal a,PetscReal b,PetscInt k,const PetscReal * points,PetscInt ndegree,const PetscInt * degrees,PetscReal * p)785 static PetscErrorCode PetscDTJacobiEval_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscInt k, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *p)
786 {
787 PetscReal ak, bk;
788 PetscReal abk1;
789 PetscInt i, l, maxdegree;
790
791 PetscFunctionBegin;
792 maxdegree = degrees[ndegree - 1] - k;
793 ak = a + k;
794 bk = b + k;
795 abk1 = a + b + k + 1.;
796 if (maxdegree < 0) {
797 for (i = 0; i < npoints; i++)
798 for (l = 0; l < ndegree; l++) p[i * ndegree + l] = 0.;
799 PetscFunctionReturn(PETSC_SUCCESS);
800 }
801 for (i = 0; i < npoints; i++) {
802 PetscReal pm1, pm2, x;
803 PetscReal cnm1, cnm1x, cnm2;
804 PetscInt j, m;
805
806 x = points[i];
807 pm2 = 1.;
808 PetscDTJacobiRecurrence_Internal(1, ak, bk, cnm1, cnm1x, cnm2);
809 pm1 = (cnm1 + cnm1x * x);
810 l = 0;
811 while (l < ndegree && degrees[l] - k < 0) p[l++] = 0.;
812 while (l < ndegree && degrees[l] - k == 0) {
813 p[l] = pm2;
814 for (m = 0; m < k; m++) p[l] *= (abk1 + m) * 0.5;
815 l++;
816 }
817 while (l < ndegree && degrees[l] - k == 1) {
818 p[l] = pm1;
819 for (m = 0; m < k; m++) p[l] *= (abk1 + 1 + m) * 0.5;
820 l++;
821 }
822 for (j = 2; j <= maxdegree; j++) {
823 PetscReal pp;
824
825 PetscDTJacobiRecurrence_Internal(j, ak, bk, cnm1, cnm1x, cnm2);
826 pp = (cnm1 + cnm1x * x) * pm1 - cnm2 * pm2;
827 pm2 = pm1;
828 pm1 = pp;
829 while (l < ndegree && degrees[l] - k == j) {
830 p[l] = pp;
831 for (m = 0; m < k; m++) p[l] *= (abk1 + j + m) * 0.5;
832 l++;
833 }
834 }
835 p += ndegree;
836 }
837 PetscFunctionReturn(PETSC_SUCCESS);
838 }
839
840 /*@
841 PetscDTJacobiEvalJet - Evaluate the jet (function and derivatives) of the Jacobi polynomials basis up to a given degree.
842
843 Input Parameters:
844 + alpha - the left exponent of the weight
845 . beta - the right exponetn of the weight
846 . npoints - the number of points to evaluate the polynomials at
847 . points - [npoints] array of point coordinates
848 . degree - the maximm degree polynomial space to evaluate, (degree + 1) will be evaluated total.
849 - k - the maximum derivative to evaluate in the jet, (k + 1) will be evaluated total.
850
851 Output Parameter:
852 . p - an array containing the evaluations of the Jacobi polynomials's jets on the points. the size is (degree + 1) x
853 (k + 1) x npoints, which also describes the order of the dimensions of this three-dimensional array: the first
854 (slowest varying) dimension is polynomial degree; the second dimension is derivative order; the third (fastest
855 varying) dimension is the index of the evaluation point.
856
857 Level: advanced
858
859 Notes:
860 The Jacobi polynomials with indices $\alpha$ and $\beta$ are orthogonal with respect to the
861 weighted inner product $\langle f, g \rangle = \int_{-1}^1 (1+x)^{\alpha} (1-x)^{\beta} f(x)
862 g(x) dx$.
863
864 .seealso: `PetscDTJacobiEval()`, `PetscDTPKDEvalJet()`
865 @*/
PetscDTJacobiEvalJet(PetscReal alpha,PetscReal beta,PetscInt npoints,const PetscReal points[],PetscInt degree,PetscInt k,PetscReal p[])866 PetscErrorCode PetscDTJacobiEvalJet(PetscReal alpha, PetscReal beta, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
867 {
868 PetscInt i, j, l;
869 PetscInt *degrees;
870 PetscReal *psingle;
871
872 PetscFunctionBegin;
873 if (degree == 0) {
874 PetscInt zero = 0;
875
876 for (i = 0; i <= k; i++) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, 1, &zero, &p[i * npoints]));
877 PetscFunctionReturn(PETSC_SUCCESS);
878 }
879 PetscCall(PetscMalloc1(degree + 1, °rees));
880 PetscCall(PetscMalloc1((degree + 1) * npoints, &psingle));
881 for (i = 0; i <= degree; i++) degrees[i] = i;
882 for (i = 0; i <= k; i++) {
883 PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, degree + 1, degrees, psingle));
884 for (j = 0; j <= degree; j++) {
885 for (l = 0; l < npoints; l++) p[(j * (k + 1) + i) * npoints + l] = psingle[l * (degree + 1) + j];
886 }
887 }
888 PetscCall(PetscFree(psingle));
889 PetscCall(PetscFree(degrees));
890 PetscFunctionReturn(PETSC_SUCCESS);
891 }
892
893 /*@
894 PetscDTJacobiEval - evaluate Jacobi polynomials for the weight function $(1.+x)^{\alpha} (1.-x)^{\beta}$ at a set of points
895 at points
896
897 Not Collective
898
899 Input Parameters:
900 + npoints - number of spatial points to evaluate at
901 . alpha - the left exponent > -1
902 . beta - the right exponent > -1
903 . points - array of locations to evaluate at
904 . ndegree - number of basis degrees to evaluate
905 - degrees - sorted array of degrees to evaluate
906
907 Output Parameters:
908 + B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or `NULL`)
909 . D - row-oriented derivative evaluation matrix (or `NULL`)
910 - D2 - row-oriented second derivative evaluation matrix (or `NULL`)
911
912 Level: intermediate
913
914 .seealso: `PetscDTGaussQuadrature()`, `PetscDTLegendreEval()`
915 @*/
PetscDTJacobiEval(PetscInt npoints,PetscReal alpha,PetscReal beta,const PetscReal * points,PetscInt ndegree,const PetscInt * degrees,PeOp PetscReal B[],PeOp PetscReal D[],PeOp PetscReal D2[])916 PetscErrorCode PetscDTJacobiEval(PetscInt npoints, PetscReal alpha, PetscReal beta, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PeOp PetscReal B[], PeOp PetscReal D[], PeOp PetscReal D2[])
917 {
918 PetscFunctionBegin;
919 PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
920 PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
921 if (!npoints || !ndegree) PetscFunctionReturn(PETSC_SUCCESS);
922 if (B) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 0, points, ndegree, degrees, B));
923 if (D) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 1, points, ndegree, degrees, D));
924 if (D2) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 2, points, ndegree, degrees, D2));
925 PetscFunctionReturn(PETSC_SUCCESS);
926 }
927
928 /*@
929 PetscDTLegendreEval - evaluate Legendre polynomials at points
930
931 Not Collective
932
933 Input Parameters:
934 + npoints - number of spatial points to evaluate at
935 . points - array of locations to evaluate at
936 . ndegree - number of basis degrees to evaluate
937 - degrees - sorted array of degrees to evaluate
938
939 Output Parameters:
940 + B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension `npoints`*`ndegrees`, allocated by caller) (or `NULL`)
941 . D - row-oriented derivative evaluation matrix (or `NULL`)
942 - D2 - row-oriented second derivative evaluation matrix (or `NULL`)
943
944 Level: intermediate
945
946 .seealso: `PetscDTGaussQuadrature()`
947 @*/
PetscDTLegendreEval(PetscInt npoints,const PetscReal * points,PetscInt ndegree,const PetscInt * degrees,PeOp PetscReal B[],PeOp PetscReal D[],PeOp PetscReal D2[])948 PetscErrorCode PetscDTLegendreEval(PetscInt npoints, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PeOp PetscReal B[], PeOp PetscReal D[], PeOp PetscReal D2[])
949 {
950 PetscFunctionBegin;
951 PetscCall(PetscDTJacobiEval(npoints, 0., 0., points, ndegree, degrees, B, D, D2));
952 PetscFunctionReturn(PETSC_SUCCESS);
953 }
954
955 /*@
956 PetscDTIndexToGradedOrder - convert an index into a tuple of monomial degrees in a graded order (that is, if the degree sum of tuple x is less than the degree sum of tuple y,
957 then the index of x is smaller than the index of y)
958
959 Input Parameters:
960 + len - the desired length of the degree tuple
961 - index - the index to convert: should be >= 0
962
963 Output Parameter:
964 . degtup - filled with a tuple of degrees
965
966 Level: beginner
967
968 Note:
969 For two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
970 acts as a tiebreaker. For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
971 last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).
972
973 .seealso: `PetscDTGradedOrderToIndex()`
974 @*/
PetscDTIndexToGradedOrder(PetscInt len,PetscInt index,PetscInt degtup[])975 PetscErrorCode PetscDTIndexToGradedOrder(PetscInt len, PetscInt index, PetscInt degtup[])
976 {
977 PetscInt i, total;
978 PetscInt sum;
979
980 PetscFunctionBeginHot;
981 PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
982 PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
983 total = 1;
984 sum = 0;
985 while (index >= total) {
986 index -= total;
987 total = (total * (len + sum)) / (sum + 1);
988 sum++;
989 }
990 for (i = 0; i < len; i++) {
991 PetscInt c;
992
993 degtup[i] = sum;
994 for (c = 0, total = 1; c < sum; c++) {
995 /* going into the loop, total is the number of way to have a tuple of sum exactly c with length len - 1 - i */
996 if (index < total) break;
997 index -= total;
998 total = (total * (len - 1 - i + c)) / (c + 1);
999 degtup[i]--;
1000 }
1001 sum -= degtup[i];
1002 }
1003 PetscFunctionReturn(PETSC_SUCCESS);
1004 }
1005
1006 /*@
1007 PetscDTGradedOrderToIndex - convert a tuple into an index in a graded order, the inverse of `PetscDTIndexToGradedOrder()`.
1008
1009 Input Parameters:
1010 + len - the length of the degree tuple
1011 - degtup - tuple with this length
1012
1013 Output Parameter:
1014 . index - index in graded order: >= 0
1015
1016 Level: beginner
1017
1018 Note:
1019 For two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
1020 acts as a tiebreaker. For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
1021 last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).
1022
1023 .seealso: `PetscDTIndexToGradedOrder()`
1024 @*/
PetscDTGradedOrderToIndex(PetscInt len,const PetscInt degtup[],PetscInt * index)1025 PetscErrorCode PetscDTGradedOrderToIndex(PetscInt len, const PetscInt degtup[], PetscInt *index)
1026 {
1027 PetscInt i, idx, sum, total;
1028
1029 PetscFunctionBeginHot;
1030 PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
1031 for (i = 0, sum = 0; i < len; i++) sum += degtup[i];
1032 idx = 0;
1033 total = 1;
1034 for (i = 0; i < sum; i++) {
1035 idx += total;
1036 total = (total * (len + i)) / (i + 1);
1037 }
1038 for (i = 0; i < len - 1; i++) {
1039 PetscInt c;
1040
1041 total = 1;
1042 sum -= degtup[i];
1043 for (c = 0; c < sum; c++) {
1044 idx += total;
1045 total = (total * (len - 1 - i + c)) / (c + 1);
1046 }
1047 }
1048 *index = idx;
1049 PetscFunctionReturn(PETSC_SUCCESS);
1050 }
1051
1052 static PetscBool PKDCite = PETSC_FALSE;
1053 const char PKDCitation[] = "@article{Kirby2010,\n"
1054 " title={Singularity-free evaluation of collapsed-coordinate orthogonal polynomials},\n"
1055 " author={Kirby, Robert C},\n"
1056 " journal={ACM Transactions on Mathematical Software (TOMS)},\n"
1057 " volume={37},\n"
1058 " number={1},\n"
1059 " pages={1--16},\n"
1060 " year={2010},\n"
1061 " publisher={ACM New York, NY, USA}\n}\n";
1062
1063 /*@
1064 PetscDTPKDEvalJet - Evaluate the jet (function and derivatives) of the Proriol-Koornwinder-Dubiner (PKD) basis for
1065 the space of polynomials up to a given degree.
1066
1067 Input Parameters:
1068 + dim - the number of variables in the multivariate polynomials
1069 . npoints - the number of points to evaluate the polynomials at
1070 . points - [npoints x dim] array of point coordinates
1071 . degree - the degree (sum of degrees on the variables in a monomial) of the polynomial space to evaluate. There are ((dim + degree) choose dim) polynomials in this space.
1072 - k - the maximum order partial derivative to evaluate in the jet. There are (dim + k choose dim) partial derivatives
1073 in the jet. Choosing k = 0 means to evaluate just the function and no derivatives
1074
1075 Output Parameter:
1076 . p - an array containing the evaluations of the PKD polynomials' jets on the points. The size is ((dim + degree)
1077 choose dim) x ((dim + k) choose dim) x npoints, which also describes the order of the dimensions of this
1078 three-dimensional array: the first (slowest varying) dimension is basis function index; the second dimension is jet
1079 index; the third (fastest varying) dimension is the index of the evaluation point.
1080
1081 Level: advanced
1082
1083 Notes:
1084 The PKD basis is L2-orthonormal on the biunit simplex (which is used as the reference element
1085 for finite elements in PETSc), which makes it a stable basis to use for evaluating
1086 polynomials in that domain.
1087
1088 The ordering of the basis functions, and the ordering of the derivatives in the jet, both follow the graded
1089 ordering of `PetscDTIndexToGradedOrder()` and `PetscDTGradedOrderToIndex()`. For example, in 3D, the polynomial with
1090 leading monomial x^2,y^0,z^1, which has degree tuple (2,0,1), which by `PetscDTGradedOrderToIndex()` has index 12 (it is the 13th basis function in the space);
1091 the partial derivative $\partial_x \partial_z$ has order tuple (1,0,1), appears at index 6 in the jet (it is the 7th partial derivative in the jet).
1092
1093 The implementation uses Kirby's singularity-free evaluation algorithm, <https://doi.org/10.1145/1644001.1644006>.
1094
1095 .seealso: `PetscDTGradedOrderToIndex()`, `PetscDTIndexToGradedOrder()`, `PetscDTJacobiEvalJet()`
1096 @*/
PetscDTPKDEvalJet(PetscInt dim,PetscInt npoints,const PetscReal points[],PetscInt degree,PetscInt k,PetscReal p[])1097 PetscErrorCode PetscDTPKDEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
1098 {
1099 PetscInt degidx, kidx, d, pt;
1100 PetscInt Nk, Ndeg;
1101 PetscInt *ktup, *degtup;
1102 PetscReal *scales, initscale, scaleexp;
1103
1104 PetscFunctionBegin;
1105 PetscCall(PetscCitationsRegister(PKDCitation, &PKDCite));
1106 PetscCall(PetscDTBinomialInt(dim + k, k, &Nk));
1107 PetscCall(PetscDTBinomialInt(degree + dim, degree, &Ndeg));
1108 PetscCall(PetscMalloc2(dim, °tup, dim, &ktup));
1109 PetscCall(PetscMalloc1(Ndeg, &scales));
1110 initscale = 1.;
1111 if (dim > 1) {
1112 PetscCall(PetscDTBinomial(dim, 2, &scaleexp));
1113 initscale = PetscPowReal(2., scaleexp * 0.5);
1114 }
1115 for (degidx = 0; degidx < Ndeg; degidx++) {
1116 PetscInt e, i;
1117 PetscInt m1idx = -1, m2idx = -1;
1118 PetscInt n;
1119 PetscInt degsum;
1120 PetscReal alpha;
1121 PetscReal cnm1, cnm1x, cnm2;
1122 PetscReal norm;
1123
1124 PetscCall(PetscDTIndexToGradedOrder(dim, degidx, degtup));
1125 for (d = dim - 1; d >= 0; d--)
1126 if (degtup[d]) break;
1127 if (d < 0) { /* constant is 1 everywhere, all derivatives are zero */
1128 scales[degidx] = initscale;
1129 for (e = 0; e < dim; e++) {
1130 PetscCall(PetscDTJacobiNorm(e, 0., 0, &norm));
1131 scales[degidx] /= norm;
1132 }
1133 for (i = 0; i < npoints; i++) p[degidx * Nk * npoints + i] = 1.;
1134 for (i = 0; i < (Nk - 1) * npoints; i++) p[(degidx * Nk + 1) * npoints + i] = 0.;
1135 continue;
1136 }
1137 n = degtup[d];
1138 degtup[d]--;
1139 PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m1idx));
1140 if (degtup[d] > 0) {
1141 degtup[d]--;
1142 PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m2idx));
1143 degtup[d]++;
1144 }
1145 degtup[d]++;
1146 for (e = 0, degsum = 0; e < d; e++) degsum += degtup[e];
1147 alpha = 2 * degsum + d;
1148 PetscDTJacobiRecurrence_Internal(n, alpha, 0., cnm1, cnm1x, cnm2);
1149
1150 scales[degidx] = initscale;
1151 for (e = 0, degsum = 0; e < dim; e++) {
1152 PetscInt f;
1153 PetscReal ealpha;
1154 PetscReal enorm;
1155
1156 ealpha = 2 * degsum + e;
1157 for (f = 0; f < degsum; f++) scales[degidx] *= 2.;
1158 PetscCall(PetscDTJacobiNorm(ealpha, 0., degtup[e], &enorm));
1159 scales[degidx] /= enorm;
1160 degsum += degtup[e];
1161 }
1162
1163 for (pt = 0; pt < npoints; pt++) {
1164 /* compute the multipliers */
1165 PetscReal thetanm1, thetanm1x, thetanm2;
1166
1167 thetanm1x = dim - (d + 1) + 2. * points[pt * dim + d];
1168 for (e = d + 1; e < dim; e++) thetanm1x += points[pt * dim + e];
1169 thetanm1x *= 0.5;
1170 thetanm1 = (2. - (dim - (d + 1)));
1171 for (e = d + 1; e < dim; e++) thetanm1 -= points[pt * dim + e];
1172 thetanm1 *= 0.5;
1173 thetanm2 = thetanm1 * thetanm1;
1174
1175 for (kidx = 0; kidx < Nk; kidx++) {
1176 PetscInt f;
1177
1178 PetscCall(PetscDTIndexToGradedOrder(dim, kidx, ktup));
1179 /* first sum in the same derivative terms */
1180 p[(degidx * Nk + kidx) * npoints + pt] = (cnm1 * thetanm1 + cnm1x * thetanm1x) * p[(m1idx * Nk + kidx) * npoints + pt];
1181 if (m2idx >= 0) p[(degidx * Nk + kidx) * npoints + pt] -= cnm2 * thetanm2 * p[(m2idx * Nk + kidx) * npoints + pt];
1182
1183 for (f = d; f < dim; f++) {
1184 PetscInt km1idx, mplty = ktup[f];
1185
1186 if (!mplty) continue;
1187 ktup[f]--;
1188 PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km1idx));
1189
1190 /* the derivative of cnm1x * thetanm1x wrt x variable f is 0.5 * cnm1x if f > d otherwise it is cnm1x */
1191 /* the derivative of cnm1 * thetanm1 wrt x variable f is 0 if f == d, otherwise it is -0.5 * cnm1 */
1192 /* the derivative of -cnm2 * thetanm2 wrt x variable f is 0 if f == d, otherwise it is cnm2 * thetanm1 */
1193 if (f > d) {
1194 PetscInt f2;
1195
1196 p[(degidx * Nk + kidx) * npoints + pt] += mplty * 0.5 * (cnm1x - cnm1) * p[(m1idx * Nk + km1idx) * npoints + pt];
1197 if (m2idx >= 0) {
1198 p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm2 * thetanm1 * p[(m2idx * Nk + km1idx) * npoints + pt];
1199 /* second derivatives of -cnm2 * thetanm2 wrt x variable f,f2 is like - 0.5 * cnm2 */
1200 for (f2 = f; f2 < dim; f2++) {
1201 PetscInt km2idx, mplty2 = ktup[f2];
1202 PetscInt factor;
1203
1204 if (!mplty2) continue;
1205 ktup[f2]--;
1206 PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km2idx));
1207
1208 factor = mplty * mplty2;
1209 if (f == f2) factor /= 2;
1210 p[(degidx * Nk + kidx) * npoints + pt] -= 0.5 * factor * cnm2 * p[(m2idx * Nk + km2idx) * npoints + pt];
1211 ktup[f2]++;
1212 }
1213 }
1214 } else {
1215 p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm1x * p[(m1idx * Nk + km1idx) * npoints + pt];
1216 }
1217 ktup[f]++;
1218 }
1219 }
1220 }
1221 }
1222 for (degidx = 0; degidx < Ndeg; degidx++) {
1223 PetscReal scale = scales[degidx];
1224 PetscInt i;
1225
1226 for (i = 0; i < Nk * npoints; i++) p[degidx * Nk * npoints + i] *= scale;
1227 }
1228 PetscCall(PetscFree(scales));
1229 PetscCall(PetscFree2(degtup, ktup));
1230 PetscFunctionReturn(PETSC_SUCCESS);
1231 }
1232
1233 /*@
1234 PetscDTPTrimmedSize - The size of the trimmed polynomial space of k-forms with a given degree and form degree,
1235 which can be evaluated in `PetscDTPTrimmedEvalJet()`.
1236
1237 Input Parameters:
1238 + dim - the number of variables in the multivariate polynomials
1239 . degree - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space.
1240 - formDegree - the degree of the form
1241
1242 Output Parameter:
1243 . size - The number ((`dim` + `degree`) choose (`dim` + `formDegree`)) x ((`degree` + `formDegree` - 1) choose (`formDegree`))
1244
1245 Level: advanced
1246
1247 .seealso: `PetscDTPTrimmedEvalJet()`
1248 @*/
PetscDTPTrimmedSize(PetscInt dim,PetscInt degree,PetscInt formDegree,PetscInt * size)1249 PetscErrorCode PetscDTPTrimmedSize(PetscInt dim, PetscInt degree, PetscInt formDegree, PetscInt *size)
1250 {
1251 PetscInt Nrk, Nbpt; // number of trimmed polynomials
1252
1253 PetscFunctionBegin;
1254 formDegree = PetscAbsInt(formDegree);
1255 PetscCall(PetscDTBinomialInt(degree + dim, degree + formDegree, &Nbpt));
1256 PetscCall(PetscDTBinomialInt(degree + formDegree - 1, formDegree, &Nrk));
1257 Nbpt *= Nrk;
1258 *size = Nbpt;
1259 PetscFunctionReturn(PETSC_SUCCESS);
1260 }
1261
1262 /* there was a reference implementation based on section 4.4 of Arnold, Falk & Winther (acta numerica, 2006), but it
1263 * was inferior to this implementation */
PetscDTPTrimmedEvalJet_Internal(PetscInt dim,PetscInt npoints,const PetscReal points[],PetscInt degree,PetscInt formDegree,PetscInt jetDegree,PetscReal p[])1264 static PetscErrorCode PetscDTPTrimmedEvalJet_Internal(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
1265 {
1266 PetscInt formDegreeOrig = formDegree;
1267 PetscBool formNegative = (formDegreeOrig < 0) ? PETSC_TRUE : PETSC_FALSE;
1268
1269 PetscFunctionBegin;
1270 formDegree = PetscAbsInt(formDegreeOrig);
1271 if (formDegree == 0) {
1272 PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree, jetDegree, p));
1273 PetscFunctionReturn(PETSC_SUCCESS);
1274 }
1275 if (formDegree == dim) {
1276 PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p));
1277 PetscFunctionReturn(PETSC_SUCCESS);
1278 }
1279 PetscInt Nbpt;
1280 PetscCall(PetscDTPTrimmedSize(dim, degree, formDegree, &Nbpt));
1281 PetscInt Nf;
1282 PetscCall(PetscDTBinomialInt(dim, formDegree, &Nf));
1283 PetscInt Nk;
1284 PetscCall(PetscDTBinomialInt(dim + jetDegree, dim, &Nk));
1285 PetscCall(PetscArrayzero(p, Nbpt * Nf * Nk * npoints));
1286
1287 PetscInt Nbpm1; // number of scalar polynomials up to degree - 1;
1288 PetscCall(PetscDTBinomialInt(dim + degree - 1, dim, &Nbpm1));
1289 PetscReal *p_scalar;
1290 PetscCall(PetscMalloc1(Nbpm1 * Nk * npoints, &p_scalar));
1291 PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p_scalar));
1292 PetscInt total = 0;
1293 // First add the full polynomials up to degree - 1 into the basis: take the scalar
1294 // and copy one for each form component
1295 for (PetscInt i = 0; i < Nbpm1; i++) {
1296 const PetscReal *src = &p_scalar[i * Nk * npoints];
1297 for (PetscInt f = 0; f < Nf; f++) {
1298 PetscReal *dest = &p[(total++ * Nf + f) * Nk * npoints];
1299 PetscCall(PetscArraycpy(dest, src, Nk * npoints));
1300 }
1301 }
1302 PetscInt *form_atoms;
1303 PetscCall(PetscMalloc1(formDegree + 1, &form_atoms));
1304 // construct the interior product pattern
1305 PetscInt (*pattern)[3];
1306 PetscInt Nf1; // number of formDegree + 1 forms
1307 PetscCall(PetscDTBinomialInt(dim, formDegree + 1, &Nf1));
1308 PetscInt nnz = Nf1 * (formDegree + 1);
1309 PetscCall(PetscMalloc1(Nf1 * (formDegree + 1), &pattern));
1310 PetscCall(PetscDTAltVInteriorPattern(dim, formDegree + 1, pattern));
1311 PetscReal centroid = (1. - dim) / (dim + 1.);
1312 PetscInt *deriv;
1313 PetscCall(PetscMalloc1(dim, &deriv));
1314 for (PetscInt d = dim; d >= formDegree + 1; d--) {
1315 PetscInt Nfd1; // number of formDegree + 1 forms in dimension d that include dx_0
1316 // (equal to the number of formDegree forms in dimension d-1)
1317 PetscCall(PetscDTBinomialInt(d - 1, formDegree, &Nfd1));
1318 // The number of homogeneous (degree-1) scalar polynomials in d variables
1319 PetscInt Nh;
1320 PetscCall(PetscDTBinomialInt(d - 1 + degree - 1, d - 1, &Nh));
1321 const PetscReal *h_scalar = &p_scalar[(Nbpm1 - Nh) * Nk * npoints];
1322 for (PetscInt b = 0; b < Nh; b++) {
1323 const PetscReal *h_s = &h_scalar[b * Nk * npoints];
1324 for (PetscInt f = 0; f < Nfd1; f++) {
1325 // construct all formDegree+1 forms that start with dx_(dim - d) /\ ...
1326 form_atoms[0] = dim - d;
1327 PetscCall(PetscDTEnumSubset(d - 1, formDegree, f, &form_atoms[1]));
1328 for (PetscInt i = 0; i < formDegree; i++) form_atoms[1 + i] += form_atoms[0] + 1;
1329 PetscInt f_ind; // index of the resulting form
1330 PetscCall(PetscDTSubsetIndex(dim, formDegree + 1, form_atoms, &f_ind));
1331 PetscReal *p_f = &p[total++ * Nf * Nk * npoints];
1332 for (PetscInt nz = 0; nz < nnz; nz++) {
1333 PetscInt i = pattern[nz][0]; // formDegree component
1334 PetscInt j = pattern[nz][1]; // (formDegree + 1) component
1335 PetscInt v = pattern[nz][2]; // coordinate component
1336 PetscReal scale = v < 0 ? -1. : 1.;
1337
1338 i = formNegative ? (Nf - 1 - i) : i;
1339 scale = (formNegative && (i & 1)) ? -scale : scale;
1340 v = v < 0 ? -(v + 1) : v;
1341 if (j != f_ind) continue;
1342 PetscReal *p_i = &p_f[i * Nk * npoints];
1343 for (PetscInt jet = 0; jet < Nk; jet++) {
1344 const PetscReal *h_jet = &h_s[jet * npoints];
1345 PetscReal *p_jet = &p_i[jet * npoints];
1346
1347 for (PetscInt pt = 0; pt < npoints; pt++) p_jet[pt] += scale * h_jet[pt] * (points[pt * dim + v] - centroid);
1348 PetscCall(PetscDTIndexToGradedOrder(dim, jet, deriv));
1349 deriv[v]++;
1350 PetscReal mult = deriv[v];
1351 PetscInt l;
1352 PetscCall(PetscDTGradedOrderToIndex(dim, deriv, &l));
1353 if (l >= Nk) continue;
1354 p_jet = &p_i[l * npoints];
1355 for (PetscInt pt = 0; pt < npoints; pt++) p_jet[pt] += scale * mult * h_jet[pt];
1356 deriv[v]--;
1357 }
1358 }
1359 }
1360 }
1361 }
1362 PetscCheck(total == Nbpt, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Incorrectly counted P trimmed polynomials");
1363 PetscCall(PetscFree(deriv));
1364 PetscCall(PetscFree(pattern));
1365 PetscCall(PetscFree(form_atoms));
1366 PetscCall(PetscFree(p_scalar));
1367 PetscFunctionReturn(PETSC_SUCCESS);
1368 }
1369
1370 /*@
1371 PetscDTPTrimmedEvalJet - Evaluate the jet (function and derivatives) of a basis of the trimmed polynomial k-forms up to
1372 a given degree.
1373
1374 Input Parameters:
1375 + dim - the number of variables in the multivariate polynomials
1376 . npoints - the number of points to evaluate the polynomials at
1377 . points - [npoints x dim] array of point coordinates
1378 . degree - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space to evaluate.
1379 There are ((dim + degree) choose (dim + formDegree)) x ((degree + formDegree - 1) choose (formDegree)) polynomials in this space.
1380 (You can use `PetscDTPTrimmedSize()` to compute this size.)
1381 . formDegree - the degree of the form
1382 - jetDegree - the maximum order partial derivative to evaluate in the jet. There are ((dim + jetDegree) choose dim) partial derivatives
1383 in the jet. Choosing jetDegree = 0 means to evaluate just the function and no derivatives
1384
1385 Output Parameter:
1386 . p - an array containing the evaluations of the PKD polynomials' jets on the points.
1387
1388 Level: advanced
1389
1390 Notes:
1391 The size of `p` is `PetscDTPTrimmedSize()` x ((dim + formDegree) choose dim) x ((dim + k)
1392 choose dim) x npoints,which also describes the order of the dimensions of this
1393 four-dimensional array\:
1394
1395 the first (slowest varying) dimension is basis function index;
1396 the second dimension is component of the form;
1397 the third dimension is jet index;
1398 the fourth (fastest varying) dimension is the index of the evaluation point.
1399
1400 The ordering of the basis functions is not graded, so the basis functions are not nested by degree like `PetscDTPKDEvalJet()`.
1401 The basis functions are not an L2-orthonormal basis on any particular domain.
1402
1403 The implementation is based on the description of the trimmed polynomials up to degree r as
1404 the direct sum of polynomials up to degree (r-1) and the Koszul differential applied to
1405 homogeneous polynomials of degree (r-1).
1406
1407 .seealso: `PetscDTPKDEvalJet()`, `PetscDTPTrimmedSize()`
1408 @*/
PetscDTPTrimmedEvalJet(PetscInt dim,PetscInt npoints,const PetscReal points[],PetscInt degree,PetscInt formDegree,PetscInt jetDegree,PetscReal p[])1409 PetscErrorCode PetscDTPTrimmedEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
1410 {
1411 PetscFunctionBegin;
1412 PetscCall(PetscDTPTrimmedEvalJet_Internal(dim, npoints, points, degree, formDegree, jetDegree, p));
1413 PetscFunctionReturn(PETSC_SUCCESS);
1414 }
1415
1416 /* solve the symmetric tridiagonal eigenvalue system, writing the eigenvalues into eigs and the eigenvectors into V
1417 * with lds n; diag and subdiag are overwritten */
PetscDTSymmetricTridiagonalEigensolve(PetscInt n,PetscReal diag[],PetscReal subdiag[],PetscReal eigs[],PetscScalar V[])1418 static PetscErrorCode PetscDTSymmetricTridiagonalEigensolve(PetscInt n, PetscReal diag[], PetscReal subdiag[], PetscReal eigs[], PetscScalar V[])
1419 {
1420 char jobz = 'V'; /* eigenvalues and eigenvectors */
1421 char range = 'A'; /* all eigenvalues will be found */
1422 PetscReal VL = 0.; /* ignored because range is 'A' */
1423 PetscReal VU = 0.; /* ignored because range is 'A' */
1424 PetscBLASInt IL = 0; /* ignored because range is 'A' */
1425 PetscBLASInt IU = 0; /* ignored because range is 'A' */
1426 PetscReal abstol = 0.; /* unused */
1427 PetscBLASInt bn, bm, ldz; /* bm will equal bn on exit */
1428 PetscBLASInt *isuppz;
1429 PetscBLASInt lwork, liwork;
1430 PetscReal workquery;
1431 PetscBLASInt iworkquery;
1432 PetscBLASInt *iwork;
1433 PetscBLASInt info;
1434 PetscReal *work = NULL;
1435
1436 PetscFunctionBegin;
1437 #if !defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1438 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1439 #endif
1440 PetscCall(PetscBLASIntCast(n, &bn));
1441 PetscCall(PetscBLASIntCast(n, &ldz));
1442 #if !defined(PETSC_MISSING_LAPACK_STEGR)
1443 PetscCall(PetscMalloc1(2 * n, &isuppz));
1444 lwork = -1;
1445 liwork = -1;
1446 PetscCallBLAS("LAPACKstegr", LAPACKstegr_(&jobz, &range, &bn, diag, subdiag, &VL, &VU, &IL, &IU, &abstol, &bm, eigs, V, &ldz, isuppz, &workquery, &lwork, &iworkquery, &liwork, &info));
1447 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEGR error");
1448 lwork = (PetscBLASInt)workquery;
1449 liwork = iworkquery;
1450 PetscCall(PetscMalloc2(lwork, &work, liwork, &iwork));
1451 PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1452 PetscCallBLAS("LAPACKstegr", LAPACKstegr_(&jobz, &range, &bn, diag, subdiag, &VL, &VU, &IL, &IU, &abstol, &bm, eigs, V, &ldz, isuppz, work, &lwork, iwork, &liwork, &info));
1453 PetscCall(PetscFPTrapPop());
1454 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEGR error");
1455 PetscCall(PetscFree2(work, iwork));
1456 PetscCall(PetscFree(isuppz));
1457 #elif !defined(PETSC_MISSING_LAPACK_STEQR)
1458 jobz = 'I'; /* Compute eigenvalues and eigenvectors of the
1459 tridiagonal matrix. Z is initialized to the identity
1460 matrix. */
1461 PetscCall(PetscMalloc1(PetscMax(1, 2 * n - 2), &work));
1462 PetscCallBLAS("LAPACKsteqr", LAPACKsteqr_("I", &bn, diag, subdiag, V, &ldz, work, &info));
1463 PetscCall(PetscFPTrapPop());
1464 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEQR error");
1465 PetscCall(PetscFree(work));
1466 PetscCall(PetscArraycpy(eigs, diag, n));
1467 #endif
1468 PetscFunctionReturn(PETSC_SUCCESS);
1469 }
1470
1471 /* Formula for the weights at the endpoints (-1 and 1) of Gauss-Lobatto-Jacobi
1472 * quadrature rules on the interval [-1, 1] */
PetscDTGaussLobattoJacobiEndweights_Internal(PetscInt n,PetscReal alpha,PetscReal beta,PetscReal * leftw,PetscReal * rightw)1473 static PetscErrorCode PetscDTGaussLobattoJacobiEndweights_Internal(PetscInt n, PetscReal alpha, PetscReal beta, PetscReal *leftw, PetscReal *rightw)
1474 {
1475 PetscReal twoab1;
1476 PetscInt m = n - 2;
1477 PetscReal a = alpha + 1.;
1478 PetscReal b = beta + 1.;
1479 PetscReal gra, grb;
1480
1481 PetscFunctionBegin;
1482 twoab1 = PetscPowReal(2., a + b - 1.);
1483 #if defined(PETSC_HAVE_LGAMMA)
1484 grb = PetscExpReal(2. * PetscLGamma(b + 1.) + PetscLGamma(m + 1.) + PetscLGamma(m + a + 1.) - (PetscLGamma(m + b + 1) + PetscLGamma(m + a + b + 1.)));
1485 gra = PetscExpReal(2. * PetscLGamma(a + 1.) + PetscLGamma(m + 1.) + PetscLGamma(m + b + 1.) - (PetscLGamma(m + a + 1) + PetscLGamma(m + a + b + 1.)));
1486 #else
1487 {
1488 PetscReal binom1, binom2;
1489 PetscInt alphai = (PetscInt)alpha;
1490 PetscInt betai = (PetscInt)beta;
1491
1492 PetscCheck((PetscReal)alphai == alpha && (PetscReal)betai == beta, PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
1493 PetscCall(PetscDTBinomial(m + b, b, &binom1));
1494 PetscCall(PetscDTBinomial(m + a + b, b, &binom2));
1495 grb = 1. / (binom1 * binom2);
1496 PetscCall(PetscDTBinomial(m + a, a, &binom1));
1497 PetscCall(PetscDTBinomial(m + a + b, a, &binom2));
1498 gra = 1. / (binom1 * binom2);
1499 }
1500 #endif
1501 *leftw = twoab1 * grb / b;
1502 *rightw = twoab1 * gra / a;
1503 PetscFunctionReturn(PETSC_SUCCESS);
1504 }
1505
1506 /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
1507 Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
PetscDTComputeJacobi(PetscReal a,PetscReal b,PetscInt n,PetscReal x,PetscReal * P)1508 static inline PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
1509 {
1510 PetscReal pn1, pn2;
1511 PetscReal cnm1, cnm1x, cnm2;
1512 PetscInt k;
1513
1514 PetscFunctionBegin;
1515 if (!n) {
1516 *P = 1.0;
1517 PetscFunctionReturn(PETSC_SUCCESS);
1518 }
1519 PetscDTJacobiRecurrence_Internal(1, a, b, cnm1, cnm1x, cnm2);
1520 pn2 = 1.;
1521 pn1 = cnm1 + cnm1x * x;
1522 if (n == 1) {
1523 *P = pn1;
1524 PetscFunctionReturn(PETSC_SUCCESS);
1525 }
1526 *P = 0.0;
1527 for (k = 2; k < n + 1; ++k) {
1528 PetscDTJacobiRecurrence_Internal(k, a, b, cnm1, cnm1x, cnm2);
1529
1530 *P = (cnm1 + cnm1x * x) * pn1 - cnm2 * pn2;
1531 pn2 = pn1;
1532 pn1 = *P;
1533 }
1534 PetscFunctionReturn(PETSC_SUCCESS);
1535 }
1536
1537 /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
PetscDTComputeJacobiDerivative(PetscReal a,PetscReal b,PetscInt n,PetscReal x,PetscInt k,PetscReal * P)1538 static inline PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscInt k, PetscReal *P)
1539 {
1540 PetscReal nP;
1541 PetscInt i;
1542
1543 PetscFunctionBegin;
1544 *P = 0.0;
1545 if (k > n) PetscFunctionReturn(PETSC_SUCCESS);
1546 PetscCall(PetscDTComputeJacobi(a + k, b + k, n - k, x, &nP));
1547 for (i = 0; i < k; i++) nP *= (a + b + n + 1. + i) * 0.5;
1548 *P = nP;
1549 PetscFunctionReturn(PETSC_SUCCESS);
1550 }
1551
PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints,PetscReal a,PetscReal b,PetscReal x[],PetscReal w[])1552 static PetscErrorCode PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
1553 {
1554 PetscInt maxIter = 100;
1555 PetscReal eps = PetscExpReal(0.75 * PetscLogReal(PETSC_MACHINE_EPSILON));
1556 PetscReal a1, a6, gf;
1557 PetscInt k;
1558
1559 PetscFunctionBegin;
1560 a1 = PetscPowReal(2.0, a + b + 1);
1561 #if defined(PETSC_HAVE_LGAMMA)
1562 {
1563 PetscReal a2, a3, a4, a5;
1564 a2 = PetscLGamma(a + npoints + 1);
1565 a3 = PetscLGamma(b + npoints + 1);
1566 a4 = PetscLGamma(a + b + npoints + 1);
1567 a5 = PetscLGamma(npoints + 1);
1568 gf = PetscExpReal(a2 + a3 - (a4 + a5));
1569 }
1570 #else
1571 {
1572 PetscInt ia, ib;
1573
1574 ia = (PetscInt)a;
1575 ib = (PetscInt)b;
1576 gf = 1.;
1577 if (ia == a && ia >= 0) { /* compute ratio of rising factorals wrt a */
1578 for (k = 0; k < ia; k++) gf *= (npoints + 1. + k) / (npoints + b + 1. + k);
1579 } else if (b == b && ib >= 0) { /* compute ratio of rising factorials wrt b */
1580 for (k = 0; k < ib; k++) gf *= (npoints + 1. + k) / (npoints + a + 1. + k);
1581 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
1582 }
1583 #endif
1584
1585 a6 = a1 * gf;
1586 /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
1587 Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
1588 for (k = 0; k < npoints; ++k) {
1589 PetscReal r = PetscCosReal(PETSC_PI * (1. - (4. * k + 3. + 2. * b) / (4. * npoints + 2. * (a + b + 1.)))), dP;
1590 PetscInt j;
1591
1592 if (k > 0) r = 0.5 * (r + x[k - 1]);
1593 for (j = 0; j < maxIter; ++j) {
1594 PetscReal s = 0.0, delta, f, fp;
1595 PetscInt i;
1596
1597 for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
1598 PetscCall(PetscDTComputeJacobi(a, b, npoints, r, &f));
1599 PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, r, 1, &fp));
1600 delta = f / (fp - f * s);
1601 r = r - delta;
1602 if (PetscAbsReal(delta) < eps) break;
1603 }
1604 x[k] = r;
1605 PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, x[k], 1, &dP));
1606 w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
1607 }
1608 PetscFunctionReturn(PETSC_SUCCESS);
1609 }
1610
1611 /* Compute the diagonals of the Jacobi matrix used in Golub & Welsch algorithms for Gauss-Jacobi
1612 * quadrature weight calculations on [-1,1] for exponents (1. + x)^a (1.-x)^b */
PetscDTJacobiMatrix_Internal(PetscInt nPoints,PetscReal a,PetscReal b,PetscReal * d,PetscReal * s)1613 static PetscErrorCode PetscDTJacobiMatrix_Internal(PetscInt nPoints, PetscReal a, PetscReal b, PetscReal *d, PetscReal *s)
1614 {
1615 PetscInt i;
1616
1617 PetscFunctionBegin;
1618 for (i = 0; i < nPoints; i++) {
1619 PetscReal A, B, C;
1620
1621 PetscDTJacobiRecurrence_Internal(i + 1, a, b, A, B, C);
1622 d[i] = -A / B;
1623 if (i) s[i - 1] *= C / B;
1624 if (i < nPoints - 1) s[i] = 1. / B;
1625 }
1626 PetscFunctionReturn(PETSC_SUCCESS);
1627 }
1628
PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints,PetscReal a,PetscReal b,PetscReal * x,PetscReal * w)1629 static PetscErrorCode PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
1630 {
1631 PetscReal mu0;
1632 PetscReal ga, gb, gab;
1633 PetscInt i;
1634
1635 PetscFunctionBegin;
1636 PetscCall(PetscCitationsRegister(GolubWelschCitation, &GolubWelschCite));
1637
1638 #if defined(PETSC_HAVE_TGAMMA)
1639 ga = PetscTGamma(a + 1);
1640 gb = PetscTGamma(b + 1);
1641 gab = PetscTGamma(a + b + 2);
1642 #else
1643 {
1644 PetscInt ia = (PetscInt)a, ib = (PetscInt)b;
1645
1646 PetscCheck(ia == a && ib == b && ia + 1 > 0 && ib + 1 > 0 && ia + ib + 2 > 0, PETSC_COMM_SELF, PETSC_ERR_SUP, "tgamma() - math routine is unavailable.");
1647 /* All gamma(x) terms are (x-1)! terms */
1648 PetscCall(PetscDTFactorial(ia, &ga));
1649 PetscCall(PetscDTFactorial(ib, &gb));
1650 PetscCall(PetscDTFactorial(ia + ib + 1, &gab));
1651 }
1652 #endif
1653 mu0 = PetscPowReal(2., a + b + 1.) * ga * gb / gab;
1654
1655 #if defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1656 {
1657 PetscReal *diag, *subdiag;
1658 PetscScalar *V;
1659
1660 PetscCall(PetscMalloc2(npoints, &diag, npoints, &subdiag));
1661 PetscCall(PetscMalloc1(npoints * npoints, &V));
1662 PetscCall(PetscDTJacobiMatrix_Internal(npoints, a, b, diag, subdiag));
1663 for (i = 0; i < npoints - 1; i++) subdiag[i] = PetscSqrtReal(subdiag[i]);
1664 PetscCall(PetscDTSymmetricTridiagonalEigensolve(npoints, diag, subdiag, x, V));
1665 for (i = 0; i < npoints; i++) w[i] = PetscSqr(PetscRealPart(V[i * npoints])) * mu0;
1666 PetscCall(PetscFree(V));
1667 PetscCall(PetscFree2(diag, subdiag));
1668 }
1669 #else
1670 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1671 #endif
1672 { /* As of March 2, 2020, The Sun Performance Library breaks the LAPACK contract for xstegr and xsteqr: the
1673 eigenvalues are not guaranteed to be in ascending order. So we heave a passive aggressive sigh and check that
1674 the eigenvalues are sorted */
1675 PetscBool sorted;
1676
1677 PetscCall(PetscSortedReal(npoints, x, &sorted));
1678 if (!sorted) {
1679 PetscInt *order, i;
1680 PetscReal *tmp;
1681
1682 PetscCall(PetscMalloc2(npoints, &order, npoints, &tmp));
1683 for (i = 0; i < npoints; i++) order[i] = i;
1684 PetscCall(PetscSortRealWithPermutation(npoints, x, order));
1685 PetscCall(PetscArraycpy(tmp, x, npoints));
1686 for (i = 0; i < npoints; i++) x[i] = tmp[order[i]];
1687 PetscCall(PetscArraycpy(tmp, w, npoints));
1688 for (i = 0; i < npoints; i++) w[i] = tmp[order[i]];
1689 PetscCall(PetscFree2(order, tmp));
1690 }
1691 }
1692 PetscFunctionReturn(PETSC_SUCCESS);
1693 }
1694
PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha,PetscReal beta,PetscReal x[],PetscReal w[],PetscBool newton)1695 static PetscErrorCode PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1696 {
1697 PetscFunctionBegin;
1698 PetscCheck(npoints >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
1699 /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1700 PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
1701 PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
1702
1703 if (newton) PetscCall(PetscDTGaussJacobiQuadrature_Newton_Internal(npoints, alpha, beta, x, w));
1704 else PetscCall(PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(npoints, alpha, beta, x, w));
1705 if (alpha == beta) { /* symmetrize */
1706 PetscInt i;
1707 for (i = 0; i < (npoints + 1) / 2; i++) {
1708 PetscInt j = npoints - 1 - i;
1709 PetscReal xi = x[i];
1710 PetscReal xj = x[j];
1711 PetscReal wi = w[i];
1712 PetscReal wj = w[j];
1713
1714 x[i] = (xi - xj) / 2.;
1715 x[j] = (xj - xi) / 2.;
1716 w[i] = w[j] = (wi + wj) / 2.;
1717 }
1718 }
1719 PetscFunctionReturn(PETSC_SUCCESS);
1720 }
1721
1722 /*@
1723 PetscDTGaussJacobiQuadrature - quadrature for the interval $[a, b]$ with the weight function
1724 $(x-a)^\alpha (x-b)^\beta$.
1725
1726 Not Collective
1727
1728 Input Parameters:
1729 + npoints - the number of points in the quadrature rule
1730 . a - the left endpoint of the interval
1731 . b - the right endpoint of the interval
1732 . alpha - the left exponent
1733 - beta - the right exponent
1734
1735 Output Parameters:
1736 + x - array of length `npoints`, the locations of the quadrature points
1737 - w - array of length `npoints`, the weights of the quadrature points
1738
1739 Level: intermediate
1740
1741 Note:
1742 This quadrature rule is exact for polynomials up to degree 2*`npoints` - 1.
1743
1744 .seealso: `PetscDTGaussQuadrature()`
1745 @*/
PetscDTGaussJacobiQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal alpha,PetscReal beta,PetscReal x[],PetscReal w[])1746 PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1747 {
1748 PetscInt i;
1749
1750 PetscFunctionBegin;
1751 PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
1752 if (a != -1. || b != 1.) { /* shift */
1753 for (i = 0; i < npoints; i++) {
1754 x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1755 w[i] *= (b - a) / 2.;
1756 }
1757 }
1758 PetscFunctionReturn(PETSC_SUCCESS);
1759 }
1760
PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha,PetscReal beta,PetscReal x[],PetscReal w[],PetscBool newton)1761 static PetscErrorCode PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1762 {
1763 PetscInt i;
1764
1765 PetscFunctionBegin;
1766 PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
1767 /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1768 PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
1769 PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
1770
1771 x[0] = -1.;
1772 x[npoints - 1] = 1.;
1773 if (npoints > 2) PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints - 2, alpha + 1., beta + 1., &x[1], &w[1], newton));
1774 for (i = 1; i < npoints - 1; i++) w[i] /= (1. - x[i] * x[i]);
1775 PetscCall(PetscDTGaussLobattoJacobiEndweights_Internal(npoints, alpha, beta, &w[0], &w[npoints - 1]));
1776 PetscFunctionReturn(PETSC_SUCCESS);
1777 }
1778
1779 /*@
1780 PetscDTGaussLobattoJacobiQuadrature - quadrature for the interval $[a, b]$ with the weight function
1781 $(x-a)^\alpha (x-b)^\beta$, with endpoints `a` and `b` included as quadrature points.
1782
1783 Not Collective
1784
1785 Input Parameters:
1786 + npoints - the number of points in the quadrature rule
1787 . a - the left endpoint of the interval
1788 . b - the right endpoint of the interval
1789 . alpha - the left exponent
1790 - beta - the right exponent
1791
1792 Output Parameters:
1793 + x - array of length `npoints`, the locations of the quadrature points
1794 - w - array of length `npoints`, the weights of the quadrature points
1795
1796 Level: intermediate
1797
1798 Note:
1799 This quadrature rule is exact for polynomials up to degree 2*`npoints` - 3.
1800
1801 .seealso: `PetscDTGaussJacobiQuadrature()`
1802 @*/
PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal alpha,PetscReal beta,PetscReal x[],PetscReal w[])1803 PetscErrorCode PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1804 {
1805 PetscInt i;
1806
1807 PetscFunctionBegin;
1808 PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
1809 if (a != -1. || b != 1.) { /* shift */
1810 for (i = 0; i < npoints; i++) {
1811 x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1812 w[i] *= (b - a) / 2.;
1813 }
1814 }
1815 PetscFunctionReturn(PETSC_SUCCESS);
1816 }
1817
1818 /*@
1819 PetscDTGaussQuadrature - create Gauss-Legendre quadrature
1820
1821 Not Collective
1822
1823 Input Parameters:
1824 + npoints - number of points
1825 . a - left end of interval (often-1)
1826 - b - right end of interval (often +1)
1827
1828 Output Parameters:
1829 + x - quadrature points
1830 - w - quadrature weights
1831
1832 Level: intermediate
1833
1834 Note:
1835 See {cite}`golub1969calculation`
1836
1837 .seealso: `PetscDTLegendreEval()`, `PetscDTGaussJacobiQuadrature()`
1838 @*/
PetscDTGaussQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal x[],PetscReal w[])1839 PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
1840 {
1841 PetscInt i;
1842
1843 PetscFunctionBegin;
1844 PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, 0., 0., x, w, PetscDTGaussQuadratureNewton_Internal));
1845 if (a != -1. || b != 1.) { /* shift */
1846 for (i = 0; i < npoints; i++) {
1847 x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1848 w[i] *= (b - a) / 2.;
1849 }
1850 }
1851 PetscFunctionReturn(PETSC_SUCCESS);
1852 }
1853
1854 /*@
1855 PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
1856 nodes of a given size on the domain $[-1,1]$
1857
1858 Not Collective
1859
1860 Input Parameters:
1861 + npoints - number of grid nodes
1862 - type - `PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA` or `PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON`
1863
1864 Output Parameters:
1865 + x - quadrature points, pass in an array of length `npoints`
1866 - w - quadrature weights, pass in an array of length `npoints`
1867
1868 Level: intermediate
1869
1870 Notes:
1871 For n > 30 the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
1872 close enough to the desired solution
1873
1874 These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes
1875
1876 See <https://epubs.siam.org/doi/abs/10.1137/110855442> <https://epubs.siam.org/doi/abs/10.1137/120889873> for better ways to compute GLL nodes
1877
1878 .seealso: `PetscDTGaussQuadrature()`, `PetscGaussLobattoLegendreCreateType`
1879
1880 @*/
PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints,PetscGaussLobattoLegendreCreateType type,PetscReal x[],PetscReal w[])1881 PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints, PetscGaussLobattoLegendreCreateType type, PetscReal x[], PetscReal w[])
1882 {
1883 PetscBool newton;
1884
1885 PetscFunctionBegin;
1886 PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must provide at least 2 grid points per element");
1887 newton = (PetscBool)(type == PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON);
1888 PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, 0., 0., x, w, newton));
1889 PetscFunctionReturn(PETSC_SUCCESS);
1890 }
1891
1892 /*@
1893 PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature
1894
1895 Not Collective
1896
1897 Input Parameters:
1898 + dim - The spatial dimension
1899 . Nc - The number of components
1900 . npoints - number of points in one dimension
1901 . a - left end of interval (often-1)
1902 - b - right end of interval (often +1)
1903
1904 Output Parameter:
1905 . q - A `PetscQuadrature` object
1906
1907 Level: intermediate
1908
1909 .seealso: `PetscDTGaussQuadrature()`, `PetscDTLegendreEval()`
1910 @*/
PetscDTGaussTensorQuadrature(PetscInt dim,PetscInt Nc,PetscInt npoints,PetscReal a,PetscReal b,PetscQuadrature * q)1911 PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1912 {
1913 DMPolytopeType ct;
1914 PetscInt totpoints = dim > 1 ? dim > 2 ? npoints * PetscSqr(npoints) : PetscSqr(npoints) : npoints;
1915 PetscReal *x, *w, *xw, *ww;
1916
1917 PetscFunctionBegin;
1918 PetscCall(PetscMalloc1(totpoints * dim, &x));
1919 PetscCall(PetscMalloc1(totpoints * Nc, &w));
1920 /* Set up the Golub-Welsch system */
1921 switch (dim) {
1922 case 0:
1923 ct = DM_POLYTOPE_POINT;
1924 PetscCall(PetscFree(x));
1925 PetscCall(PetscFree(w));
1926 PetscCall(PetscMalloc1(1, &x));
1927 PetscCall(PetscMalloc1(Nc, &w));
1928 totpoints = 1;
1929 x[0] = 0.0;
1930 for (PetscInt c = 0; c < Nc; ++c) w[c] = 1.0;
1931 break;
1932 case 1:
1933 ct = DM_POLYTOPE_SEGMENT;
1934 PetscCall(PetscMalloc1(npoints, &ww));
1935 PetscCall(PetscDTGaussQuadrature(npoints, a, b, x, ww));
1936 for (PetscInt i = 0; i < npoints; ++i)
1937 for (PetscInt c = 0; c < Nc; ++c) w[i * Nc + c] = ww[i];
1938 PetscCall(PetscFree(ww));
1939 break;
1940 case 2:
1941 ct = DM_POLYTOPE_QUADRILATERAL;
1942 PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
1943 PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1944 for (PetscInt i = 0; i < npoints; ++i) {
1945 for (PetscInt j = 0; j < npoints; ++j) {
1946 x[(i * npoints + j) * dim + 0] = xw[i];
1947 x[(i * npoints + j) * dim + 1] = xw[j];
1948 for (PetscInt c = 0; c < Nc; ++c) w[(i * npoints + j) * Nc + c] = ww[i] * ww[j];
1949 }
1950 }
1951 PetscCall(PetscFree2(xw, ww));
1952 break;
1953 case 3:
1954 ct = DM_POLYTOPE_HEXAHEDRON;
1955 PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
1956 PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1957 for (PetscInt i = 0; i < npoints; ++i) {
1958 for (PetscInt j = 0; j < npoints; ++j) {
1959 for (PetscInt k = 0; k < npoints; ++k) {
1960 x[((i * npoints + j) * npoints + k) * dim + 0] = xw[i];
1961 x[((i * npoints + j) * npoints + k) * dim + 1] = xw[j];
1962 x[((i * npoints + j) * npoints + k) * dim + 2] = xw[k];
1963 for (PetscInt c = 0; c < Nc; ++c) w[((i * npoints + j) * npoints + k) * Nc + c] = ww[i] * ww[j] * ww[k];
1964 }
1965 }
1966 }
1967 PetscCall(PetscFree2(xw, ww));
1968 break;
1969 default:
1970 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %" PetscInt_FMT, dim);
1971 }
1972 PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
1973 PetscCall(PetscQuadratureSetCellType(*q, ct));
1974 PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
1975 PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
1976 PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "GaussTensor"));
1977 PetscFunctionReturn(PETSC_SUCCESS);
1978 }
1979
1980 /*@
1981 PetscDTStroudConicalQuadrature - create Stroud conical quadrature for a simplex {cite}`karniadakis2005spectral`
1982
1983 Not Collective
1984
1985 Input Parameters:
1986 + dim - The simplex dimension
1987 . Nc - The number of components
1988 . npoints - The number of points in one dimension
1989 . a - left end of interval (often-1)
1990 - b - right end of interval (often +1)
1991
1992 Output Parameter:
1993 . q - A `PetscQuadrature` object
1994
1995 Level: intermediate
1996
1997 Note:
1998 For `dim` == 1, this is Gauss-Legendre quadrature
1999
2000 .seealso: `PetscDTGaussTensorQuadrature()`, `PetscDTGaussQuadrature()`
2001 @*/
PetscDTStroudConicalQuadrature(PetscInt dim,PetscInt Nc,PetscInt npoints,PetscReal a,PetscReal b,PetscQuadrature * q)2002 PetscErrorCode PetscDTStroudConicalQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
2003 {
2004 DMPolytopeType ct;
2005 PetscInt totpoints;
2006 PetscReal *p1, *w1;
2007 PetscReal *x, *w;
2008
2009 PetscFunctionBegin;
2010 PetscCheck(!(a != -1.0) && !(b != 1.0), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
2011 switch (dim) {
2012 case 0:
2013 ct = DM_POLYTOPE_POINT;
2014 break;
2015 case 1:
2016 ct = DM_POLYTOPE_SEGMENT;
2017 break;
2018 case 2:
2019 ct = DM_POLYTOPE_TRIANGLE;
2020 break;
2021 case 3:
2022 ct = DM_POLYTOPE_TETRAHEDRON;
2023 break;
2024 default:
2025 ct = DM_POLYTOPE_UNKNOWN;
2026 }
2027 totpoints = 1;
2028 for (PetscInt i = 0; i < dim; ++i) totpoints *= npoints;
2029 PetscCall(PetscMalloc1(totpoints * dim, &x));
2030 PetscCall(PetscMalloc1(totpoints * Nc, &w));
2031 PetscCall(PetscMalloc2(npoints, &p1, npoints, &w1));
2032 for (PetscInt i = 0; i < totpoints * Nc; ++i) w[i] = 1.;
2033 for (PetscInt i = 0, totprev = 1, totrem = totpoints / npoints; i < dim; ++i) {
2034 PetscReal mul;
2035
2036 mul = PetscPowReal(2., -i);
2037 PetscCall(PetscDTGaussJacobiQuadrature(npoints, -1., 1., i, 0.0, p1, w1));
2038 for (PetscInt pt = 0, l = 0; l < totprev; l++) {
2039 for (PetscInt j = 0; j < npoints; j++) {
2040 for (PetscInt m = 0; m < totrem; m++, pt++) {
2041 for (PetscInt k = 0; k < i; k++) x[pt * dim + k] = (x[pt * dim + k] + 1.) * (1. - p1[j]) * 0.5 - 1.;
2042 x[pt * dim + i] = p1[j];
2043 for (PetscInt c = 0; c < Nc; c++) w[pt * Nc + c] *= mul * w1[j];
2044 }
2045 }
2046 }
2047 totprev *= npoints;
2048 totrem /= npoints;
2049 }
2050 PetscCall(PetscFree2(p1, w1));
2051 PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2052 PetscCall(PetscQuadratureSetCellType(*q, ct));
2053 PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
2054 PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
2055 PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "StroudConical"));
2056 PetscFunctionReturn(PETSC_SUCCESS);
2057 }
2058
2059 static PetscBool MinSymTriQuadCite = PETSC_FALSE;
2060 const char MinSymTriQuadCitation[] = "@article{WitherdenVincent2015,\n"
2061 " title = {On the identification of symmetric quadrature rules for finite element methods},\n"
2062 " journal = {Computers & Mathematics with Applications},\n"
2063 " volume = {69},\n"
2064 " number = {10},\n"
2065 " pages = {1232-1241},\n"
2066 " year = {2015},\n"
2067 " issn = {0898-1221},\n"
2068 " doi = {10.1016/j.camwa.2015.03.017},\n"
2069 " url = {https://www.sciencedirect.com/science/article/pii/S0898122115001224},\n"
2070 " author = {F.D. Witherden and P.E. Vincent},\n"
2071 "}\n";
2072
2073 #include "petscdttriquadrules.h"
2074
2075 static PetscBool MinSymTetQuadCite = PETSC_FALSE;
2076 const char MinSymTetQuadCitation[] = "@article{JaskowiecSukumar2021\n"
2077 " author = {Jaskowiec, Jan and Sukumar, N.},\n"
2078 " title = {High-order symmetric cubature rules for tetrahedra and pyramids},\n"
2079 " journal = {International Journal for Numerical Methods in Engineering},\n"
2080 " volume = {122},\n"
2081 " number = {1},\n"
2082 " pages = {148-171},\n"
2083 " doi = {10.1002/nme.6528},\n"
2084 " url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/nme.6528},\n"
2085 " eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/nme.6528},\n"
2086 " year = {2021}\n"
2087 "}\n";
2088
2089 #include "petscdttetquadrules.h"
2090
2091 static PetscBool DiagSymTriQuadCite = PETSC_FALSE;
2092 const char DiagSymTriQuadCitation[] = "@article{KongMulderVeldhuizen1999,\n"
2093 " title = {Higher-order triangular and tetrahedral finite elements with mass lumping for solving the wave equation},\n"
2094 " journal = {Journal of Engineering Mathematics},\n"
2095 " volume = {35},\n"
2096 " number = {4},\n"
2097 " pages = {405--426},\n"
2098 " year = {1999},\n"
2099 " doi = {10.1023/A:1004420829610},\n"
2100 " url = {https://link.springer.com/article/10.1023/A:1004420829610},\n"
2101 " author = {MJS Chin-Joe-Kong and Wim A Mulder and Marinus Van Veldhuizen},\n"
2102 "}\n";
2103
2104 #include "petscdttridiagquadrules.h"
2105
2106 // https://en.wikipedia.org/wiki/Partition_(number_theory)
PetscDTPartitionNumber(PetscInt n,PetscInt * p)2107 static PetscErrorCode PetscDTPartitionNumber(PetscInt n, PetscInt *p)
2108 {
2109 // sequence A000041 in the OEIS
2110 const PetscInt partition[] = {1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77, 101, 135, 176, 231, 297, 385, 490, 627, 792, 1002, 1255, 1575, 1958, 2436, 3010, 3718, 4565, 5604};
2111 PetscInt tabulated_max = PETSC_STATIC_ARRAY_LENGTH(partition) - 1;
2112
2113 PetscFunctionBegin;
2114 PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Partition number not defined for negative number %" PetscInt_FMT, n);
2115 // not implementing the pentagonal number recurrence, we don't need partition numbers for n that high
2116 PetscCheck(n <= tabulated_max, PETSC_COMM_SELF, PETSC_ERR_SUP, "Partition numbers only tabulated up to %" PetscInt_FMT ", not computed for %" PetscInt_FMT, tabulated_max, n);
2117 *p = partition[n];
2118 PetscFunctionReturn(PETSC_SUCCESS);
2119 }
2120
2121 /*@
2122 PetscDTSimplexQuadrature - Create a quadrature rule for a simplex that exactly integrates polynomials up to a given degree.
2123
2124 Not Collective
2125
2126 Input Parameters:
2127 + dim - The spatial dimension of the simplex (1 = segment, 2 = triangle, 3 = tetrahedron)
2128 . degree - The largest polynomial degree that is required to be integrated exactly
2129 - type - `PetscDTSimplexQuadratureType` indicating the type of quadrature rule
2130
2131 Output Parameter:
2132 . quad - A `PetscQuadrature` object for integration over the biunit simplex
2133 (defined by the bounds $x_i >= -1$ and $\sum_i x_i <= 2 - d$) that is exact for
2134 polynomials up to the given degree
2135
2136 Level: intermediate
2137
2138 .seealso: `PetscDTSimplexQuadratureType`, `PetscDTGaussQuadrature()`, `PetscDTStroudCononicalQuadrature()`, `PetscQuadrature`
2139 @*/
PetscDTSimplexQuadrature(PetscInt dim,PetscInt degree,PetscDTSimplexQuadratureType type,PetscQuadrature * quad)2140 PetscErrorCode PetscDTSimplexQuadrature(PetscInt dim, PetscInt degree, PetscDTSimplexQuadratureType type, PetscQuadrature *quad)
2141 {
2142 PetscDTSimplexQuadratureType orig_type = type;
2143
2144 PetscFunctionBegin;
2145 PetscCheck(dim >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative dimension %" PetscInt_FMT, dim);
2146 PetscCheck(degree >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative degree %" PetscInt_FMT, degree);
2147 if (type == PETSCDTSIMPLEXQUAD_DEFAULT) type = PETSCDTSIMPLEXQUAD_MINSYM;
2148 if (type == PETSCDTSIMPLEXQUAD_CONIC || dim < 2) {
2149 PetscInt points_per_dim = (degree + 2) / 2; // ceil((degree + 1) / 2);
2150 PetscCall(PetscDTStroudConicalQuadrature(dim, 1, points_per_dim, -1, 1, quad));
2151 } else {
2152 DMPolytopeType ct;
2153 PetscInt n = dim + 1;
2154 PetscInt fact = 1;
2155 PetscInt *part, *perm;
2156 PetscInt p = 0;
2157 PetscInt max_degree;
2158 const PetscInt *nodes_per_type = NULL;
2159 const PetscInt *all_num_full_nodes = NULL;
2160 const PetscReal **weights_list = NULL;
2161 const PetscReal **compact_nodes_list = NULL;
2162 const char *citation = NULL;
2163 PetscBool *cited = NULL;
2164
2165 switch (dim) {
2166 case 0:
2167 ct = DM_POLYTOPE_POINT;
2168 break;
2169 case 1:
2170 ct = DM_POLYTOPE_SEGMENT;
2171 break;
2172 case 2:
2173 ct = DM_POLYTOPE_TRIANGLE;
2174 break;
2175 case 3:
2176 ct = DM_POLYTOPE_TETRAHEDRON;
2177 break;
2178 default:
2179 ct = DM_POLYTOPE_UNKNOWN;
2180 }
2181 if (type == PETSCDTSIMPLEXQUAD_MINSYM) {
2182 switch (dim) {
2183 case 2:
2184 cited = &MinSymTriQuadCite;
2185 citation = MinSymTriQuadCitation;
2186 max_degree = PetscDTWVTriQuad_max_degree;
2187 nodes_per_type = PetscDTWVTriQuad_num_orbits;
2188 all_num_full_nodes = PetscDTWVTriQuad_num_nodes;
2189 weights_list = PetscDTWVTriQuad_weights;
2190 compact_nodes_list = PetscDTWVTriQuad_orbits;
2191 break;
2192 case 3:
2193 cited = &MinSymTetQuadCite;
2194 citation = MinSymTetQuadCitation;
2195 max_degree = PetscDTJSTetQuad_max_degree;
2196 nodes_per_type = PetscDTJSTetQuad_num_orbits;
2197 all_num_full_nodes = PetscDTJSTetQuad_num_nodes;
2198 weights_list = PetscDTJSTetQuad_weights;
2199 compact_nodes_list = PetscDTJSTetQuad_orbits;
2200 break;
2201 default:
2202 max_degree = -1;
2203 break;
2204 }
2205 } else {
2206 switch (dim) {
2207 case 2:
2208 cited = &DiagSymTriQuadCite;
2209 citation = DiagSymTriQuadCitation;
2210 max_degree = PetscDTKMVTriQuad_max_degree;
2211 nodes_per_type = PetscDTKMVTriQuad_num_orbits;
2212 all_num_full_nodes = PetscDTKMVTriQuad_num_nodes;
2213 weights_list = PetscDTKMVTriQuad_weights;
2214 compact_nodes_list = PetscDTKMVTriQuad_orbits;
2215 break;
2216 default:
2217 max_degree = -1;
2218 break;
2219 }
2220 }
2221
2222 if (degree > max_degree) {
2223 PetscCheck(orig_type == PETSCDTSIMPLEXQUAD_DEFAULT, PETSC_COMM_SELF, PETSC_ERR_SUP, "%s symmetric quadrature for dim %" PetscInt_FMT ", degree %" PetscInt_FMT " unsupported", orig_type == PETSCDTSIMPLEXQUAD_MINSYM ? "Minimal" : "Diagonal", dim, degree);
2224 // fall back to conic
2225 PetscCall(PetscDTSimplexQuadrature(dim, degree, PETSCDTSIMPLEXQUAD_CONIC, quad));
2226 PetscFunctionReturn(PETSC_SUCCESS);
2227 }
2228
2229 PetscCall(PetscCitationsRegister(citation, cited));
2230
2231 PetscCall(PetscDTPartitionNumber(n, &p));
2232 for (PetscInt d = 2; d <= n; d++) fact *= d;
2233
2234 PetscInt num_full_nodes = all_num_full_nodes[degree];
2235 const PetscReal *all_compact_nodes = compact_nodes_list[degree];
2236 const PetscReal *all_compact_weights = weights_list[degree];
2237 nodes_per_type = &nodes_per_type[p * degree];
2238
2239 PetscReal *points;
2240 PetscReal *counts;
2241 PetscReal *weights;
2242 PetscReal *bary_to_biunit; // row-major transformation of barycentric coordinate to biunit
2243 PetscQuadrature q;
2244
2245 // compute the transformation
2246 PetscCall(PetscMalloc1(n * dim, &bary_to_biunit));
2247 for (PetscInt d = 0; d < dim; d++) {
2248 for (PetscInt b = 0; b < n; b++) bary_to_biunit[d * n + b] = (d == b) ? 1.0 : -1.0;
2249 }
2250
2251 PetscCall(PetscMalloc3(n, &part, n, &perm, n, &counts));
2252 PetscCall(PetscCalloc1(num_full_nodes * dim, &points));
2253 PetscCall(PetscMalloc1(num_full_nodes, &weights));
2254
2255 // (0, 0, ...) is the first partition lexicographically
2256 PetscCall(PetscArrayzero(part, n));
2257 PetscCall(PetscArrayzero(counts, n));
2258 counts[0] = n;
2259
2260 // for each partition
2261 for (PetscInt s = 0, node_offset = 0; s < p; s++) {
2262 PetscInt num_compact_coords = part[n - 1] + 1;
2263
2264 const PetscReal *compact_nodes = all_compact_nodes;
2265 const PetscReal *compact_weights = all_compact_weights;
2266 all_compact_nodes += num_compact_coords * nodes_per_type[s];
2267 all_compact_weights += nodes_per_type[s];
2268
2269 // for every permutation of the vertices
2270 for (PetscInt f = 0; f < fact; f++) {
2271 PetscCall(PetscDTEnumPerm(n, f, perm, NULL));
2272
2273 // check if it is a valid permutation
2274 PetscInt digit;
2275 for (digit = 1; digit < n; digit++) {
2276 // skip permutations that would duplicate a node because it has a smaller symmetry group
2277 if (part[digit - 1] == part[digit] && perm[digit - 1] > perm[digit]) break;
2278 }
2279 if (digit < n) continue;
2280
2281 // create full nodes from this permutation of the compact nodes
2282 PetscReal *full_nodes = &points[node_offset * dim];
2283 PetscReal *full_weights = &weights[node_offset];
2284
2285 PetscCall(PetscArraycpy(full_weights, compact_weights, nodes_per_type[s]));
2286 for (PetscInt b = 0; b < n; b++) {
2287 for (PetscInt d = 0; d < dim; d++) {
2288 for (PetscInt node = 0; node < nodes_per_type[s]; node++) full_nodes[node * dim + d] += bary_to_biunit[d * n + perm[b]] * compact_nodes[node * num_compact_coords + part[b]];
2289 }
2290 }
2291 node_offset += nodes_per_type[s];
2292 }
2293
2294 if (s < p - 1) { // Generate the next partition
2295 /* A partition is described by the number of coordinates that are in
2296 * each set of duplicates (counts) and redundantly by mapping each
2297 * index to its set of duplicates (part)
2298 *
2299 * Counts should always be in nonincreasing order
2300 *
2301 * We want to generate the partitions lexically by part, which means
2302 * finding the last index where count > 1 and reducing by 1.
2303 *
2304 * For the new counts beyond that index, we eagerly assign the remaining
2305 * capacity of the partition to smaller indices (ensures lexical ordering),
2306 * while respecting the nonincreasing invariant of the counts
2307 */
2308 PetscInt last_digit = part[n - 1];
2309 PetscInt last_digit_with_extra = last_digit;
2310 while (counts[last_digit_with_extra] == 1) last_digit_with_extra--;
2311 PetscInt limit = --counts[last_digit_with_extra];
2312 PetscInt total_to_distribute = last_digit - last_digit_with_extra + 1;
2313 for (PetscInt digit = last_digit_with_extra + 1; digit < n; digit++) {
2314 counts[digit] = PetscMin(limit, total_to_distribute);
2315 total_to_distribute -= PetscMin(limit, total_to_distribute);
2316 }
2317 for (PetscInt digit = 0, offset = 0; digit < n; digit++) {
2318 PetscInt count = counts[digit];
2319 for (PetscInt c = 0; c < count; c++) part[offset++] = digit;
2320 }
2321 }
2322 PetscCheck(node_offset <= num_full_nodes, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Node offset %" PetscInt_FMT " > %" PetscInt_FMT " number of nodes", node_offset, num_full_nodes);
2323 }
2324 PetscCall(PetscFree3(part, perm, counts));
2325 PetscCall(PetscFree(bary_to_biunit));
2326 PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &q));
2327 PetscCall(PetscQuadratureSetCellType(q, ct));
2328 PetscCall(PetscQuadratureSetOrder(q, degree));
2329 PetscCall(PetscQuadratureSetData(q, dim, 1, num_full_nodes, points, weights));
2330 *quad = q;
2331 }
2332 PetscFunctionReturn(PETSC_SUCCESS);
2333 }
2334
2335 /*@
2336 PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell
2337
2338 Not Collective
2339
2340 Input Parameters:
2341 + dim - The cell dimension
2342 . level - The number of points in one dimension, $2^l$
2343 . a - left end of interval (often-1)
2344 - b - right end of interval (often +1)
2345
2346 Output Parameter:
2347 . q - A `PetscQuadrature` object
2348
2349 Level: intermediate
2350
2351 .seealso: `PetscDTGaussTensorQuadrature()`, `PetscQuadrature`
2352 @*/
PetscDTTanhSinhTensorQuadrature(PetscInt dim,PetscInt level,PetscReal a,PetscReal b,PetscQuadrature * q)2353 PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
2354 {
2355 DMPolytopeType ct;
2356 const PetscInt p = 16; /* Digits of precision in the evaluation */
2357 const PetscReal alpha = (b - a) / 2.; /* Half-width of the integration interval */
2358 const PetscReal beta = (b + a) / 2.; /* Center of the integration interval */
2359 const PetscReal h = PetscPowReal(2.0, -level); /* Step size, length between x_k */
2360 PetscReal xk; /* Quadrature point x_k on reference domain [-1, 1] */
2361 PetscReal wk = 0.5 * PETSC_PI; /* Quadrature weight at x_k */
2362 PetscReal *x, *w;
2363 PetscInt K, k, npoints;
2364
2365 PetscFunctionBegin;
2366 PetscCheck(dim <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT " not yet implemented", dim);
2367 PetscCheck(level, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
2368 switch (dim) {
2369 case 0:
2370 ct = DM_POLYTOPE_POINT;
2371 break;
2372 case 1:
2373 ct = DM_POLYTOPE_SEGMENT;
2374 break;
2375 case 2:
2376 ct = DM_POLYTOPE_QUADRILATERAL;
2377 break;
2378 case 3:
2379 ct = DM_POLYTOPE_HEXAHEDRON;
2380 break;
2381 default:
2382 ct = DM_POLYTOPE_UNKNOWN;
2383 }
2384 /* Find K such that the weights are < 32 digits of precision */
2385 for (K = 1; PetscAbsReal(PetscLog10Real(wk)) < 2 * p; ++K) wk = 0.5 * h * PETSC_PI * PetscCoshReal(K * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(K * h)));
2386 PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2387 PetscCall(PetscQuadratureSetCellType(*q, ct));
2388 PetscCall(PetscQuadratureSetOrder(*q, 2 * K + 1));
2389 npoints = 2 * K - 1;
2390 PetscCall(PetscMalloc1(npoints * dim, &x));
2391 PetscCall(PetscMalloc1(npoints, &w));
2392 /* Center term */
2393 x[0] = beta;
2394 w[0] = 0.5 * alpha * PETSC_PI;
2395 for (k = 1; k < K; ++k) {
2396 wk = 0.5 * alpha * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2397 xk = PetscTanhReal(0.5 * PETSC_PI * PetscSinhReal(k * h));
2398 x[2 * k - 1] = -alpha * xk + beta;
2399 w[2 * k - 1] = wk;
2400 x[2 * k + 0] = alpha * xk + beta;
2401 w[2 * k + 0] = wk;
2402 }
2403 PetscCall(PetscQuadratureSetData(*q, dim, 1, npoints, x, w));
2404 PetscFunctionReturn(PETSC_SUCCESS);
2405 }
2406
PetscDTTanhSinhIntegrate(void (* func)(const PetscReal[],void *,PetscReal *),PetscReal a,PetscReal b,PetscInt digits,PetscCtx ctx,PetscReal * sol)2407 PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscCtx ctx, PetscReal *sol)
2408 {
2409 const PetscInt p = 16; /* Digits of precision in the evaluation */
2410 const PetscReal alpha = (b - a) / 2.; /* Half-width of the integration interval */
2411 const PetscReal beta = (b + a) / 2.; /* Center of the integration interval */
2412 PetscReal h = 1.0; /* Step size, length between x_k */
2413 PetscInt l = 0; /* Level of refinement, h = 2^{-l} */
2414 PetscReal osum = 0.0; /* Integral on last level */
2415 PetscReal psum = 0.0; /* Integral on the level before the last level */
2416 PetscReal sum; /* Integral on current level */
2417 PetscReal yk; /* Quadrature point 1 - x_k on reference domain [-1, 1] */
2418 PetscReal lx, rx; /* Quadrature points to the left and right of 0 on the real domain [a, b] */
2419 PetscReal wk; /* Quadrature weight at x_k */
2420 PetscReal lval, rval; /* Terms in the quadature sum to the left and right of 0 */
2421 PetscInt d; /* Digits of precision in the integral */
2422
2423 PetscFunctionBegin;
2424 PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
2425 PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
2426 /* Center term */
2427 func(&beta, ctx, &lval);
2428 sum = 0.5 * alpha * PETSC_PI * lval;
2429 /* */
2430 do {
2431 PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
2432 PetscInt k = 1;
2433
2434 ++l;
2435 /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
2436 /* At each level of refinement, h --> h/2 and sum --> sum/2 */
2437 psum = osum;
2438 osum = sum;
2439 h *= 0.5;
2440 sum *= 0.5;
2441 do {
2442 wk = 0.5 * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2443 yk = 1.0 / (PetscExpReal(0.5 * PETSC_PI * PetscSinhReal(k * h)) * PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2444 lx = -alpha * (1.0 - yk) + beta;
2445 rx = alpha * (1.0 - yk) + beta;
2446 func(&lx, ctx, &lval);
2447 func(&rx, ctx, &rval);
2448 lterm = alpha * wk * lval;
2449 maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
2450 sum += lterm;
2451 rterm = alpha * wk * rval;
2452 maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
2453 sum += rterm;
2454 ++k;
2455 /* Only need to evaluate every other point on refined levels */
2456 if (l != 1) ++k;
2457 } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */
2458
2459 d1 = PetscLog10Real(PetscAbsReal(sum - osum));
2460 d2 = PetscLog10Real(PetscAbsReal(sum - psum));
2461 d3 = PetscLog10Real(maxTerm) - p;
2462 if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
2463 else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
2464 d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
2465 } while (d < digits && l < 12);
2466 *sol = sum;
2467 PetscCall(PetscFPTrapPop());
2468 PetscFunctionReturn(PETSC_SUCCESS);
2469 }
2470
2471 #if defined(PETSC_HAVE_MPFR)
PetscDTTanhSinhIntegrateMPFR(void (* func)(const PetscReal[],void *,PetscReal *),PetscReal a,PetscReal b,PetscInt digits,PetscCtx ctx,PetscReal * sol)2472 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscCtx ctx, PetscReal *sol)
2473 {
2474 const PetscInt safetyFactor = 2; /* Calculate abscissa until 2*p digits */
2475 PetscInt l = 0; /* Level of refinement, h = 2^{-l} */
2476 mpfr_t alpha; /* Half-width of the integration interval */
2477 mpfr_t beta; /* Center of the integration interval */
2478 mpfr_t h; /* Step size, length between x_k */
2479 mpfr_t osum; /* Integral on last level */
2480 mpfr_t psum; /* Integral on the level before the last level */
2481 mpfr_t sum; /* Integral on current level */
2482 mpfr_t yk; /* Quadrature point 1 - x_k on reference domain [-1, 1] */
2483 mpfr_t lx, rx; /* Quadrature points to the left and right of 0 on the real domain [a, b] */
2484 mpfr_t wk; /* Quadrature weight at x_k */
2485 PetscReal lval, rval, rtmp; /* Terms in the quadature sum to the left and right of 0 */
2486 PetscInt d; /* Digits of precision in the integral */
2487 mpfr_t pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;
2488
2489 PetscFunctionBegin;
2490 PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
2491 /* Create high precision storage */
2492 mpfr_inits2(PetscCeilReal(safetyFactor * digits * PetscLogReal(10.) / PetscLogReal(2.)), alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
2493 /* Initialization */
2494 mpfr_set_d(alpha, 0.5 * (b - a), MPFR_RNDN);
2495 mpfr_set_d(beta, 0.5 * (b + a), MPFR_RNDN);
2496 mpfr_set_d(osum, 0.0, MPFR_RNDN);
2497 mpfr_set_d(psum, 0.0, MPFR_RNDN);
2498 mpfr_set_d(h, 1.0, MPFR_RNDN);
2499 mpfr_const_pi(pi2, MPFR_RNDN);
2500 mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
2501 /* Center term */
2502 rtmp = 0.5 * (b + a);
2503 func(&rtmp, ctx, &lval);
2504 mpfr_set(sum, pi2, MPFR_RNDN);
2505 mpfr_mul(sum, sum, alpha, MPFR_RNDN);
2506 mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
2507 /* */
2508 do {
2509 PetscReal d1, d2, d3, d4;
2510 PetscInt k = 1;
2511
2512 ++l;
2513 mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
2514 /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
2515 /* At each level of refinement, h --> h/2 and sum --> sum/2 */
2516 mpfr_set(psum, osum, MPFR_RNDN);
2517 mpfr_set(osum, sum, MPFR_RNDN);
2518 mpfr_mul_d(h, h, 0.5, MPFR_RNDN);
2519 mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
2520 do {
2521 mpfr_set_si(kh, k, MPFR_RNDN);
2522 mpfr_mul(kh, kh, h, MPFR_RNDN);
2523 /* Weight */
2524 mpfr_set(wk, h, MPFR_RNDN);
2525 mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
2526 mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
2527 mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
2528 mpfr_cosh(tmp, msinh, MPFR_RNDN);
2529 mpfr_sqr(tmp, tmp, MPFR_RNDN);
2530 mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
2531 mpfr_div(wk, wk, tmp, MPFR_RNDN);
2532 /* Abscissa */
2533 mpfr_set_d(yk, 1.0, MPFR_RNDZ);
2534 mpfr_cosh(tmp, msinh, MPFR_RNDN);
2535 mpfr_div(yk, yk, tmp, MPFR_RNDZ);
2536 mpfr_exp(tmp, msinh, MPFR_RNDN);
2537 mpfr_div(yk, yk, tmp, MPFR_RNDZ);
2538 /* Quadrature points */
2539 mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
2540 mpfr_mul(lx, lx, alpha, MPFR_RNDU);
2541 mpfr_add(lx, lx, beta, MPFR_RNDU);
2542 mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
2543 mpfr_mul(rx, rx, alpha, MPFR_RNDD);
2544 mpfr_add(rx, rx, beta, MPFR_RNDD);
2545 /* Evaluation */
2546 rtmp = mpfr_get_d(lx, MPFR_RNDU);
2547 func(&rtmp, ctx, &lval);
2548 rtmp = mpfr_get_d(rx, MPFR_RNDD);
2549 func(&rtmp, ctx, &rval);
2550 /* Update */
2551 mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
2552 mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
2553 mpfr_add(sum, sum, tmp, MPFR_RNDN);
2554 mpfr_abs(tmp, tmp, MPFR_RNDN);
2555 mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
2556 mpfr_set(curTerm, tmp, MPFR_RNDN);
2557 mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
2558 mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
2559 mpfr_add(sum, sum, tmp, MPFR_RNDN);
2560 mpfr_abs(tmp, tmp, MPFR_RNDN);
2561 mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
2562 mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
2563 ++k;
2564 /* Only need to evaluate every other point on refined levels */
2565 if (l != 1) ++k;
2566 mpfr_log10(tmp, wk, MPFR_RNDN);
2567 mpfr_abs(tmp, tmp, MPFR_RNDN);
2568 } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor * digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
2569 mpfr_sub(tmp, sum, osum, MPFR_RNDN);
2570 mpfr_abs(tmp, tmp, MPFR_RNDN);
2571 mpfr_log10(tmp, tmp, MPFR_RNDN);
2572 d1 = mpfr_get_d(tmp, MPFR_RNDN);
2573 mpfr_sub(tmp, sum, psum, MPFR_RNDN);
2574 mpfr_abs(tmp, tmp, MPFR_RNDN);
2575 mpfr_log10(tmp, tmp, MPFR_RNDN);
2576 d2 = mpfr_get_d(tmp, MPFR_RNDN);
2577 mpfr_log10(tmp, maxTerm, MPFR_RNDN);
2578 d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
2579 mpfr_log10(tmp, curTerm, MPFR_RNDN);
2580 d4 = mpfr_get_d(tmp, MPFR_RNDN);
2581 d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
2582 } while (d < digits && l < 8);
2583 *sol = mpfr_get_d(sum, MPFR_RNDN);
2584 /* Cleanup */
2585 mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
2586 PetscFunctionReturn(PETSC_SUCCESS);
2587 }
2588 #else
2589
PetscDTTanhSinhIntegrateMPFR(void (* func)(const PetscReal[],void *,PetscReal *),PetscReal a,PetscReal b,PetscInt digits,PetscCtx ctx,PetscReal * sol)2590 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscCtx ctx, PetscReal *sol)
2591 {
2592 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
2593 }
2594 #endif
2595
2596 /*@
2597 PetscDTTensorQuadratureCreate - create the tensor product quadrature from two lower-dimensional quadratures
2598
2599 Not Collective
2600
2601 Input Parameters:
2602 + q1 - The first quadrature
2603 - q2 - The second quadrature
2604
2605 Output Parameter:
2606 . q - A `PetscQuadrature` object
2607
2608 Level: intermediate
2609
2610 .seealso: `PetscQuadrature`, `PetscDTGaussTensorQuadrature()`
2611 @*/
PetscDTTensorQuadratureCreate(PetscQuadrature q1,PetscQuadrature q2,PetscQuadrature * q)2612 PetscErrorCode PetscDTTensorQuadratureCreate(PetscQuadrature q1, PetscQuadrature q2, PetscQuadrature *q)
2613 {
2614 DMPolytopeType ct1, ct2, ct;
2615 const PetscReal *x1, *w1, *x2, *w2;
2616 PetscReal *x, *w;
2617 PetscInt dim1, Nc1, Np1, order1, qa, d1;
2618 PetscInt dim2, Nc2, Np2, order2, qb, d2;
2619 PetscInt dim, Nc, Np, order, qc, d;
2620
2621 PetscFunctionBegin;
2622 PetscValidHeaderSpecific(q1, PETSCQUADRATURE_CLASSID, 1);
2623 PetscValidHeaderSpecific(q2, PETSCQUADRATURE_CLASSID, 2);
2624 PetscAssertPointer(q, 3);
2625
2626 PetscCall(PetscQuadratureGetOrder(q1, &order1));
2627 PetscCall(PetscQuadratureGetOrder(q2, &order2));
2628 PetscCheck(order1 == order2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Order1 %" PetscInt_FMT " != %" PetscInt_FMT " Order2", order1, order2);
2629 PetscCall(PetscQuadratureGetData(q1, &dim1, &Nc1, &Np1, &x1, &w1));
2630 PetscCall(PetscQuadratureGetCellType(q1, &ct1));
2631 PetscCall(PetscQuadratureGetData(q2, &dim2, &Nc2, &Np2, &x2, &w2));
2632 PetscCall(PetscQuadratureGetCellType(q2, &ct2));
2633 PetscCheck(Nc1 == Nc2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "NumComp1 %" PetscInt_FMT " != %" PetscInt_FMT " NumComp2", Nc1, Nc2);
2634
2635 switch (ct1) {
2636 case DM_POLYTOPE_POINT:
2637 ct = ct2;
2638 break;
2639 case DM_POLYTOPE_SEGMENT:
2640 switch (ct2) {
2641 case DM_POLYTOPE_POINT:
2642 ct = ct1;
2643 break;
2644 case DM_POLYTOPE_SEGMENT:
2645 ct = DM_POLYTOPE_QUADRILATERAL;
2646 break;
2647 case DM_POLYTOPE_TRIANGLE:
2648 ct = DM_POLYTOPE_TRI_PRISM;
2649 break;
2650 case DM_POLYTOPE_QUADRILATERAL:
2651 ct = DM_POLYTOPE_HEXAHEDRON;
2652 break;
2653 case DM_POLYTOPE_TETRAHEDRON:
2654 ct = DM_POLYTOPE_UNKNOWN;
2655 break;
2656 case DM_POLYTOPE_HEXAHEDRON:
2657 ct = DM_POLYTOPE_UNKNOWN;
2658 break;
2659 default:
2660 ct = DM_POLYTOPE_UNKNOWN;
2661 }
2662 break;
2663 case DM_POLYTOPE_TRIANGLE:
2664 switch (ct2) {
2665 case DM_POLYTOPE_POINT:
2666 ct = ct1;
2667 break;
2668 case DM_POLYTOPE_SEGMENT:
2669 ct = DM_POLYTOPE_TRI_PRISM;
2670 break;
2671 case DM_POLYTOPE_TRIANGLE:
2672 ct = DM_POLYTOPE_UNKNOWN;
2673 break;
2674 case DM_POLYTOPE_QUADRILATERAL:
2675 ct = DM_POLYTOPE_UNKNOWN;
2676 break;
2677 case DM_POLYTOPE_TETRAHEDRON:
2678 ct = DM_POLYTOPE_UNKNOWN;
2679 break;
2680 case DM_POLYTOPE_HEXAHEDRON:
2681 ct = DM_POLYTOPE_UNKNOWN;
2682 break;
2683 default:
2684 ct = DM_POLYTOPE_UNKNOWN;
2685 }
2686 break;
2687 case DM_POLYTOPE_QUADRILATERAL:
2688 switch (ct2) {
2689 case DM_POLYTOPE_POINT:
2690 ct = ct1;
2691 break;
2692 case DM_POLYTOPE_SEGMENT:
2693 ct = DM_POLYTOPE_HEXAHEDRON;
2694 break;
2695 case DM_POLYTOPE_TRIANGLE:
2696 ct = DM_POLYTOPE_UNKNOWN;
2697 break;
2698 case DM_POLYTOPE_QUADRILATERAL:
2699 ct = DM_POLYTOPE_UNKNOWN;
2700 break;
2701 case DM_POLYTOPE_TETRAHEDRON:
2702 ct = DM_POLYTOPE_UNKNOWN;
2703 break;
2704 case DM_POLYTOPE_HEXAHEDRON:
2705 ct = DM_POLYTOPE_UNKNOWN;
2706 break;
2707 default:
2708 ct = DM_POLYTOPE_UNKNOWN;
2709 }
2710 break;
2711 case DM_POLYTOPE_TETRAHEDRON:
2712 switch (ct2) {
2713 case DM_POLYTOPE_POINT:
2714 ct = ct1;
2715 break;
2716 case DM_POLYTOPE_SEGMENT:
2717 ct = DM_POLYTOPE_UNKNOWN;
2718 break;
2719 case DM_POLYTOPE_TRIANGLE:
2720 ct = DM_POLYTOPE_UNKNOWN;
2721 break;
2722 case DM_POLYTOPE_QUADRILATERAL:
2723 ct = DM_POLYTOPE_UNKNOWN;
2724 break;
2725 case DM_POLYTOPE_TETRAHEDRON:
2726 ct = DM_POLYTOPE_UNKNOWN;
2727 break;
2728 case DM_POLYTOPE_HEXAHEDRON:
2729 ct = DM_POLYTOPE_UNKNOWN;
2730 break;
2731 default:
2732 ct = DM_POLYTOPE_UNKNOWN;
2733 }
2734 break;
2735 case DM_POLYTOPE_HEXAHEDRON:
2736 switch (ct2) {
2737 case DM_POLYTOPE_POINT:
2738 ct = ct1;
2739 break;
2740 case DM_POLYTOPE_SEGMENT:
2741 ct = DM_POLYTOPE_UNKNOWN;
2742 break;
2743 case DM_POLYTOPE_TRIANGLE:
2744 ct = DM_POLYTOPE_UNKNOWN;
2745 break;
2746 case DM_POLYTOPE_QUADRILATERAL:
2747 ct = DM_POLYTOPE_UNKNOWN;
2748 break;
2749 case DM_POLYTOPE_TETRAHEDRON:
2750 ct = DM_POLYTOPE_UNKNOWN;
2751 break;
2752 case DM_POLYTOPE_HEXAHEDRON:
2753 ct = DM_POLYTOPE_UNKNOWN;
2754 break;
2755 default:
2756 ct = DM_POLYTOPE_UNKNOWN;
2757 }
2758 break;
2759 default:
2760 ct = DM_POLYTOPE_UNKNOWN;
2761 }
2762 dim = dim1 + dim2;
2763 Nc = Nc1;
2764 Np = Np1 * Np2;
2765 order = order1;
2766 PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2767 PetscCall(PetscQuadratureSetCellType(*q, ct));
2768 PetscCall(PetscQuadratureSetOrder(*q, order));
2769 PetscCall(PetscMalloc1(Np * dim, &x));
2770 PetscCall(PetscMalloc1(Np, &w));
2771 for (qa = 0, qc = 0; qa < Np1; ++qa) {
2772 for (qb = 0; qb < Np2; ++qb, ++qc) {
2773 for (d1 = 0, d = 0; d1 < dim1; ++d1, ++d) x[qc * dim + d] = x1[qa * dim1 + d1];
2774 for (d2 = 0; d2 < dim2; ++d2, ++d) x[qc * dim + d] = x2[qb * dim2 + d2];
2775 w[qc] = w1[qa] * w2[qb];
2776 }
2777 }
2778 PetscCall(PetscQuadratureSetData(*q, dim, Nc, Np, x, w));
2779 PetscFunctionReturn(PETSC_SUCCESS);
2780 }
2781
2782 /* Overwrites A. Can only handle full-rank problems with m>=n
2783 A in column-major format
2784 Ainv in row-major format
2785 tau has length m
2786 worksize must be >= max(1,n)
2787 */
PetscDTPseudoInverseQR(PetscInt m,PetscInt mstride,PetscInt n,PetscReal * A_in,PetscReal * Ainv_out,PetscScalar * tau,PetscInt worksize,PetscScalar * work)2788 static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m, PetscInt mstride, PetscInt n, PetscReal *A_in, PetscReal *Ainv_out, PetscScalar *tau, PetscInt worksize, PetscScalar *work)
2789 {
2790 PetscBLASInt M, N, K, lda, ldb, ldwork, info;
2791 PetscScalar *A, *Ainv, *R, *Q, Alpha;
2792
2793 PetscFunctionBegin;
2794 #if defined(PETSC_USE_COMPLEX)
2795 {
2796 PetscInt i, j;
2797 PetscCall(PetscMalloc2(m * n, &A, m * n, &Ainv));
2798 for (j = 0; j < n; j++) {
2799 for (i = 0; i < m; i++) A[i + m * j] = A_in[i + mstride * j];
2800 }
2801 mstride = m;
2802 }
2803 #else
2804 A = A_in;
2805 Ainv = Ainv_out;
2806 #endif
2807
2808 PetscCall(PetscBLASIntCast(m, &M));
2809 PetscCall(PetscBLASIntCast(n, &N));
2810 PetscCall(PetscBLASIntCast(mstride, &lda));
2811 PetscCall(PetscBLASIntCast(worksize, &ldwork));
2812 PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
2813 PetscCallBLAS("LAPACKgeqrf", LAPACKgeqrf_(&M, &N, A, &lda, tau, work, &ldwork, &info));
2814 PetscCall(PetscFPTrapPop());
2815 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xGEQRF error");
2816 R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */
2817
2818 /* Extract an explicit representation of Q */
2819 Q = Ainv;
2820 PetscCall(PetscArraycpy(Q, A, mstride * n));
2821 K = N; /* full rank */
2822 PetscCallBLAS("LAPACKorgqr", LAPACKorgqr_(&M, &N, &K, Q, &lda, tau, work, &ldwork, &info));
2823 PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xORGQR/xUNGQR error");
2824
2825 /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
2826 Alpha = 1.0;
2827 ldb = lda;
2828 PetscCallBLAS("BLAStrsm", BLAStrsm_("Right", "Upper", "ConjugateTranspose", "NotUnitTriangular", &M, &N, &Alpha, R, &lda, Q, &ldb));
2829 /* Ainv is Q, overwritten with inverse */
2830
2831 #if defined(PETSC_USE_COMPLEX)
2832 {
2833 PetscInt i;
2834 for (i = 0; i < m * n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
2835 PetscCall(PetscFree2(A, Ainv));
2836 }
2837 #endif
2838 PetscFunctionReturn(PETSC_SUCCESS);
2839 }
2840
2841 /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
PetscDTLegendreIntegrate(PetscInt ninterval,const PetscReal * x,PetscInt ndegree,const PetscInt * degrees,PetscBool Transpose,PetscReal * B)2842 static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval, const PetscReal *x, PetscInt ndegree, const PetscInt *degrees, PetscBool Transpose, PetscReal *B)
2843 {
2844 PetscReal *Bv;
2845 PetscInt i, j;
2846
2847 PetscFunctionBegin;
2848 PetscCall(PetscMalloc1((ninterval + 1) * ndegree, &Bv));
2849 /* Point evaluation of L_p on all the source vertices */
2850 PetscCall(PetscDTLegendreEval(ninterval + 1, x, ndegree, degrees, Bv, NULL, NULL));
2851 /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
2852 for (i = 0; i < ninterval; i++) {
2853 for (j = 0; j < ndegree; j++) {
2854 if (Transpose) B[i + ninterval * j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
2855 else B[i * ndegree + j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
2856 }
2857 }
2858 PetscCall(PetscFree(Bv));
2859 PetscFunctionReturn(PETSC_SUCCESS);
2860 }
2861
2862 /*@
2863 PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals
2864
2865 Not Collective
2866
2867 Input Parameters:
2868 + degree - degree of reconstruction polynomial
2869 . nsource - number of source intervals
2870 . sourcex - sorted coordinates of source cell boundaries (length `nsource`+1)
2871 . ntarget - number of target intervals
2872 - targetx - sorted coordinates of target cell boundaries (length `ntarget`+1)
2873
2874 Output Parameter:
2875 . R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]
2876
2877 Level: advanced
2878
2879 .seealso: `PetscDTLegendreEval()`
2880 @*/
PetscDTReconstructPoly(PetscInt degree,PetscInt nsource,const PetscReal sourcex[],PetscInt ntarget,const PetscReal targetx[],PetscReal R[])2881 PetscErrorCode PetscDTReconstructPoly(PetscInt degree, PetscInt nsource, const PetscReal sourcex[], PetscInt ntarget, const PetscReal targetx[], PetscReal R[])
2882 {
2883 PetscInt i, j, k, *bdegrees, worksize;
2884 PetscReal xmin, xmax, center, hscale, *sourcey, *targety, *Bsource, *Bsinv, *Btarget;
2885 PetscScalar *tau, *work;
2886
2887 PetscFunctionBegin;
2888 PetscAssertPointer(sourcex, 3);
2889 PetscAssertPointer(targetx, 5);
2890 PetscAssertPointer(R, 6);
2891 PetscCheck(degree < nsource, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Reconstruction degree %" PetscInt_FMT " must be less than number of source intervals %" PetscInt_FMT, degree, nsource);
2892 if (PetscDefined(USE_DEBUG)) {
2893 for (i = 0; i < nsource; i++) PetscCheck(sourcex[i] < sourcex[i + 1], PETSC_COMM_SELF, PETSC_ERR_ARG_CORRUPT, "Source interval %" PetscInt_FMT " has negative orientation (%g,%g)", i, (double)sourcex[i], (double)sourcex[i + 1]);
2894 for (i = 0; i < ntarget; i++) PetscCheck(targetx[i] < targetx[i + 1], PETSC_COMM_SELF, PETSC_ERR_ARG_CORRUPT, "Target interval %" PetscInt_FMT " has negative orientation (%g,%g)", i, (double)targetx[i], (double)targetx[i + 1]);
2895 }
2896 xmin = PetscMin(sourcex[0], targetx[0]);
2897 xmax = PetscMax(sourcex[nsource], targetx[ntarget]);
2898 center = (xmin + xmax) / 2;
2899 hscale = (xmax - xmin) / 2;
2900 worksize = nsource;
2901 PetscCall(PetscMalloc4(degree + 1, &bdegrees, nsource + 1, &sourcey, nsource * (degree + 1), &Bsource, worksize, &work));
2902 PetscCall(PetscMalloc4(nsource, &tau, nsource * (degree + 1), &Bsinv, ntarget + 1, &targety, ntarget * (degree + 1), &Btarget));
2903 for (i = 0; i <= nsource; i++) sourcey[i] = (sourcex[i] - center) / hscale;
2904 for (i = 0; i <= degree; i++) bdegrees[i] = i + 1;
2905 PetscCall(PetscDTLegendreIntegrate(nsource, sourcey, degree + 1, bdegrees, PETSC_TRUE, Bsource));
2906 PetscCall(PetscDTPseudoInverseQR(nsource, nsource, degree + 1, Bsource, Bsinv, tau, nsource, work));
2907 for (i = 0; i <= ntarget; i++) targety[i] = (targetx[i] - center) / hscale;
2908 PetscCall(PetscDTLegendreIntegrate(ntarget, targety, degree + 1, bdegrees, PETSC_FALSE, Btarget));
2909 for (i = 0; i < ntarget; i++) {
2910 PetscReal rowsum = 0;
2911 for (j = 0; j < nsource; j++) {
2912 PetscReal sum = 0;
2913 for (k = 0; k < degree + 1; k++) sum += Btarget[i * (degree + 1) + k] * Bsinv[k * nsource + j];
2914 R[i * nsource + j] = sum;
2915 rowsum += sum;
2916 }
2917 for (j = 0; j < nsource; j++) R[i * nsource + j] /= rowsum; /* normalize each row */
2918 }
2919 PetscCall(PetscFree4(bdegrees, sourcey, Bsource, work));
2920 PetscCall(PetscFree4(tau, Bsinv, targety, Btarget));
2921 PetscFunctionReturn(PETSC_SUCCESS);
2922 }
2923
2924 /*@
2925 PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points
2926
2927 Not Collective
2928
2929 Input Parameters:
2930 + n - the number of GLL nodes
2931 . nodes - the GLL nodes
2932 . weights - the GLL weights
2933 - f - the function values at the nodes
2934
2935 Output Parameter:
2936 . in - the value of the integral
2937
2938 Level: beginner
2939
2940 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`
2941 @*/
PetscGaussLobattoLegendreIntegrate(PetscInt n,PetscReal nodes[],PetscReal weights[],const PetscReal f[],PetscReal * in)2942 PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n, PetscReal nodes[], PetscReal weights[], const PetscReal f[], PetscReal *in)
2943 {
2944 PetscInt i;
2945
2946 PetscFunctionBegin;
2947 *in = 0.;
2948 for (i = 0; i < n; i++) *in += f[i] * f[i] * weights[i];
2949 PetscFunctionReturn(PETSC_SUCCESS);
2950 }
2951
2952 /*@C
2953 PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element
2954
2955 Not Collective
2956
2957 Input Parameters:
2958 + n - the number of GLL nodes
2959 . nodes - the GLL nodes, of length `n`
2960 - weights - the GLL weights, of length `n`
2961
2962 Output Parameter:
2963 . AA - the stiffness element, of size `n` by `n`
2964
2965 Level: beginner
2966
2967 Notes:
2968 Destroy this with `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2969
2970 You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row-oriented (the array is symmetric)
2971
2972 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2973 @*/
PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n,PetscReal nodes[],PetscReal weights[],PetscReal *** AA)2974 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
2975 {
2976 PetscReal **A;
2977 const PetscReal *gllnodes = nodes;
2978 const PetscInt p = n - 1;
2979 PetscReal z0, z1, z2 = -1, x, Lpj, Lpr;
2980 PetscInt i, j, nn, r;
2981
2982 PetscFunctionBegin;
2983 PetscCall(PetscMalloc1(n, &A));
2984 PetscCall(PetscMalloc1(n * n, &A[0]));
2985 for (i = 1; i < n; i++) A[i] = A[i - 1] + n;
2986
2987 for (j = 1; j < p; j++) {
2988 x = gllnodes[j];
2989 z0 = 1.;
2990 z1 = x;
2991 for (nn = 1; nn < p; nn++) {
2992 z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2993 z0 = z1;
2994 z1 = z2;
2995 }
2996 Lpj = z2;
2997 for (r = 1; r < p; r++) {
2998 if (r == j) {
2999 A[j][j] = 2. / (3. * (1. - gllnodes[j] * gllnodes[j]) * Lpj * Lpj);
3000 } else {
3001 x = gllnodes[r];
3002 z0 = 1.;
3003 z1 = x;
3004 for (nn = 1; nn < p; nn++) {
3005 z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
3006 z0 = z1;
3007 z1 = z2;
3008 }
3009 Lpr = z2;
3010 A[r][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * Lpr * (gllnodes[j] - gllnodes[r]) * (gllnodes[j] - gllnodes[r]));
3011 }
3012 }
3013 }
3014 for (j = 1; j < p + 1; j++) {
3015 x = gllnodes[j];
3016 z0 = 1.;
3017 z1 = x;
3018 for (nn = 1; nn < p; nn++) {
3019 z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
3020 z0 = z1;
3021 z1 = z2;
3022 }
3023 Lpj = z2;
3024 A[j][0] = 4. * PetscPowRealInt(-1., p) / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. + gllnodes[j]) * (1. + gllnodes[j]));
3025 A[0][j] = A[j][0];
3026 }
3027 for (j = 0; j < p; j++) {
3028 x = gllnodes[j];
3029 z0 = 1.;
3030 z1 = x;
3031 for (nn = 1; nn < p; nn++) {
3032 z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
3033 z0 = z1;
3034 z1 = z2;
3035 }
3036 Lpj = z2;
3037
3038 A[p][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. - gllnodes[j]) * (1. - gllnodes[j]));
3039 A[j][p] = A[p][j];
3040 }
3041 A[0][0] = 0.5 + (((PetscReal)p) * (((PetscReal)p) + 1.) - 2.) / 6.;
3042 A[p][p] = A[0][0];
3043 *AA = A;
3044 PetscFunctionReturn(PETSC_SUCCESS);
3045 }
3046
3047 /*@C
3048 PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element created with `PetscGaussLobattoLegendreElementLaplacianCreate()`
3049
3050 Not Collective
3051
3052 Input Parameters:
3053 + n - the number of GLL nodes
3054 . nodes - the GLL nodes, ignored
3055 . weights - the GLL weightss, ignored
3056 - AA - the stiffness element from `PetscGaussLobattoLegendreElementLaplacianCreate()`
3057
3058 Level: beginner
3059
3060 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`
3061 @*/
PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n,PetscReal nodes[],PetscReal weights[],PetscReal *** AA)3062 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
3063 {
3064 PetscFunctionBegin;
3065 PetscCall(PetscFree((*AA)[0]));
3066 PetscCall(PetscFree(*AA));
3067 *AA = NULL;
3068 PetscFunctionReturn(PETSC_SUCCESS);
3069 }
3070
3071 /*@C
3072 PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element
3073
3074 Not Collective
3075
3076 Input Parameters:
3077 + n - the number of GLL nodes
3078 . nodes - the GLL nodes, of length `n`
3079 - weights - the GLL weights, of length `n`
3080
3081 Output Parameters:
3082 + AA - the stiffness element, of dimension `n` by `n`
3083 - AAT - the transpose of AA (pass in `NULL` if you do not need this array), of dimension `n` by `n`
3084
3085 Level: beginner
3086
3087 Notes:
3088 Destroy this with `PetscGaussLobattoLegendreElementGradientDestroy()`
3089
3090 You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row-oriented
3091
3092 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`, `PetscGaussLobattoLegendreElementGradientDestroy()`
3093 @*/
PetscGaussLobattoLegendreElementGradientCreate(PetscInt n,PetscReal nodes[],PetscReal weights[],PetscReal *** AA,PetscReal *** AAT)3094 PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA, PetscReal ***AAT)
3095 {
3096 PetscReal **A, **AT = NULL;
3097 const PetscReal *gllnodes = nodes;
3098 const PetscInt p = n - 1;
3099 PetscReal Li, Lj, d0;
3100 PetscInt i, j;
3101
3102 PetscFunctionBegin;
3103 PetscCall(PetscMalloc1(n, &A));
3104 PetscCall(PetscMalloc1(n * n, &A[0]));
3105 for (i = 1; i < n; i++) A[i] = A[i - 1] + n;
3106
3107 if (AAT) {
3108 PetscCall(PetscMalloc1(n, &AT));
3109 PetscCall(PetscMalloc1(n * n, &AT[0]));
3110 for (i = 1; i < n; i++) AT[i] = AT[i - 1] + n;
3111 }
3112
3113 if (n == 1) A[0][0] = 0.;
3114 d0 = (PetscReal)p * ((PetscReal)p + 1.) / 4.;
3115 for (i = 0; i < n; i++) {
3116 for (j = 0; j < n; j++) {
3117 A[i][j] = 0.;
3118 PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[i], &Li));
3119 PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[j], &Lj));
3120 if (i != j) A[i][j] = Li / (Lj * (gllnodes[i] - gllnodes[j]));
3121 if ((j == i) && (i == 0)) A[i][j] = -d0;
3122 if (j == i && i == p) A[i][j] = d0;
3123 if (AT) AT[j][i] = A[i][j];
3124 }
3125 }
3126 if (AAT) *AAT = AT;
3127 *AA = A;
3128 PetscFunctionReturn(PETSC_SUCCESS);
3129 }
3130
3131 /*@C
3132 PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`
3133
3134 Not Collective
3135
3136 Input Parameters:
3137 + n - the number of GLL nodes
3138 . nodes - the GLL nodes, ignored
3139 . weights - the GLL weights, ignored
3140 . AA - the stiffness element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`
3141 - AAT - the transpose of the element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`
3142
3143 Level: beginner
3144
3145 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
3146 @*/
PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n,PetscReal nodes[],PetscReal weights[],PetscReal *** AA,PetscReal *** AAT)3147 PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA, PetscReal ***AAT)
3148 {
3149 PetscFunctionBegin;
3150 PetscCall(PetscFree((*AA)[0]));
3151 PetscCall(PetscFree(*AA));
3152 *AA = NULL;
3153 if (AAT) {
3154 PetscCall(PetscFree((*AAT)[0]));
3155 PetscCall(PetscFree(*AAT));
3156 *AAT = NULL;
3157 }
3158 PetscFunctionReturn(PETSC_SUCCESS);
3159 }
3160
3161 /*@C
3162 PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element
3163
3164 Not Collective
3165
3166 Input Parameters:
3167 + n - the number of GLL nodes
3168 . nodes - the GLL nodes, of length `n`
3169 - weights - the GLL weights, of length `n`
3170
3171 Output Parameter:
3172 . AA - the stiffness element, of dimension `n` by `n`
3173
3174 Level: beginner
3175
3176 Notes:
3177 Destroy this with `PetscGaussLobattoLegendreElementAdvectionDestroy()`
3178
3179 This is the same as the Gradient operator multiplied by the diagonal mass matrix
3180
3181 You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row-oriented
3182
3183 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionDestroy()`
3184 @*/
PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n,PetscReal nodes[],PetscReal weights[],PetscReal *** AA)3185 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
3186 {
3187 PetscReal **D;
3188 const PetscReal *gllweights = weights;
3189 const PetscInt glln = n;
3190 PetscInt i, j;
3191
3192 PetscFunctionBegin;
3193 PetscCall(PetscGaussLobattoLegendreElementGradientCreate(n, nodes, weights, &D, NULL));
3194 for (i = 0; i < glln; i++) {
3195 for (j = 0; j < glln; j++) D[i][j] = gllweights[i] * D[i][j];
3196 }
3197 *AA = D;
3198 PetscFunctionReturn(PETSC_SUCCESS);
3199 }
3200
3201 /*@C
3202 PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element created with `PetscGaussLobattoLegendreElementAdvectionCreate()`
3203
3204 Not Collective
3205
3206 Input Parameters:
3207 + n - the number of GLL nodes
3208 . nodes - the GLL nodes, ignored
3209 . weights - the GLL weights, ignored
3210 - AA - advection obtained with `PetscGaussLobattoLegendreElementAdvectionCreate()`
3211
3212 Level: beginner
3213
3214 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
3215 @*/
PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n,PetscReal nodes[],PetscReal weights[],PetscReal *** AA)3216 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
3217 {
3218 PetscFunctionBegin;
3219 PetscCall(PetscFree((*AA)[0]));
3220 PetscCall(PetscFree(*AA));
3221 *AA = NULL;
3222 PetscFunctionReturn(PETSC_SUCCESS);
3223 }
3224
PetscGaussLobattoLegendreElementMassCreate(PetscInt n,PetscReal * nodes,PetscReal * weights,PetscReal *** AA)3225 PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
3226 {
3227 PetscReal **A;
3228 const PetscReal *gllweights = weights;
3229 const PetscInt glln = n;
3230 PetscInt i, j;
3231
3232 PetscFunctionBegin;
3233 PetscCall(PetscMalloc1(glln, &A));
3234 PetscCall(PetscMalloc1(glln * glln, &A[0]));
3235 for (i = 1; i < glln; i++) A[i] = A[i - 1] + glln;
3236 if (glln == 1) A[0][0] = 0.;
3237 for (i = 0; i < glln; i++) {
3238 for (j = 0; j < glln; j++) {
3239 A[i][j] = 0.;
3240 if (j == i) A[i][j] = gllweights[i];
3241 }
3242 }
3243 *AA = A;
3244 PetscFunctionReturn(PETSC_SUCCESS);
3245 }
3246
PetscGaussLobattoLegendreElementMassDestroy(PetscInt n,PetscReal * nodes,PetscReal * weights,PetscReal *** AA)3247 PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
3248 {
3249 PetscFunctionBegin;
3250 PetscCall(PetscFree((*AA)[0]));
3251 PetscCall(PetscFree(*AA));
3252 *AA = NULL;
3253 PetscFunctionReturn(PETSC_SUCCESS);
3254 }
3255
3256 /*@
3257 PetscDTIndexToBary - convert an index into a barycentric coordinate.
3258
3259 Input Parameters:
3260 + len - the desired length of the barycentric tuple (usually 1 more than the dimension it represents, so a barycentric coordinate in a triangle has length 3)
3261 . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
3262 - index - the index to convert: should be >= 0 and < Binomial(len - 1 + sum, sum)
3263
3264 Output Parameter:
3265 . coord - will be filled with the barycentric coordinate, of length `n`
3266
3267 Level: beginner
3268
3269 Note:
3270 The indices map to barycentric coordinates in lexicographic order, where the first index is the
3271 least significant and the last index is the most significant.
3272
3273 .seealso: `PetscDTBaryToIndex()`
3274 @*/
PetscDTIndexToBary(PetscInt len,PetscInt sum,PetscInt index,PetscInt coord[])3275 PetscErrorCode PetscDTIndexToBary(PetscInt len, PetscInt sum, PetscInt index, PetscInt coord[])
3276 {
3277 PetscInt c, d, s, total, subtotal, nexttotal;
3278
3279 PetscFunctionBeginHot;
3280 PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
3281 PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
3282 if (!len) {
3283 PetscCheck(!sum && !index, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
3284 PetscFunctionReturn(PETSC_SUCCESS);
3285 }
3286 for (c = 1, total = 1; c <= len; c++) {
3287 /* total is the number of ways to have a tuple of length c with sum */
3288 if (index < total) break;
3289 total = (total * (sum + c)) / c;
3290 }
3291 PetscCheck(c <= len, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index out of range");
3292 for (d = c; d < len; d++) coord[d] = 0;
3293 for (s = 0, subtotal = 1, nexttotal = 1; c > 0;) {
3294 /* subtotal is the number of ways to have a tuple of length c with sum s */
3295 /* nexttotal is the number of ways to have a tuple of length c-1 with sum s */
3296 if ((index + subtotal) >= total) {
3297 coord[--c] = sum - s;
3298 index -= (total - subtotal);
3299 sum = s;
3300 total = nexttotal;
3301 subtotal = 1;
3302 nexttotal = 1;
3303 s = 0;
3304 } else {
3305 subtotal = (subtotal * (c + s)) / (s + 1);
3306 nexttotal = (nexttotal * (c - 1 + s)) / (s + 1);
3307 s++;
3308 }
3309 }
3310 PetscFunctionReturn(PETSC_SUCCESS);
3311 }
3312
3313 /*@
3314 PetscDTBaryToIndex - convert a barycentric coordinate to an index
3315
3316 Input Parameters:
3317 + len - the desired length of the barycentric tuple (usually 1 more than the dimension it represents, so a barycentric coordinate in a triangle has length 3)
3318 . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
3319 - coord - a barycentric coordinate with the given length `len` and `sum`
3320
3321 Output Parameter:
3322 . index - the unique index for the coordinate, >= 0 and < Binomial(len - 1 + sum, sum)
3323
3324 Level: beginner
3325
3326 Note:
3327 The indices map to barycentric coordinates in lexicographic order, where the first index is the
3328 least significant and the last index is the most significant.
3329
3330 .seealso: `PetscDTIndexToBary`
3331 @*/
PetscDTBaryToIndex(PetscInt len,PetscInt sum,const PetscInt coord[],PetscInt * index)3332 PetscErrorCode PetscDTBaryToIndex(PetscInt len, PetscInt sum, const PetscInt coord[], PetscInt *index)
3333 {
3334 PetscInt c;
3335 PetscInt i;
3336 PetscInt total;
3337
3338 PetscFunctionBeginHot;
3339 PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
3340 if (!len) {
3341 PetscCheck(!sum, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
3342 *index = 0;
3343 PetscFunctionReturn(PETSC_SUCCESS);
3344 }
3345 for (c = 1, total = 1; c < len; c++) total = (total * (sum + c)) / c;
3346 i = total - 1;
3347 c = len - 1;
3348 sum -= coord[c];
3349 while (sum > 0) {
3350 PetscInt subtotal;
3351 PetscInt s;
3352
3353 for (s = 1, subtotal = 1; s < sum; s++) subtotal = (subtotal * (c + s)) / s;
3354 i -= subtotal;
3355 sum -= coord[--c];
3356 }
3357 *index = i;
3358 PetscFunctionReturn(PETSC_SUCCESS);
3359 }
3360
3361 /*@
3362 PetscQuadratureComputePermutations - Compute permutations of quadrature points corresponding to domain orientations
3363
3364 Input Parameter:
3365 . quad - The `PetscQuadrature`
3366
3367 Output Parameters:
3368 + Np - The number of domain orientations
3369 - perm - An array of `IS` permutations, one for ech orientation,
3370
3371 Level: developer
3372
3373 .seealso: `PetscQuadratureSetCellType()`, `PetscQuadrature`
3374 @*/
PetscQuadratureComputePermutations(PetscQuadrature quad,PeOp PetscInt * Np,IS * perm[])3375 PetscErrorCode PetscQuadratureComputePermutations(PetscQuadrature quad, PeOp PetscInt *Np, IS *perm[])
3376 {
3377 DMPolytopeType ct;
3378 const PetscReal *xq, *wq;
3379 PetscInt dim, qdim, d, Na, o, Nq, q, qp;
3380
3381 PetscFunctionBegin;
3382 PetscCall(PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &xq, &wq));
3383 PetscCall(PetscQuadratureGetCellType(quad, &ct));
3384 dim = DMPolytopeTypeGetDim(ct);
3385 Na = DMPolytopeTypeGetNumArrangements(ct);
3386 PetscCall(PetscMalloc1(Na, perm));
3387 if (Np) *Np = Na;
3388 Na /= 2;
3389 for (o = -Na; o < Na; ++o) {
3390 DM refdm;
3391 PetscInt *idx;
3392 PetscReal xi0[3] = {-1., -1., -1.}, v0[3], J[9], detJ, txq[3];
3393 PetscBool flg;
3394
3395 PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, ct, &refdm));
3396 PetscCall(DMPlexOrientPoint(refdm, 0, o));
3397 PetscCall(DMPlexComputeCellGeometryFEM(refdm, 0, NULL, v0, J, NULL, &detJ));
3398 PetscCall(PetscMalloc1(Nq, &idx));
3399 for (q = 0; q < Nq; ++q) {
3400 CoordinatesRefToReal(dim, dim, xi0, v0, J, &xq[q * dim], txq);
3401 for (qp = 0; qp < Nq; ++qp) {
3402 PetscReal diff = 0.;
3403
3404 for (d = 0; d < dim; ++d) diff += PetscAbsReal(txq[d] - xq[qp * dim + d]);
3405 if (diff < PETSC_SMALL) break;
3406 }
3407 PetscCheck(qp < Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Transformed quad point %" PetscInt_FMT " does not match another quad point", q);
3408 idx[q] = qp;
3409 }
3410 PetscCall(DMDestroy(&refdm));
3411 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, Nq, idx, PETSC_OWN_POINTER, &(*perm)[o + Na]));
3412 PetscCall(ISGetInfo((*perm)[o + Na], IS_PERMUTATION, IS_LOCAL, PETSC_TRUE, &flg));
3413 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ordering for orientation %" PetscInt_FMT " was not a permutation", o);
3414 PetscCall(ISSetPermutation((*perm)[o + Na]));
3415 }
3416 if (!Na) (*perm)[0] = NULL;
3417 PetscFunctionReturn(PETSC_SUCCESS);
3418 }
3419
3420 /*@
3421 PetscDTCreateQuadratureByCell - Create default quadrature for a given cell
3422
3423 Not collective
3424
3425 Input Parameters:
3426 + ct - The integration domain
3427 . qorder - The desired quadrature order
3428 - qtype - The type of simplex quadrature, or PETSCDTSIMPLEXQUAD_DEFAULT
3429
3430 Output Parameters:
3431 + q - The cell quadrature
3432 - fq - The face quadrature
3433
3434 Level: developer
3435
3436 .seealso: `PetscDTCreateDefaultQuadrature()`, `PetscFECreateDefault()`, `PetscDTGaussTensorQuadrature()`, `PetscDTSimplexQuadrature()`, `PetscDTTensorQuadratureCreate()`
3437 @*/
PetscDTCreateQuadratureByCell(DMPolytopeType ct,PetscInt qorder,PetscDTSimplexQuadratureType qtype,PetscQuadrature * q,PetscQuadrature * fq)3438 PetscErrorCode PetscDTCreateQuadratureByCell(DMPolytopeType ct, PetscInt qorder, PetscDTSimplexQuadratureType qtype, PetscQuadrature *q, PetscQuadrature *fq)
3439 {
3440 const PetscInt quadPointsPerEdge = PetscMax(qorder + 1, 1);
3441 const PetscInt dim = DMPolytopeTypeGetDim(ct);
3442
3443 PetscFunctionBegin;
3444 switch (ct) {
3445 case DM_POLYTOPE_SEGMENT:
3446 case DM_POLYTOPE_POINT_PRISM_TENSOR:
3447 case DM_POLYTOPE_QUADRILATERAL:
3448 case DM_POLYTOPE_SEG_PRISM_TENSOR:
3449 case DM_POLYTOPE_HEXAHEDRON:
3450 case DM_POLYTOPE_QUAD_PRISM_TENSOR:
3451 PetscCall(PetscDTGaussTensorQuadrature(dim, 1, quadPointsPerEdge, -1.0, 1.0, q));
3452 PetscCall(PetscDTGaussTensorQuadrature(dim - 1, 1, quadPointsPerEdge, -1.0, 1.0, fq));
3453 break;
3454 case DM_POLYTOPE_TRIANGLE:
3455 case DM_POLYTOPE_TETRAHEDRON:
3456 PetscCall(PetscDTSimplexQuadrature(dim, 2 * qorder, qtype, q));
3457 PetscCall(PetscDTSimplexQuadrature(dim - 1, 2 * qorder, qtype, fq));
3458 break;
3459 case DM_POLYTOPE_TRI_PRISM:
3460 case DM_POLYTOPE_TRI_PRISM_TENSOR: {
3461 PetscQuadrature q1, q2;
3462
3463 // TODO: this should be able to use symmetric rules, but doing so causes tests to fail
3464 PetscCall(PetscDTSimplexQuadrature(2, 2 * qorder, PETSCDTSIMPLEXQUAD_CONIC, &q1));
3465 PetscCall(PetscDTGaussTensorQuadrature(1, 1, quadPointsPerEdge, -1.0, 1.0, &q2));
3466 PetscCall(PetscDTTensorQuadratureCreate(q1, q2, q));
3467 PetscCall(PetscQuadratureDestroy(&q2));
3468 *fq = q1;
3469 /* TODO Need separate quadratures for each face */
3470 } break;
3471 default:
3472 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No quadrature for celltype %s", DMPolytopeTypes[PetscMin(ct, DM_POLYTOPE_UNKNOWN)]);
3473 }
3474 PetscFunctionReturn(PETSC_SUCCESS);
3475 }
3476
3477 /*@
3478 PetscDTCreateDefaultQuadrature - Create default quadrature for a given cell
3479
3480 Not collective
3481
3482 Input Parameters:
3483 + ct - The integration domain
3484 - qorder - The desired quadrature order
3485
3486 Output Parameters:
3487 + q - The cell quadrature
3488 - fq - The face quadrature
3489
3490 Level: developer
3491
3492 .seealso: `PetscDTCreateQuadratureByCell()`, `PetscFECreateDefault()`, `PetscDTGaussTensorQuadrature()`, `PetscDTSimplexQuadrature()`, `PetscDTTensorQuadratureCreate()`
3493 @*/
PetscDTCreateDefaultQuadrature(DMPolytopeType ct,PetscInt qorder,PetscQuadrature * q,PetscQuadrature * fq)3494 PetscErrorCode PetscDTCreateDefaultQuadrature(DMPolytopeType ct, PetscInt qorder, PetscQuadrature *q, PetscQuadrature *fq)
3495 {
3496 PetscFunctionBegin;
3497 PetscCall(PetscDTCreateQuadratureByCell(ct, qorder, PETSCDTSIMPLEXQUAD_DEFAULT, q, fq));
3498 PetscFunctionReturn(PETSC_SUCCESS);
3499 }
3500