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