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