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