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