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 static PetscBool GaussCite = PETSC_FALSE; 16 const char GaussCitation[] = "@article{GolubWelsch1969,\n" 17 " author = {Golub and Welsch},\n" 18 " title = {Calculation of Quadrature Rules},\n" 19 " journal = {Math. Comp.},\n" 20 " volume = {23},\n" 21 " number = {106},\n" 22 " pages = {221--230},\n" 23 " year = {1969}\n}\n"; 24 25 /*@ 26 PetscQuadratureCreate - Create a PetscQuadrature object 27 28 Collective 29 30 Input Parameter: 31 . comm - The communicator for the PetscQuadrature object 32 33 Output Parameter: 34 . q - The PetscQuadrature object 35 36 Level: beginner 37 38 .seealso: PetscQuadratureDestroy(), PetscQuadratureGetData() 39 @*/ 40 PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q) 41 { 42 PetscErrorCode ierr; 43 44 PetscFunctionBegin; 45 PetscValidPointer(q, 2); 46 ierr = PetscSysInitializePackage();CHKERRQ(ierr); 47 ierr = PetscHeaderCreate(*q,PETSC_OBJECT_CLASSID,"PetscQuadrature","Quadrature","DT",comm,PetscQuadratureDestroy,PetscQuadratureView);CHKERRQ(ierr); 48 (*q)->dim = -1; 49 (*q)->Nc = 1; 50 (*q)->order = -1; 51 (*q)->numPoints = 0; 52 (*q)->points = NULL; 53 (*q)->weights = NULL; 54 PetscFunctionReturn(0); 55 } 56 57 /*@ 58 PetscQuadratureDuplicate - Create a deep copy of the PetscQuadrature object 59 60 Collective on q 61 62 Input Parameter: 63 . q - The PetscQuadrature object 64 65 Output Parameter: 66 . r - The new PetscQuadrature object 67 68 Level: beginner 69 70 .seealso: PetscQuadratureCreate(), PetscQuadratureDestroy(), PetscQuadratureGetData() 71 @*/ 72 PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r) 73 { 74 PetscInt order, dim, Nc, Nq; 75 const PetscReal *points, *weights; 76 PetscReal *p, *w; 77 PetscErrorCode ierr; 78 79 PetscFunctionBegin; 80 PetscValidPointer(q, 2); 81 ierr = PetscQuadratureCreate(PetscObjectComm((PetscObject) q), r);CHKERRQ(ierr); 82 ierr = PetscQuadratureGetOrder(q, &order);CHKERRQ(ierr); 83 ierr = PetscQuadratureSetOrder(*r, order);CHKERRQ(ierr); 84 ierr = PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights);CHKERRQ(ierr); 85 ierr = PetscMalloc1(Nq*dim, &p);CHKERRQ(ierr); 86 ierr = PetscMalloc1(Nq*Nc, &w);CHKERRQ(ierr); 87 ierr = PetscArraycpy(p, points, Nq*dim);CHKERRQ(ierr); 88 ierr = PetscArraycpy(w, weights, Nc * Nq);CHKERRQ(ierr); 89 ierr = PetscQuadratureSetData(*r, dim, Nc, Nq, p, w);CHKERRQ(ierr); 90 PetscFunctionReturn(0); 91 } 92 93 /*@ 94 PetscQuadratureDestroy - Destroys a PetscQuadrature object 95 96 Collective on q 97 98 Input Parameter: 99 . q - The PetscQuadrature object 100 101 Level: beginner 102 103 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData() 104 @*/ 105 PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q) 106 { 107 PetscErrorCode ierr; 108 109 PetscFunctionBegin; 110 if (!*q) PetscFunctionReturn(0); 111 PetscValidHeaderSpecific((*q),PETSC_OBJECT_CLASSID,1); 112 if (--((PetscObject)(*q))->refct > 0) { 113 *q = NULL; 114 PetscFunctionReturn(0); 115 } 116 ierr = PetscFree((*q)->points);CHKERRQ(ierr); 117 ierr = PetscFree((*q)->weights);CHKERRQ(ierr); 118 ierr = PetscHeaderDestroy(q);CHKERRQ(ierr); 119 PetscFunctionReturn(0); 120 } 121 122 /*@ 123 PetscQuadratureGetOrder - Return the order of the method 124 125 Not collective 126 127 Input Parameter: 128 . q - The PetscQuadrature object 129 130 Output Parameter: 131 . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated 132 133 Level: intermediate 134 135 .seealso: PetscQuadratureSetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData() 136 @*/ 137 PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order) 138 { 139 PetscFunctionBegin; 140 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 141 PetscValidPointer(order, 2); 142 *order = q->order; 143 PetscFunctionReturn(0); 144 } 145 146 /*@ 147 PetscQuadratureSetOrder - Return the order of the method 148 149 Not collective 150 151 Input Parameters: 152 + q - The PetscQuadrature object 153 - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated 154 155 Level: intermediate 156 157 .seealso: PetscQuadratureGetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData() 158 @*/ 159 PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order) 160 { 161 PetscFunctionBegin; 162 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 163 q->order = order; 164 PetscFunctionReturn(0); 165 } 166 167 /*@ 168 PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated 169 170 Not collective 171 172 Input Parameter: 173 . q - The PetscQuadrature object 174 175 Output Parameter: 176 . Nc - The number of components 177 178 Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components. 179 180 Level: intermediate 181 182 .seealso: PetscQuadratureSetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData() 183 @*/ 184 PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc) 185 { 186 PetscFunctionBegin; 187 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 188 PetscValidPointer(Nc, 2); 189 *Nc = q->Nc; 190 PetscFunctionReturn(0); 191 } 192 193 /*@ 194 PetscQuadratureSetNumComponents - Return the number of components for functions to be integrated 195 196 Not collective 197 198 Input Parameters: 199 + q - The PetscQuadrature object 200 - Nc - The number of components 201 202 Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components. 203 204 Level: intermediate 205 206 .seealso: PetscQuadratureGetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData() 207 @*/ 208 PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc) 209 { 210 PetscFunctionBegin; 211 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 212 q->Nc = Nc; 213 PetscFunctionReturn(0); 214 } 215 216 /*@C 217 PetscQuadratureGetData - Returns the data defining the quadrature 218 219 Not collective 220 221 Input Parameter: 222 . q - The PetscQuadrature object 223 224 Output Parameters: 225 + dim - The spatial dimension 226 . Nc - The number of components 227 . npoints - The number of quadrature points 228 . points - The coordinates of each quadrature point 229 - weights - The weight of each quadrature point 230 231 Level: intermediate 232 233 Fortran Notes: 234 From Fortran you must call PetscQuadratureRestoreData() when you are done with the data 235 236 .seealso: PetscQuadratureCreate(), PetscQuadratureSetData() 237 @*/ 238 PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PetscInt *dim, PetscInt *Nc, PetscInt *npoints, const PetscReal *points[], const PetscReal *weights[]) 239 { 240 PetscFunctionBegin; 241 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 242 if (dim) { 243 PetscValidPointer(dim, 2); 244 *dim = q->dim; 245 } 246 if (Nc) { 247 PetscValidPointer(Nc, 3); 248 *Nc = q->Nc; 249 } 250 if (npoints) { 251 PetscValidPointer(npoints, 4); 252 *npoints = q->numPoints; 253 } 254 if (points) { 255 PetscValidPointer(points, 5); 256 *points = q->points; 257 } 258 if (weights) { 259 PetscValidPointer(weights, 6); 260 *weights = q->weights; 261 } 262 PetscFunctionReturn(0); 263 } 264 265 static PetscErrorCode PetscDTJacobianInverse_Internal(PetscInt m, PetscInt n, const PetscReal J[], PetscReal Jinv[]) 266 { 267 PetscScalar *Js, *Jinvs; 268 PetscInt i, j, k; 269 PetscBLASInt bm, bn, info; 270 PetscErrorCode ierr; 271 272 PetscFunctionBegin; 273 ierr = PetscBLASIntCast(m, &bm);CHKERRQ(ierr); 274 ierr = PetscBLASIntCast(n, &bn);CHKERRQ(ierr); 275 #if defined(PETSC_USE_COMPLEX) 276 ierr = PetscMalloc2(m*n, &Js, m*n, &Jinvs);CHKERRQ(ierr); 277 for (i = 0; i < m*n; j++) Js[i] = J[i]; 278 #else 279 Js = (PetscReal *) J; 280 Jinvs = Jinv; 281 #endif 282 if (m == n) { 283 PetscBLASInt *pivots; 284 PetscScalar *W; 285 286 ierr = PetscMalloc2(m, &pivots, m, &W);CHKERRQ(ierr); 287 288 ierr = PetscArraycpy(Jinvs, Js, m * m);CHKERRQ(ierr); 289 PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, Jinvs, &bm, pivots, &info)); 290 if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info); 291 PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, Jinvs, &bm, pivots, W, &bm, &info)); 292 if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info); 293 ierr = PetscFree2(pivots, W);CHKERRQ(ierr); 294 } else if (m < n) { 295 PetscScalar *JJT; 296 PetscBLASInt *pivots; 297 PetscScalar *W; 298 299 ierr = PetscMalloc1(m*m, &JJT);CHKERRQ(ierr); 300 ierr = PetscMalloc2(m, &pivots, m, &W);CHKERRQ(ierr); 301 for (i = 0; i < m; i++) { 302 for (j = 0; j < m; j++) { 303 PetscScalar val = 0.; 304 305 for (k = 0; k < n; k++) val += Js[i * n + k] * Js[j * n + k]; 306 JJT[i * m + j] = val; 307 } 308 } 309 310 PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, JJT, &bm, pivots, &info)); 311 if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info); 312 PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, JJT, &bm, pivots, W, &bm, &info)); 313 if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info); 314 for (i = 0; i < n; i++) { 315 for (j = 0; j < m; j++) { 316 PetscScalar val = 0.; 317 318 for (k = 0; k < m; k++) val += Js[k * n + i] * JJT[k * m + j]; 319 Jinvs[i * m + j] = val; 320 } 321 } 322 ierr = PetscFree2(pivots, W);CHKERRQ(ierr); 323 ierr = PetscFree(JJT);CHKERRQ(ierr); 324 } else { 325 PetscScalar *JTJ; 326 PetscBLASInt *pivots; 327 PetscScalar *W; 328 329 ierr = PetscMalloc1(n*n, &JTJ);CHKERRQ(ierr); 330 ierr = PetscMalloc2(n, &pivots, n, &W);CHKERRQ(ierr); 331 for (i = 0; i < n; i++) { 332 for (j = 0; j < n; j++) { 333 PetscScalar val = 0.; 334 335 for (k = 0; k < m; k++) val += Js[k * n + i] * Js[k * n + j]; 336 JTJ[i * n + j] = val; 337 } 338 } 339 340 PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bn, &bn, JTJ, &bm, pivots, &info)); 341 if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info); 342 PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bn, JTJ, &bn, pivots, W, &bn, &info)); 343 if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info); 344 for (i = 0; i < n; i++) { 345 for (j = 0; j < m; j++) { 346 PetscScalar val = 0.; 347 348 for (k = 0; k < n; k++) val += JTJ[i * n + k] * Js[j * n + k]; 349 Jinvs[i * m + j] = val; 350 } 351 } 352 ierr = PetscFree2(pivots, W);CHKERRQ(ierr); 353 ierr = PetscFree(JTJ);CHKERRQ(ierr); 354 } 355 #if defined(PETSC_USE_COMPLEX) 356 for (i = 0; i < m*n; j++) Jinv[i] = PetscRealPart(Jinvs[i]); 357 ierr = PetscFree2(Js, Jinvs);CHKERRQ(ierr); 358 #endif 359 PetscFunctionReturn(0); 360 } 361 362 /*@ 363 PetscQuadraturePushForward - Push forward a quadrature functional under an affine transformation. 364 365 Collecive on PetscQuadrature 366 367 Input Arguments: 368 + q - the quadrature functional 369 . imageDim - the dimension of the image of the transformation 370 . origin - a point in the original space 371 . originImage - the image of the origin under the transformation 372 . J - the Jacobian of the image: an [imageDim x dim] matrix in row major order 373 - formIndex - transform the quadrature weights as k-forms of this index (if the number of components is a multiple of (dim choose formIndex), it is assumed that they represent multiple k-forms) [see PetscDTAltVPullback() for interpretation of formIndex] 374 375 Output Arguments: 376 . 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. 377 378 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. 379 380 .seealso: PetscDTAltVPullback(), PetscDTAltVPullbackMatrix() 381 @*/ 382 PetscErrorCode PetscQuadraturePushForward(PetscQuadrature q, PetscInt imageDim, const PetscReal origin[], const PetscReal originImage[], const PetscReal J[], PetscInt formIndex, PetscQuadrature *Jinvstarq) 383 { 384 PetscInt dim, Nc, imageNc, formSize, Ncopies, imageFormSize, Npoints, pt, i, j, c; 385 const PetscReal *points; 386 const PetscReal *weights; 387 PetscReal *imagePoints, *imageWeights; 388 PetscReal *Jinv; 389 PetscReal *Jinvstar; 390 PetscErrorCode ierr; 391 392 PetscFunctionBegin; 393 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 394 if (imageDim < PetscAbsInt(formIndex)) SETERRQ2(PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Cannot represent a %D form in %D dimensions", PetscAbsInt(formIndex), imageDim); 395 ierr = PetscQuadratureGetData(q, &dim, &Nc, &Npoints, &points, &weights);CHKERRQ(ierr); 396 if (Nc % formSize) SETERRQ2(PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Number of components %D is not a multiple of formSize %D\n", Nc, formSize); 397 Ncopies = Nc / formSize; 398 ierr = PetscDTBinomialInt(dim, PetscAbsInt(formIndex), &formSize);CHKERRQ(ierr); 399 ierr = PetscDTBinomialInt(imageDim, PetscAbsInt(formIndex), &imageFormSize);CHKERRQ(ierr); 400 imageNc = Ncopies * imageFormSize; 401 ierr = PetscMalloc1(Npoints * imageDim, &imagePoints);CHKERRQ(ierr); 402 ierr = PetscMalloc1(Npoints * imageNc, &imageWeights);CHKERRQ(ierr); 403 ierr = PetscMalloc2(imageDim * dim, &Jinv, formSize * imageFormSize, &Jinvstar);CHKERRQ(ierr); 404 ierr = PetscDTJacobianInverse_Internal(dim, imageDim, J, Jinv);CHKERRQ(ierr); 405 ierr = PetscDTAltVPullbackMatrix(imageDim, dim, Jinv, formIndex, Jinvstar);CHKERRQ(ierr); 406 for (pt = 0; pt < Npoints; pt++) { 407 const PetscReal *point = &points[pt * dim]; 408 PetscReal *imagePoint = &imagePoints[pt * imageDim]; 409 410 for (i = 0; i < imageDim; i++) { 411 PetscReal val = originImage[i]; 412 413 for (j = 0; j < dim; j++) val += J[i * dim + j] * (point[j] - origin[j]); 414 imagePoint[i] = val; 415 } 416 for (c = 0; c < Ncopies; c++) { 417 const PetscReal *form = &weights[pt * Nc + c * formSize]; 418 PetscReal *imageForm = &imageWeights[pt * imageNc + c * imageFormSize]; 419 420 for (i = 0; i < imageFormSize; i++) { 421 PetscReal val = 0.; 422 423 for (j = 0; j < formSize; j++) val += Jinvstar[i * formSize + j] * form[j]; 424 imageForm[i] = val; 425 } 426 } 427 } 428 ierr = PetscQuadratureCreate(PetscObjectComm((PetscObject)q), Jinvstarq);CHKERRQ(ierr); 429 ierr = PetscQuadratureSetData(*Jinvstarq, imageDim, imageNc, Npoints, imagePoints, imageWeights);CHKERRQ(ierr); 430 ierr = PetscFree2(Jinv, Jinvstar);CHKERRQ(ierr); 431 PetscFunctionReturn(0); 432 } 433 434 /*@C 435 PetscQuadratureSetData - Sets the data defining the quadrature 436 437 Not collective 438 439 Input Parameters: 440 + q - The PetscQuadrature object 441 . dim - The spatial dimension 442 . Nc - The number of components 443 . npoints - The number of quadrature points 444 . points - The coordinates of each quadrature point 445 - weights - The weight of each quadrature point 446 447 Note: This routine owns the references to points and weights, so they must be allocated using PetscMalloc() and the user should not free them. 448 449 Level: intermediate 450 451 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData() 452 @*/ 453 PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[]) 454 { 455 PetscFunctionBegin; 456 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 457 if (dim >= 0) q->dim = dim; 458 if (Nc >= 0) q->Nc = Nc; 459 if (npoints >= 0) q->numPoints = npoints; 460 if (points) { 461 PetscValidPointer(points, 4); 462 q->points = points; 463 } 464 if (weights) { 465 PetscValidPointer(weights, 5); 466 q->weights = weights; 467 } 468 PetscFunctionReturn(0); 469 } 470 471 static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v) 472 { 473 PetscInt q, d, c; 474 PetscViewerFormat format; 475 PetscErrorCode ierr; 476 477 PetscFunctionBegin; 478 if (quad->Nc > 1) {ierr = PetscViewerASCIIPrintf(v, "Quadrature of order %D on %D points (dim %D) with %D components\n", quad->order, quad->numPoints, quad->dim, quad->Nc);CHKERRQ(ierr);} 479 else {ierr = PetscViewerASCIIPrintf(v, "Quadrature of order %D on %D points (dim %D)\n", quad->order, quad->numPoints, quad->dim);CHKERRQ(ierr);} 480 ierr = PetscViewerGetFormat(v, &format);CHKERRQ(ierr); 481 if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) PetscFunctionReturn(0); 482 for (q = 0; q < quad->numPoints; ++q) { 483 ierr = PetscViewerASCIIPrintf(v, "p%D (", q);CHKERRQ(ierr); 484 ierr = PetscViewerASCIIUseTabs(v, PETSC_FALSE);CHKERRQ(ierr); 485 for (d = 0; d < quad->dim; ++d) { 486 if (d) {ierr = PetscViewerASCIIPrintf(v, ", ");CHKERRQ(ierr);} 487 ierr = PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q*quad->dim+d]);CHKERRQ(ierr); 488 } 489 ierr = PetscViewerASCIIPrintf(v, ") ");CHKERRQ(ierr); 490 if (quad->Nc > 1) {ierr = PetscViewerASCIIPrintf(v, "w%D (", q);CHKERRQ(ierr);} 491 for (c = 0; c < quad->Nc; ++c) { 492 if (c) {ierr = PetscViewerASCIIPrintf(v, ", ");CHKERRQ(ierr);} 493 ierr = PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q*quad->Nc+c]);CHKERRQ(ierr); 494 } 495 if (quad->Nc > 1) {ierr = PetscViewerASCIIPrintf(v, ")");CHKERRQ(ierr);} 496 ierr = PetscViewerASCIIPrintf(v, "\n");CHKERRQ(ierr); 497 ierr = PetscViewerASCIIUseTabs(v, PETSC_TRUE);CHKERRQ(ierr); 498 } 499 PetscFunctionReturn(0); 500 } 501 502 /*@C 503 PetscQuadratureView - Views a PetscQuadrature object 504 505 Collective on quad 506 507 Input Parameters: 508 + quad - The PetscQuadrature object 509 - viewer - The PetscViewer object 510 511 Level: beginner 512 513 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData() 514 @*/ 515 PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer) 516 { 517 PetscBool iascii; 518 PetscErrorCode ierr; 519 520 PetscFunctionBegin; 521 PetscValidHeader(quad, 1); 522 if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2); 523 if (!viewer) {ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject) quad), &viewer);CHKERRQ(ierr);} 524 ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);CHKERRQ(ierr); 525 ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr); 526 if (iascii) {ierr = PetscQuadratureView_Ascii(quad, viewer);CHKERRQ(ierr);} 527 ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr); 528 PetscFunctionReturn(0); 529 } 530 531 /*@C 532 PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement 533 534 Not collective 535 536 Input Parameter: 537 + q - The original PetscQuadrature 538 . numSubelements - The number of subelements the original element is divided into 539 . v0 - An array of the initial points for each subelement 540 - jac - An array of the Jacobian mappings from the reference to each subelement 541 542 Output Parameters: 543 . dim - The dimension 544 545 Note: Together v0 and jac define an affine mapping from the original reference element to each subelement 546 547 Not available from Fortran 548 549 Level: intermediate 550 551 .seealso: PetscFECreate(), PetscSpaceGetDimension(), PetscDualSpaceGetDimension() 552 @*/ 553 PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref) 554 { 555 const PetscReal *points, *weights; 556 PetscReal *pointsRef, *weightsRef; 557 PetscInt dim, Nc, order, npoints, npointsRef, c, p, cp, d, e; 558 PetscErrorCode ierr; 559 560 PetscFunctionBegin; 561 PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1); 562 PetscValidPointer(v0, 3); 563 PetscValidPointer(jac, 4); 564 PetscValidPointer(qref, 5); 565 ierr = PetscQuadratureCreate(PETSC_COMM_SELF, qref);CHKERRQ(ierr); 566 ierr = PetscQuadratureGetOrder(q, &order);CHKERRQ(ierr); 567 ierr = PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights);CHKERRQ(ierr); 568 npointsRef = npoints*numSubelements; 569 ierr = PetscMalloc1(npointsRef*dim,&pointsRef);CHKERRQ(ierr); 570 ierr = PetscMalloc1(npointsRef*Nc, &weightsRef);CHKERRQ(ierr); 571 for (c = 0; c < numSubelements; ++c) { 572 for (p = 0; p < npoints; ++p) { 573 for (d = 0; d < dim; ++d) { 574 pointsRef[(c*npoints + p)*dim+d] = v0[c*dim+d]; 575 for (e = 0; e < dim; ++e) { 576 pointsRef[(c*npoints + p)*dim+d] += jac[(c*dim + d)*dim+e]*(points[p*dim+e] + 1.0); 577 } 578 } 579 /* Could also use detJ here */ 580 for (cp = 0; cp < Nc; ++cp) weightsRef[(c*npoints+p)*Nc+cp] = weights[p*Nc+cp]/numSubelements; 581 } 582 } 583 ierr = PetscQuadratureSetOrder(*qref, order);CHKERRQ(ierr); 584 ierr = PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef);CHKERRQ(ierr); 585 PetscFunctionReturn(0); 586 } 587 588 /*@ 589 PetscDTLegendreEval - evaluate Legendre polynomial at points 590 591 Not Collective 592 593 Input Arguments: 594 + npoints - number of spatial points to evaluate at 595 . points - array of locations to evaluate at 596 . ndegree - number of basis degrees to evaluate 597 - degrees - sorted array of degrees to evaluate 598 599 Output Arguments: 600 + B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL) 601 . D - row-oriented derivative evaluation matrix (or NULL) 602 - D2 - row-oriented second derivative evaluation matrix (or NULL) 603 604 Level: intermediate 605 606 .seealso: PetscDTGaussQuadrature() 607 @*/ 608 PetscErrorCode PetscDTLegendreEval(PetscInt npoints,const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2) 609 { 610 PetscInt i,maxdegree; 611 612 PetscFunctionBegin; 613 if (!npoints || !ndegree) PetscFunctionReturn(0); 614 maxdegree = degrees[ndegree-1]; 615 for (i=0; i<npoints; i++) { 616 PetscReal pm1,pm2,pd1,pd2,pdd1,pdd2,x; 617 PetscInt j,k; 618 x = points[i]; 619 pm2 = 0; 620 pm1 = 1; 621 pd2 = 0; 622 pd1 = 0; 623 pdd2 = 0; 624 pdd1 = 0; 625 k = 0; 626 if (degrees[k] == 0) { 627 if (B) B[i*ndegree+k] = pm1; 628 if (D) D[i*ndegree+k] = pd1; 629 if (D2) D2[i*ndegree+k] = pdd1; 630 k++; 631 } 632 for (j=1; j<=maxdegree; j++,k++) { 633 PetscReal p,d,dd; 634 p = ((2*j-1)*x*pm1 - (j-1)*pm2)/j; 635 d = pd2 + (2*j-1)*pm1; 636 dd = pdd2 + (2*j-1)*pd1; 637 pm2 = pm1; 638 pm1 = p; 639 pd2 = pd1; 640 pd1 = d; 641 pdd2 = pdd1; 642 pdd1 = dd; 643 if (degrees[k] == j) { 644 if (B) B[i*ndegree+k] = p; 645 if (D) D[i*ndegree+k] = d; 646 if (D2) D2[i*ndegree+k] = dd; 647 } 648 } 649 } 650 PetscFunctionReturn(0); 651 } 652 653 /*@ 654 PetscDTGaussQuadrature - create Gauss quadrature 655 656 Not Collective 657 658 Input Arguments: 659 + npoints - number of points 660 . a - left end of interval (often-1) 661 - b - right end of interval (often +1) 662 663 Output Arguments: 664 + x - quadrature points 665 - w - quadrature weights 666 667 Level: intermediate 668 669 References: 670 . 1. - Golub and Welsch, Calculation of Quadrature Rules, Math. Comp. 23(106), 1969. 671 672 .seealso: PetscDTLegendreEval() 673 @*/ 674 PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal *x,PetscReal *w) 675 { 676 PetscErrorCode ierr; 677 PetscInt i; 678 PetscReal *work; 679 PetscScalar *Z; 680 PetscBLASInt N,LDZ,info; 681 682 PetscFunctionBegin; 683 ierr = PetscCitationsRegister(GaussCitation, &GaussCite);CHKERRQ(ierr); 684 /* Set up the Golub-Welsch system */ 685 for (i=0; i<npoints; i++) { 686 x[i] = 0; /* diagonal is 0 */ 687 if (i) w[i-1] = 0.5 / PetscSqrtReal(1 - 1./PetscSqr(2*i)); 688 } 689 ierr = PetscMalloc2(npoints*npoints,&Z,PetscMax(1,2*npoints-2),&work);CHKERRQ(ierr); 690 ierr = PetscBLASIntCast(npoints,&N);CHKERRQ(ierr); 691 LDZ = N; 692 ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr); 693 PetscStackCallBLAS("LAPACKsteqr",LAPACKsteqr_("I",&N,x,w,Z,&LDZ,work,&info)); 694 ierr = PetscFPTrapPop();CHKERRQ(ierr); 695 if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEQR error"); 696 697 for (i=0; i<(npoints+1)/2; i++) { 698 PetscReal y = 0.5 * (-x[i] + x[npoints-i-1]); /* enforces symmetry */ 699 x[i] = (a+b)/2 - y*(b-a)/2; 700 if (x[i] == -0.0) x[i] = 0.0; 701 x[npoints-i-1] = (a+b)/2 + y*(b-a)/2; 702 703 w[i] = w[npoints-1-i] = 0.5*(b-a)*(PetscSqr(PetscAbsScalar(Z[i*npoints])) + PetscSqr(PetscAbsScalar(Z[(npoints-i-1)*npoints]))); 704 } 705 ierr = PetscFree2(Z,work);CHKERRQ(ierr); 706 PetscFunctionReturn(0); 707 } 708 709 static void qAndLEvaluation(PetscInt n, PetscReal x, PetscReal *q, PetscReal *qp, PetscReal *Ln) 710 /* 711 Compute the polynomial q(x) = L_{N+1}(x) - L_{n-1}(x) and its derivative in 712 addition to L_N(x) as these are needed for computing the GLL points via Newton's method. 713 Reference: "Implementing Spectral Methods for Partial Differential Equations: Algorithms 714 for Scientists and Engineers" by David A. Kopriva. 715 */ 716 { 717 PetscInt k; 718 719 PetscReal Lnp; 720 PetscReal Lnp1, Lnp1p; 721 PetscReal Lnm1, Lnm1p; 722 PetscReal Lnm2, Lnm2p; 723 724 Lnm1 = 1.0; 725 *Ln = x; 726 Lnm1p = 0.0; 727 Lnp = 1.0; 728 729 for (k=2; k<=n; ++k) { 730 Lnm2 = Lnm1; 731 Lnm1 = *Ln; 732 Lnm2p = Lnm1p; 733 Lnm1p = Lnp; 734 *Ln = (2.*((PetscReal)k)-1.)/(1.0*((PetscReal)k))*x*Lnm1 - (((PetscReal)k)-1.)/((PetscReal)k)*Lnm2; 735 Lnp = Lnm2p + (2.0*((PetscReal)k)-1.)*Lnm1; 736 } 737 k = n+1; 738 Lnp1 = (2.*((PetscReal)k)-1.)/(((PetscReal)k))*x*(*Ln) - (((PetscReal)k)-1.)/((PetscReal)k)*Lnm1; 739 Lnp1p = Lnm1p + (2.0*((PetscReal)k)-1.)*(*Ln); 740 *q = Lnp1 - Lnm1; 741 *qp = Lnp1p - Lnm1p; 742 } 743 744 /*@C 745 PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre 746 nodes of a given size on the domain [-1,1] 747 748 Not Collective 749 750 Input Parameter: 751 + n - number of grid nodes 752 - type - PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA or PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON 753 754 Output Arguments: 755 + x - quadrature points 756 - w - quadrature weights 757 758 Notes: 759 For n > 30 the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not 760 close enough to the desired solution 761 762 These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes 763 764 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 765 766 Level: intermediate 767 768 .seealso: PetscDTGaussQuadrature() 769 770 @*/ 771 PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints,PetscGaussLobattoLegendreCreateType type,PetscReal *x,PetscReal *w) 772 { 773 PetscErrorCode ierr; 774 775 PetscFunctionBegin; 776 if (npoints < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Must provide at least 2 grid points per element"); 777 778 if (type == PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA) { 779 PetscReal *M,si; 780 PetscBLASInt bn,lierr; 781 PetscReal x0,z0,z1,z2; 782 PetscInt i,p = npoints - 1,nn; 783 784 x[0] =-1.0; 785 x[npoints-1] = 1.0; 786 if (npoints-2 > 0){ 787 ierr = PetscMalloc1(npoints-1,&M);CHKERRQ(ierr); 788 for (i=0; i<npoints-2; i++) { 789 si = ((PetscReal)i)+1.0; 790 M[i]=0.5*PetscSqrtReal(si*(si+2.0)/((si+0.5)*(si+1.5))); 791 } 792 ierr = PetscBLASIntCast(npoints-2,&bn);CHKERRQ(ierr); 793 ierr = PetscArrayzero(&x[1],bn);CHKERRQ(ierr); 794 ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr); 795 x0=0; 796 PetscStackCallBLAS("LAPACKsteqr",LAPACKREALsteqr_("N",&bn,&x[1],M,&x0,&bn,M,&lierr)); 797 if (lierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error in STERF Lapack routine %d",(int)lierr); 798 ierr = PetscFPTrapPop();CHKERRQ(ierr); 799 ierr = PetscFree(M);CHKERRQ(ierr); 800 } 801 if ((npoints-1)%2==0) { 802 x[(npoints-1)/2] = 0.0; /* hard wire to exactly 0.0 since linear algebra produces nonzero */ 803 } 804 805 w[0] = w[p] = 2.0/(((PetscReal)(p))*(((PetscReal)p)+1.0)); 806 z2 = -1.; /* Dummy value to avoid -Wmaybe-initialized */ 807 for (i=1; i<p; i++) { 808 x0 = x[i]; 809 z0 = 1.0; 810 z1 = x0; 811 for (nn=1; nn<p; nn++) { 812 z2 = x0*z1*(2.0*((PetscReal)nn)+1.0)/(((PetscReal)nn)+1.0)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.0)); 813 z0 = z1; 814 z1 = z2; 815 } 816 w[i]=2.0/(((PetscReal)p)*(((PetscReal)p)+1.0)*z2*z2); 817 } 818 } else { 819 PetscInt j,m; 820 PetscReal z1,z,q,qp,Ln; 821 PetscReal *pt; 822 ierr = PetscMalloc1(npoints,&pt);CHKERRQ(ierr); 823 824 if (npoints > 30) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON produces incorrect answers for n > 30"); 825 x[0] = -1.0; 826 x[npoints-1] = 1.0; 827 w[0] = w[npoints-1] = 2./(((PetscReal)npoints)*(((PetscReal)npoints)-1.0)); 828 m = (npoints-1)/2; /* The roots are symmetric, so we only find half of them. */ 829 for (j=1; j<=m; j++) { /* Loop over the desired roots. */ 830 z = -1.0*PetscCosReal((PETSC_PI*((PetscReal)j)+0.25)/(((PetscReal)npoints)-1.0))-(3.0/(8.0*(((PetscReal)npoints)-1.0)*PETSC_PI))*(1.0/(((PetscReal)j)+0.25)); 831 /* Starting with the above approximation to the ith root, we enter */ 832 /* the main loop of refinement by Newton's method. */ 833 do { 834 qAndLEvaluation(npoints-1,z,&q,&qp,&Ln); 835 z1 = z; 836 z = z1-q/qp; /* Newton's method. */ 837 } while (PetscAbs(z-z1) > 10.*PETSC_MACHINE_EPSILON); 838 qAndLEvaluation(npoints-1,z,&q,&qp,&Ln); 839 840 x[j] = z; 841 x[npoints-1-j] = -z; /* and put in its symmetric counterpart. */ 842 w[j] = 2.0/(((PetscReal)npoints)*(((PetscReal)npoints)-1.)*Ln*Ln); /* Compute the weight */ 843 w[npoints-1-j] = w[j]; /* and its symmetric counterpart. */ 844 pt[j]=qp; 845 } 846 847 if ((npoints-1)%2==0) { 848 qAndLEvaluation(npoints-1,0.0,&q,&qp,&Ln); 849 x[(npoints-1)/2] = 0.0; 850 w[(npoints-1)/2] = 2.0/(((PetscReal)npoints)*(((PetscReal)npoints)-1.)*Ln*Ln); 851 } 852 ierr = PetscFree(pt);CHKERRQ(ierr); 853 } 854 PetscFunctionReturn(0); 855 } 856 857 /*@ 858 PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature 859 860 Not Collective 861 862 Input Arguments: 863 + dim - The spatial dimension 864 . Nc - The number of components 865 . npoints - number of points in one dimension 866 . a - left end of interval (often-1) 867 - b - right end of interval (often +1) 868 869 Output Argument: 870 . q - A PetscQuadrature object 871 872 Level: intermediate 873 874 .seealso: PetscDTGaussQuadrature(), PetscDTLegendreEval() 875 @*/ 876 PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q) 877 { 878 PetscInt totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints, i, j, k, c; 879 PetscReal *x, *w, *xw, *ww; 880 PetscErrorCode ierr; 881 882 PetscFunctionBegin; 883 ierr = PetscMalloc1(totpoints*dim,&x);CHKERRQ(ierr); 884 ierr = PetscMalloc1(totpoints*Nc,&w);CHKERRQ(ierr); 885 /* Set up the Golub-Welsch system */ 886 switch (dim) { 887 case 0: 888 ierr = PetscFree(x);CHKERRQ(ierr); 889 ierr = PetscFree(w);CHKERRQ(ierr); 890 ierr = PetscMalloc1(1, &x);CHKERRQ(ierr); 891 ierr = PetscMalloc1(Nc, &w);CHKERRQ(ierr); 892 x[0] = 0.0; 893 for (c = 0; c < Nc; ++c) w[c] = 1.0; 894 break; 895 case 1: 896 ierr = PetscMalloc1(npoints,&ww);CHKERRQ(ierr); 897 ierr = PetscDTGaussQuadrature(npoints, a, b, x, ww);CHKERRQ(ierr); 898 for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = ww[i]; 899 ierr = PetscFree(ww);CHKERRQ(ierr); 900 break; 901 case 2: 902 ierr = PetscMalloc2(npoints,&xw,npoints,&ww);CHKERRQ(ierr); 903 ierr = PetscDTGaussQuadrature(npoints, a, b, xw, ww);CHKERRQ(ierr); 904 for (i = 0; i < npoints; ++i) { 905 for (j = 0; j < npoints; ++j) { 906 x[(i*npoints+j)*dim+0] = xw[i]; 907 x[(i*npoints+j)*dim+1] = xw[j]; 908 for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = ww[i] * ww[j]; 909 } 910 } 911 ierr = PetscFree2(xw,ww);CHKERRQ(ierr); 912 break; 913 case 3: 914 ierr = PetscMalloc2(npoints,&xw,npoints,&ww);CHKERRQ(ierr); 915 ierr = PetscDTGaussQuadrature(npoints, a, b, xw, ww);CHKERRQ(ierr); 916 for (i = 0; i < npoints; ++i) { 917 for (j = 0; j < npoints; ++j) { 918 for (k = 0; k < npoints; ++k) { 919 x[((i*npoints+j)*npoints+k)*dim+0] = xw[i]; 920 x[((i*npoints+j)*npoints+k)*dim+1] = xw[j]; 921 x[((i*npoints+j)*npoints+k)*dim+2] = xw[k]; 922 for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = ww[i] * ww[j] * ww[k]; 923 } 924 } 925 } 926 ierr = PetscFree2(xw,ww);CHKERRQ(ierr); 927 break; 928 default: 929 SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim); 930 } 931 ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr); 932 ierr = PetscQuadratureSetOrder(*q, 2*npoints-1);CHKERRQ(ierr); 933 ierr = PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);CHKERRQ(ierr); 934 ierr = PetscObjectChangeTypeName((PetscObject)*q,"GaussTensor");CHKERRQ(ierr); 935 PetscFunctionReturn(0); 936 } 937 938 /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x. 939 Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */ 940 PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P) 941 { 942 PetscReal apb, pn1, pn2; 943 PetscInt k; 944 945 PetscFunctionBegin; 946 if (!n) {*P = 1.0; PetscFunctionReturn(0);} 947 if (n == 1) {*P = 0.5 * (a - b + (a + b + 2.0) * x); PetscFunctionReturn(0);} 948 apb = a + b; 949 pn2 = 1.0; 950 pn1 = 0.5 * (a - b + (apb + 2.0) * x); 951 *P = 0.0; 952 for (k = 2; k < n+1; ++k) { 953 PetscReal a1 = 2.0 * k * (k + apb) * (2.0*k + apb - 2.0); 954 PetscReal a2 = (2.0 * k + apb - 1.0) * (a*a - b*b); 955 PetscReal a3 = (2.0 * k + apb - 2.0) * (2.0 * k + apb - 1.0) * (2.0 * k + apb); 956 PetscReal a4 = 2.0 * (k + a - 1.0) * (k + b - 1.0) * (2.0 * k + apb); 957 958 a2 = a2 / a1; 959 a3 = a3 / a1; 960 a4 = a4 / a1; 961 *P = (a2 + a3 * x) * pn1 - a4 * pn2; 962 pn2 = pn1; 963 pn1 = *P; 964 } 965 PetscFunctionReturn(0); 966 } 967 968 /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */ 969 PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P) 970 { 971 PetscReal nP; 972 PetscErrorCode ierr; 973 974 PetscFunctionBegin; 975 if (!n) {*P = 0.0; PetscFunctionReturn(0);} 976 ierr = PetscDTComputeJacobi(a+1, b+1, n-1, x, &nP);CHKERRQ(ierr); 977 *P = 0.5 * (a + b + n + 1) * nP; 978 PetscFunctionReturn(0); 979 } 980 981 /* Maps from [-1,1]^2 to the (-1,1) reference triangle */ 982 PETSC_STATIC_INLINE PetscErrorCode PetscDTMapSquareToTriangle_Internal(PetscReal x, PetscReal y, PetscReal *xi, PetscReal *eta) 983 { 984 PetscFunctionBegin; 985 *xi = 0.5 * (1.0 + x) * (1.0 - y) - 1.0; 986 *eta = y; 987 PetscFunctionReturn(0); 988 } 989 990 /* Maps from [-1,1]^2 to the (-1,1) reference triangle */ 991 PETSC_STATIC_INLINE PetscErrorCode PetscDTMapCubeToTetrahedron_Internal(PetscReal x, PetscReal y, PetscReal z, PetscReal *xi, PetscReal *eta, PetscReal *zeta) 992 { 993 PetscFunctionBegin; 994 *xi = 0.25 * (1.0 + x) * (1.0 - y) * (1.0 - z) - 1.0; 995 *eta = 0.5 * (1.0 + y) * (1.0 - z) - 1.0; 996 *zeta = z; 997 PetscFunctionReturn(0); 998 } 999 1000 static PetscErrorCode PetscDTGaussJacobiQuadrature1D_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w) 1001 { 1002 PetscInt maxIter = 100; 1003 PetscReal eps = 1.0e-8; 1004 PetscReal a1, a2, a3, a4, a5, a6; 1005 PetscInt k; 1006 PetscErrorCode ierr; 1007 1008 PetscFunctionBegin; 1009 1010 a1 = PetscPowReal(2.0, a+b+1); 1011 #if defined(PETSC_HAVE_TGAMMA) 1012 a2 = PetscTGamma(a + npoints + 1); 1013 a3 = PetscTGamma(b + npoints + 1); 1014 a4 = PetscTGamma(a + b + npoints + 1); 1015 #else 1016 { 1017 PetscInt ia, ib; 1018 1019 ia = (PetscInt) a; 1020 ib = (PetscInt) b; 1021 if (ia == a && ib == b && ia + npoints + 1 > 0 && ib + npoints + 1 > 0 && ia + ib + npoints + 1 > 0) { /* All gamma(x) terms are (x-1)! terms */ 1022 ierr = PetscDTFactorial(ia + npoints, &a2);CHKERRQ(ierr); 1023 ierr = PetscDTFactorial(ib + npoints, &a3);CHKERRQ(ierr); 1024 ierr = PetscDTFactorial(ia + ib + npoints, &a4);CHKERRQ(ierr); 1025 } else { 1026 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"tgamma() - math routine is unavailable."); 1027 } 1028 } 1029 #endif 1030 1031 ierr = PetscDTFactorial(npoints, &a5);CHKERRQ(ierr); 1032 a6 = a1 * a2 * a3 / a4 / a5; 1033 /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses. 1034 Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */ 1035 for (k = 0; k < npoints; ++k) { 1036 PetscReal r = -PetscCosReal((2.0*k + 1.0) * PETSC_PI / (2.0 * npoints)), dP; 1037 PetscInt j; 1038 1039 if (k > 0) r = 0.5 * (r + x[k-1]); 1040 for (j = 0; j < maxIter; ++j) { 1041 PetscReal s = 0.0, delta, f, fp; 1042 PetscInt i; 1043 1044 for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]); 1045 ierr = PetscDTComputeJacobi(a, b, npoints, r, &f);CHKERRQ(ierr); 1046 ierr = PetscDTComputeJacobiDerivative(a, b, npoints, r, &fp);CHKERRQ(ierr); 1047 delta = f / (fp - f * s); 1048 r = r - delta; 1049 if (PetscAbsReal(delta) < eps) break; 1050 } 1051 x[k] = r; 1052 ierr = PetscDTComputeJacobiDerivative(a, b, npoints, x[k], &dP);CHKERRQ(ierr); 1053 w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP); 1054 } 1055 PetscFunctionReturn(0); 1056 } 1057 1058 /*@ 1059 PetscDTGaussJacobiQuadrature - create Gauss-Jacobi quadrature for a simplex 1060 1061 Not Collective 1062 1063 Input Arguments: 1064 + dim - The simplex dimension 1065 . Nc - The number of components 1066 . npoints - The number of points in one dimension 1067 . a - left end of interval (often-1) 1068 - b - right end of interval (often +1) 1069 1070 Output Argument: 1071 . q - A PetscQuadrature object 1072 1073 Level: intermediate 1074 1075 References: 1076 . 1. - Karniadakis and Sherwin. FIAT 1077 1078 .seealso: PetscDTGaussTensorQuadrature(), PetscDTGaussQuadrature() 1079 @*/ 1080 PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q) 1081 { 1082 PetscInt totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints; 1083 PetscReal *px, *wx, *py, *wy, *pz, *wz, *x, *w; 1084 PetscInt i, j, k, c; 1085 PetscErrorCode ierr; 1086 1087 PetscFunctionBegin; 1088 if ((a != -1.0) || (b != 1.0)) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now"); 1089 ierr = PetscMalloc1(totpoints*dim, &x);CHKERRQ(ierr); 1090 ierr = PetscMalloc1(totpoints*Nc, &w);CHKERRQ(ierr); 1091 switch (dim) { 1092 case 0: 1093 ierr = PetscFree(x);CHKERRQ(ierr); 1094 ierr = PetscFree(w);CHKERRQ(ierr); 1095 ierr = PetscMalloc1(1, &x);CHKERRQ(ierr); 1096 ierr = PetscMalloc1(Nc, &w);CHKERRQ(ierr); 1097 x[0] = 0.0; 1098 for (c = 0; c < Nc; ++c) w[c] = 1.0; 1099 break; 1100 case 1: 1101 ierr = PetscMalloc1(npoints,&wx);CHKERRQ(ierr); 1102 ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 0.0, 0.0, x, wx);CHKERRQ(ierr); 1103 for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = wx[i]; 1104 ierr = PetscFree(wx);CHKERRQ(ierr); 1105 break; 1106 case 2: 1107 ierr = PetscMalloc4(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy);CHKERRQ(ierr); 1108 ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 0.0, 0.0, px, wx);CHKERRQ(ierr); 1109 ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 1.0, 0.0, py, wy);CHKERRQ(ierr); 1110 for (i = 0; i < npoints; ++i) { 1111 for (j = 0; j < npoints; ++j) { 1112 ierr = PetscDTMapSquareToTriangle_Internal(px[i], py[j], &x[(i*npoints+j)*2+0], &x[(i*npoints+j)*2+1]);CHKERRQ(ierr); 1113 for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = 0.5 * wx[i] * wy[j]; 1114 } 1115 } 1116 ierr = PetscFree4(px,wx,py,wy);CHKERRQ(ierr); 1117 break; 1118 case 3: 1119 ierr = PetscMalloc6(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy,npoints,&pz,npoints,&wz);CHKERRQ(ierr); 1120 ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 0.0, 0.0, px, wx);CHKERRQ(ierr); 1121 ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 1.0, 0.0, py, wy);CHKERRQ(ierr); 1122 ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 2.0, 0.0, pz, wz);CHKERRQ(ierr); 1123 for (i = 0; i < npoints; ++i) { 1124 for (j = 0; j < npoints; ++j) { 1125 for (k = 0; k < npoints; ++k) { 1126 ierr = PetscDTMapCubeToTetrahedron_Internal(px[i], py[j], pz[k], &x[((i*npoints+j)*npoints+k)*3+0], &x[((i*npoints+j)*npoints+k)*3+1], &x[((i*npoints+j)*npoints+k)*3+2]);CHKERRQ(ierr); 1127 for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = 0.125 * wx[i] * wy[j] * wz[k]; 1128 } 1129 } 1130 } 1131 ierr = PetscFree6(px,wx,py,wy,pz,wz);CHKERRQ(ierr); 1132 break; 1133 default: 1134 SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim); 1135 } 1136 ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr); 1137 ierr = PetscQuadratureSetOrder(*q, 2*npoints-1);CHKERRQ(ierr); 1138 ierr = PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);CHKERRQ(ierr); 1139 ierr = PetscObjectChangeTypeName((PetscObject)*q,"GaussJacobi");CHKERRQ(ierr); 1140 PetscFunctionReturn(0); 1141 } 1142 1143 /*@ 1144 PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell 1145 1146 Not Collective 1147 1148 Input Arguments: 1149 + dim - The cell dimension 1150 . level - The number of points in one dimension, 2^l 1151 . a - left end of interval (often-1) 1152 - b - right end of interval (often +1) 1153 1154 Output Argument: 1155 . q - A PetscQuadrature object 1156 1157 Level: intermediate 1158 1159 .seealso: PetscDTGaussTensorQuadrature() 1160 @*/ 1161 PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q) 1162 { 1163 const PetscInt p = 16; /* Digits of precision in the evaluation */ 1164 const PetscReal alpha = (b-a)/2.; /* Half-width of the integration interval */ 1165 const PetscReal beta = (b+a)/2.; /* Center of the integration interval */ 1166 const PetscReal h = PetscPowReal(2.0, -level); /* Step size, length between x_k */ 1167 PetscReal xk; /* Quadrature point x_k on reference domain [-1, 1] */ 1168 PetscReal wk = 0.5*PETSC_PI; /* Quadrature weight at x_k */ 1169 PetscReal *x, *w; 1170 PetscInt K, k, npoints; 1171 PetscErrorCode ierr; 1172 1173 PetscFunctionBegin; 1174 if (dim > 1) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %d not yet implemented", dim); 1175 if (!level) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits"); 1176 /* Find K such that the weights are < 32 digits of precision */ 1177 for (K = 1; PetscAbsReal(PetscLog10Real(wk)) < 2*p; ++K) { 1178 wk = 0.5*h*PETSC_PI*PetscCoshReal(K*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(K*h))); 1179 } 1180 ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr); 1181 ierr = PetscQuadratureSetOrder(*q, 2*K+1);CHKERRQ(ierr); 1182 npoints = 2*K-1; 1183 ierr = PetscMalloc1(npoints*dim, &x);CHKERRQ(ierr); 1184 ierr = PetscMalloc1(npoints, &w);CHKERRQ(ierr); 1185 /* Center term */ 1186 x[0] = beta; 1187 w[0] = 0.5*alpha*PETSC_PI; 1188 for (k = 1; k < K; ++k) { 1189 wk = 0.5*alpha*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h))); 1190 xk = PetscTanhReal(0.5*PETSC_PI*PetscSinhReal(k*h)); 1191 x[2*k-1] = -alpha*xk+beta; 1192 w[2*k-1] = wk; 1193 x[2*k+0] = alpha*xk+beta; 1194 w[2*k+0] = wk; 1195 } 1196 ierr = PetscQuadratureSetData(*q, dim, 1, npoints, x, w);CHKERRQ(ierr); 1197 PetscFunctionReturn(0); 1198 } 1199 1200 PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol) 1201 { 1202 const PetscInt p = 16; /* Digits of precision in the evaluation */ 1203 const PetscReal alpha = (b-a)/2.; /* Half-width of the integration interval */ 1204 const PetscReal beta = (b+a)/2.; /* Center of the integration interval */ 1205 PetscReal h = 1.0; /* Step size, length between x_k */ 1206 PetscInt l = 0; /* Level of refinement, h = 2^{-l} */ 1207 PetscReal osum = 0.0; /* Integral on last level */ 1208 PetscReal psum = 0.0; /* Integral on the level before the last level */ 1209 PetscReal sum; /* Integral on current level */ 1210 PetscReal yk; /* Quadrature point 1 - x_k on reference domain [-1, 1] */ 1211 PetscReal lx, rx; /* Quadrature points to the left and right of 0 on the real domain [a, b] */ 1212 PetscReal wk; /* Quadrature weight at x_k */ 1213 PetscReal lval, rval; /* Terms in the quadature sum to the left and right of 0 */ 1214 PetscInt d; /* Digits of precision in the integral */ 1215 1216 PetscFunctionBegin; 1217 if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits"); 1218 /* Center term */ 1219 func(beta, &lval); 1220 sum = 0.5*alpha*PETSC_PI*lval; 1221 /* */ 1222 do { 1223 PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4; 1224 PetscInt k = 1; 1225 1226 ++l; 1227 /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */ 1228 /* At each level of refinement, h --> h/2 and sum --> sum/2 */ 1229 psum = osum; 1230 osum = sum; 1231 h *= 0.5; 1232 sum *= 0.5; 1233 do { 1234 wk = 0.5*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h))); 1235 yk = 1.0/(PetscExpReal(0.5*PETSC_PI*PetscSinhReal(k*h)) * PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h))); 1236 lx = -alpha*(1.0 - yk)+beta; 1237 rx = alpha*(1.0 - yk)+beta; 1238 func(lx, &lval); 1239 func(rx, &rval); 1240 lterm = alpha*wk*lval; 1241 maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm); 1242 sum += lterm; 1243 rterm = alpha*wk*rval; 1244 maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm); 1245 sum += rterm; 1246 ++k; 1247 /* Only need to evaluate every other point on refined levels */ 1248 if (l != 1) ++k; 1249 } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */ 1250 1251 d1 = PetscLog10Real(PetscAbsReal(sum - osum)); 1252 d2 = PetscLog10Real(PetscAbsReal(sum - psum)); 1253 d3 = PetscLog10Real(maxTerm) - p; 1254 if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0; 1255 else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm))); 1256 d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4))); 1257 } while (d < digits && l < 12); 1258 *sol = sum; 1259 1260 PetscFunctionReturn(0); 1261 } 1262 1263 #if defined(PETSC_HAVE_MPFR) 1264 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol) 1265 { 1266 const PetscInt safetyFactor = 2; /* Calculate abcissa until 2*p digits */ 1267 PetscInt l = 0; /* Level of refinement, h = 2^{-l} */ 1268 mpfr_t alpha; /* Half-width of the integration interval */ 1269 mpfr_t beta; /* Center of the integration interval */ 1270 mpfr_t h; /* Step size, length between x_k */ 1271 mpfr_t osum; /* Integral on last level */ 1272 mpfr_t psum; /* Integral on the level before the last level */ 1273 mpfr_t sum; /* Integral on current level */ 1274 mpfr_t yk; /* Quadrature point 1 - x_k on reference domain [-1, 1] */ 1275 mpfr_t lx, rx; /* Quadrature points to the left and right of 0 on the real domain [a, b] */ 1276 mpfr_t wk; /* Quadrature weight at x_k */ 1277 PetscReal lval, rval; /* Terms in the quadature sum to the left and right of 0 */ 1278 PetscInt d; /* Digits of precision in the integral */ 1279 mpfr_t pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp; 1280 1281 PetscFunctionBegin; 1282 if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits"); 1283 /* Create high precision storage */ 1284 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); 1285 /* Initialization */ 1286 mpfr_set_d(alpha, 0.5*(b-a), MPFR_RNDN); 1287 mpfr_set_d(beta, 0.5*(b+a), MPFR_RNDN); 1288 mpfr_set_d(osum, 0.0, MPFR_RNDN); 1289 mpfr_set_d(psum, 0.0, MPFR_RNDN); 1290 mpfr_set_d(h, 1.0, MPFR_RNDN); 1291 mpfr_const_pi(pi2, MPFR_RNDN); 1292 mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN); 1293 /* Center term */ 1294 func(0.5*(b+a), &lval); 1295 mpfr_set(sum, pi2, MPFR_RNDN); 1296 mpfr_mul(sum, sum, alpha, MPFR_RNDN); 1297 mpfr_mul_d(sum, sum, lval, MPFR_RNDN); 1298 /* */ 1299 do { 1300 PetscReal d1, d2, d3, d4; 1301 PetscInt k = 1; 1302 1303 ++l; 1304 mpfr_set_d(maxTerm, 0.0, MPFR_RNDN); 1305 /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */ 1306 /* At each level of refinement, h --> h/2 and sum --> sum/2 */ 1307 mpfr_set(psum, osum, MPFR_RNDN); 1308 mpfr_set(osum, sum, MPFR_RNDN); 1309 mpfr_mul_d(h, h, 0.5, MPFR_RNDN); 1310 mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN); 1311 do { 1312 mpfr_set_si(kh, k, MPFR_RNDN); 1313 mpfr_mul(kh, kh, h, MPFR_RNDN); 1314 /* Weight */ 1315 mpfr_set(wk, h, MPFR_RNDN); 1316 mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN); 1317 mpfr_mul(msinh, msinh, pi2, MPFR_RNDN); 1318 mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN); 1319 mpfr_cosh(tmp, msinh, MPFR_RNDN); 1320 mpfr_sqr(tmp, tmp, MPFR_RNDN); 1321 mpfr_mul(wk, wk, mcosh, MPFR_RNDN); 1322 mpfr_div(wk, wk, tmp, MPFR_RNDN); 1323 /* Abscissa */ 1324 mpfr_set_d(yk, 1.0, MPFR_RNDZ); 1325 mpfr_cosh(tmp, msinh, MPFR_RNDN); 1326 mpfr_div(yk, yk, tmp, MPFR_RNDZ); 1327 mpfr_exp(tmp, msinh, MPFR_RNDN); 1328 mpfr_div(yk, yk, tmp, MPFR_RNDZ); 1329 /* Quadrature points */ 1330 mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ); 1331 mpfr_mul(lx, lx, alpha, MPFR_RNDU); 1332 mpfr_add(lx, lx, beta, MPFR_RNDU); 1333 mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ); 1334 mpfr_mul(rx, rx, alpha, MPFR_RNDD); 1335 mpfr_add(rx, rx, beta, MPFR_RNDD); 1336 /* Evaluation */ 1337 func(mpfr_get_d(lx, MPFR_RNDU), &lval); 1338 func(mpfr_get_d(rx, MPFR_RNDD), &rval); 1339 /* Update */ 1340 mpfr_mul(tmp, wk, alpha, MPFR_RNDN); 1341 mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN); 1342 mpfr_add(sum, sum, tmp, MPFR_RNDN); 1343 mpfr_abs(tmp, tmp, MPFR_RNDN); 1344 mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN); 1345 mpfr_set(curTerm, tmp, MPFR_RNDN); 1346 mpfr_mul(tmp, wk, alpha, MPFR_RNDN); 1347 mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN); 1348 mpfr_add(sum, sum, tmp, MPFR_RNDN); 1349 mpfr_abs(tmp, tmp, MPFR_RNDN); 1350 mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN); 1351 mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN); 1352 ++k; 1353 /* Only need to evaluate every other point on refined levels */ 1354 if (l != 1) ++k; 1355 mpfr_log10(tmp, wk, MPFR_RNDN); 1356 mpfr_abs(tmp, tmp, MPFR_RNDN); 1357 } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor*digits); /* Only need to evaluate sum until weights are < 32 digits of precision */ 1358 mpfr_sub(tmp, sum, osum, MPFR_RNDN); 1359 mpfr_abs(tmp, tmp, MPFR_RNDN); 1360 mpfr_log10(tmp, tmp, MPFR_RNDN); 1361 d1 = mpfr_get_d(tmp, MPFR_RNDN); 1362 mpfr_sub(tmp, sum, psum, MPFR_RNDN); 1363 mpfr_abs(tmp, tmp, MPFR_RNDN); 1364 mpfr_log10(tmp, tmp, MPFR_RNDN); 1365 d2 = mpfr_get_d(tmp, MPFR_RNDN); 1366 mpfr_log10(tmp, maxTerm, MPFR_RNDN); 1367 d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits; 1368 mpfr_log10(tmp, curTerm, MPFR_RNDN); 1369 d4 = mpfr_get_d(tmp, MPFR_RNDN); 1370 d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4))); 1371 } while (d < digits && l < 8); 1372 *sol = mpfr_get_d(sum, MPFR_RNDN); 1373 /* Cleanup */ 1374 mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL); 1375 PetscFunctionReturn(0); 1376 } 1377 #else 1378 1379 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol) 1380 { 1381 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp"); 1382 } 1383 #endif 1384 1385 /* Overwrites A. Can only handle full-rank problems with m>=n 1386 * A in column-major format 1387 * Ainv in row-major format 1388 * tau has length m 1389 * worksize must be >= max(1,n) 1390 */ 1391 static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m,PetscInt mstride,PetscInt n,PetscReal *A_in,PetscReal *Ainv_out,PetscScalar *tau,PetscInt worksize,PetscScalar *work) 1392 { 1393 PetscErrorCode ierr; 1394 PetscBLASInt M,N,K,lda,ldb,ldwork,info; 1395 PetscScalar *A,*Ainv,*R,*Q,Alpha; 1396 1397 PetscFunctionBegin; 1398 #if defined(PETSC_USE_COMPLEX) 1399 { 1400 PetscInt i,j; 1401 ierr = PetscMalloc2(m*n,&A,m*n,&Ainv);CHKERRQ(ierr); 1402 for (j=0; j<n; j++) { 1403 for (i=0; i<m; i++) A[i+m*j] = A_in[i+mstride*j]; 1404 } 1405 mstride = m; 1406 } 1407 #else 1408 A = A_in; 1409 Ainv = Ainv_out; 1410 #endif 1411 1412 ierr = PetscBLASIntCast(m,&M);CHKERRQ(ierr); 1413 ierr = PetscBLASIntCast(n,&N);CHKERRQ(ierr); 1414 ierr = PetscBLASIntCast(mstride,&lda);CHKERRQ(ierr); 1415 ierr = PetscBLASIntCast(worksize,&ldwork);CHKERRQ(ierr); 1416 ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr); 1417 PetscStackCallBLAS("LAPACKgeqrf",LAPACKgeqrf_(&M,&N,A,&lda,tau,work,&ldwork,&info)); 1418 ierr = PetscFPTrapPop();CHKERRQ(ierr); 1419 if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xGEQRF error"); 1420 R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */ 1421 1422 /* Extract an explicit representation of Q */ 1423 Q = Ainv; 1424 ierr = PetscArraycpy(Q,A,mstride*n);CHKERRQ(ierr); 1425 K = N; /* full rank */ 1426 PetscStackCallBLAS("LAPACKorgqr",LAPACKorgqr_(&M,&N,&K,Q,&lda,tau,work,&ldwork,&info)); 1427 if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xORGQR/xUNGQR error"); 1428 1429 /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */ 1430 Alpha = 1.0; 1431 ldb = lda; 1432 PetscStackCallBLAS("BLAStrsm",BLAStrsm_("Right","Upper","ConjugateTranspose","NotUnitTriangular",&M,&N,&Alpha,R,&lda,Q,&ldb)); 1433 /* Ainv is Q, overwritten with inverse */ 1434 1435 #if defined(PETSC_USE_COMPLEX) 1436 { 1437 PetscInt i; 1438 for (i=0; i<m*n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]); 1439 ierr = PetscFree2(A,Ainv);CHKERRQ(ierr); 1440 } 1441 #endif 1442 PetscFunctionReturn(0); 1443 } 1444 1445 /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */ 1446 static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval,const PetscReal *x,PetscInt ndegree,const PetscInt *degrees,PetscBool Transpose,PetscReal *B) 1447 { 1448 PetscErrorCode ierr; 1449 PetscReal *Bv; 1450 PetscInt i,j; 1451 1452 PetscFunctionBegin; 1453 ierr = PetscMalloc1((ninterval+1)*ndegree,&Bv);CHKERRQ(ierr); 1454 /* Point evaluation of L_p on all the source vertices */ 1455 ierr = PetscDTLegendreEval(ninterval+1,x,ndegree,degrees,Bv,NULL,NULL);CHKERRQ(ierr); 1456 /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */ 1457 for (i=0; i<ninterval; i++) { 1458 for (j=0; j<ndegree; j++) { 1459 if (Transpose) B[i+ninterval*j] = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j]; 1460 else B[i*ndegree+j] = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j]; 1461 } 1462 } 1463 ierr = PetscFree(Bv);CHKERRQ(ierr); 1464 PetscFunctionReturn(0); 1465 } 1466 1467 /*@ 1468 PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals 1469 1470 Not Collective 1471 1472 Input Arguments: 1473 + degree - degree of reconstruction polynomial 1474 . nsource - number of source intervals 1475 . sourcex - sorted coordinates of source cell boundaries (length nsource+1) 1476 . ntarget - number of target intervals 1477 - targetx - sorted coordinates of target cell boundaries (length ntarget+1) 1478 1479 Output Arguments: 1480 . R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s] 1481 1482 Level: advanced 1483 1484 .seealso: PetscDTLegendreEval() 1485 @*/ 1486 PetscErrorCode PetscDTReconstructPoly(PetscInt degree,PetscInt nsource,const PetscReal *sourcex,PetscInt ntarget,const PetscReal *targetx,PetscReal *R) 1487 { 1488 PetscErrorCode ierr; 1489 PetscInt i,j,k,*bdegrees,worksize; 1490 PetscReal xmin,xmax,center,hscale,*sourcey,*targety,*Bsource,*Bsinv,*Btarget; 1491 PetscScalar *tau,*work; 1492 1493 PetscFunctionBegin; 1494 PetscValidRealPointer(sourcex,3); 1495 PetscValidRealPointer(targetx,5); 1496 PetscValidRealPointer(R,6); 1497 if (degree >= nsource) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Reconstruction degree %D must be less than number of source intervals %D",degree,nsource); 1498 #if defined(PETSC_USE_DEBUG) 1499 for (i=0; i<nsource; i++) { 1500 if (sourcex[i] >= sourcex[i+1]) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Source interval %D has negative orientation (%g,%g)",i,(double)sourcex[i],(double)sourcex[i+1]); 1501 } 1502 for (i=0; i<ntarget; i++) { 1503 if (targetx[i] >= targetx[i+1]) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Target interval %D has negative orientation (%g,%g)",i,(double)targetx[i],(double)targetx[i+1]); 1504 } 1505 #endif 1506 xmin = PetscMin(sourcex[0],targetx[0]); 1507 xmax = PetscMax(sourcex[nsource],targetx[ntarget]); 1508 center = (xmin + xmax)/2; 1509 hscale = (xmax - xmin)/2; 1510 worksize = nsource; 1511 ierr = PetscMalloc4(degree+1,&bdegrees,nsource+1,&sourcey,nsource*(degree+1),&Bsource,worksize,&work);CHKERRQ(ierr); 1512 ierr = PetscMalloc4(nsource,&tau,nsource*(degree+1),&Bsinv,ntarget+1,&targety,ntarget*(degree+1),&Btarget);CHKERRQ(ierr); 1513 for (i=0; i<=nsource; i++) sourcey[i] = (sourcex[i]-center)/hscale; 1514 for (i=0; i<=degree; i++) bdegrees[i] = i+1; 1515 ierr = PetscDTLegendreIntegrate(nsource,sourcey,degree+1,bdegrees,PETSC_TRUE,Bsource);CHKERRQ(ierr); 1516 ierr = PetscDTPseudoInverseQR(nsource,nsource,degree+1,Bsource,Bsinv,tau,nsource,work);CHKERRQ(ierr); 1517 for (i=0; i<=ntarget; i++) targety[i] = (targetx[i]-center)/hscale; 1518 ierr = PetscDTLegendreIntegrate(ntarget,targety,degree+1,bdegrees,PETSC_FALSE,Btarget);CHKERRQ(ierr); 1519 for (i=0; i<ntarget; i++) { 1520 PetscReal rowsum = 0; 1521 for (j=0; j<nsource; j++) { 1522 PetscReal sum = 0; 1523 for (k=0; k<degree+1; k++) { 1524 sum += Btarget[i*(degree+1)+k] * Bsinv[k*nsource+j]; 1525 } 1526 R[i*nsource+j] = sum; 1527 rowsum += sum; 1528 } 1529 for (j=0; j<nsource; j++) R[i*nsource+j] /= rowsum; /* normalize each row */ 1530 } 1531 ierr = PetscFree4(bdegrees,sourcey,Bsource,work);CHKERRQ(ierr); 1532 ierr = PetscFree4(tau,Bsinv,targety,Btarget);CHKERRQ(ierr); 1533 PetscFunctionReturn(0); 1534 } 1535 1536 /*@C 1537 PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points 1538 1539 Not Collective 1540 1541 Input Parameter: 1542 + n - the number of GLL nodes 1543 . nodes - the GLL nodes 1544 . weights - the GLL weights 1545 . f - the function values at the nodes 1546 1547 Output Parameter: 1548 . in - the value of the integral 1549 1550 Level: beginner 1551 1552 .seealso: PetscDTGaussLobattoLegendreQuadrature() 1553 1554 @*/ 1555 PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n,PetscReal *nodes,PetscReal *weights,const PetscReal *f,PetscReal *in) 1556 { 1557 PetscInt i; 1558 1559 PetscFunctionBegin; 1560 *in = 0.; 1561 for (i=0; i<n; i++) { 1562 *in += f[i]*f[i]*weights[i]; 1563 } 1564 PetscFunctionReturn(0); 1565 } 1566 1567 /*@C 1568 PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element 1569 1570 Not Collective 1571 1572 Input Parameter: 1573 + n - the number of GLL nodes 1574 . nodes - the GLL nodes 1575 . weights - the GLL weights 1576 1577 Output Parameter: 1578 . A - the stiffness element 1579 1580 Level: beginner 1581 1582 Notes: 1583 Destroy this with PetscGaussLobattoLegendreElementLaplacianDestroy() 1584 1585 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) 1586 1587 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy() 1588 1589 @*/ 1590 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA) 1591 { 1592 PetscReal **A; 1593 PetscErrorCode ierr; 1594 const PetscReal *gllnodes = nodes; 1595 const PetscInt p = n-1; 1596 PetscReal z0,z1,z2 = -1,x,Lpj,Lpr; 1597 PetscInt i,j,nn,r; 1598 1599 PetscFunctionBegin; 1600 ierr = PetscMalloc1(n,&A);CHKERRQ(ierr); 1601 ierr = PetscMalloc1(n*n,&A[0]);CHKERRQ(ierr); 1602 for (i=1; i<n; i++) A[i] = A[i-1]+n; 1603 1604 for (j=1; j<p; j++) { 1605 x = gllnodes[j]; 1606 z0 = 1.; 1607 z1 = x; 1608 for (nn=1; nn<p; nn++) { 1609 z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.)); 1610 z0 = z1; 1611 z1 = z2; 1612 } 1613 Lpj=z2; 1614 for (r=1; r<p; r++) { 1615 if (r == j) { 1616 A[j][j]=2./(3.*(1.-gllnodes[j]*gllnodes[j])*Lpj*Lpj); 1617 } else { 1618 x = gllnodes[r]; 1619 z0 = 1.; 1620 z1 = x; 1621 for (nn=1; nn<p; nn++) { 1622 z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.)); 1623 z0 = z1; 1624 z1 = z2; 1625 } 1626 Lpr = z2; 1627 A[r][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*Lpr*(gllnodes[j]-gllnodes[r])*(gllnodes[j]-gllnodes[r])); 1628 } 1629 } 1630 } 1631 for (j=1; j<p+1; j++) { 1632 x = gllnodes[j]; 1633 z0 = 1.; 1634 z1 = x; 1635 for (nn=1; nn<p; nn++) { 1636 z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.)); 1637 z0 = z1; 1638 z1 = z2; 1639 } 1640 Lpj = z2; 1641 A[j][0] = 4.*PetscPowRealInt(-1.,p)/(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.+gllnodes[j])*(1.+gllnodes[j])); 1642 A[0][j] = A[j][0]; 1643 } 1644 for (j=0; j<p; j++) { 1645 x = gllnodes[j]; 1646 z0 = 1.; 1647 z1 = x; 1648 for (nn=1; nn<p; nn++) { 1649 z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.)); 1650 z0 = z1; 1651 z1 = z2; 1652 } 1653 Lpj=z2; 1654 1655 A[p][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.-gllnodes[j])*(1.-gllnodes[j])); 1656 A[j][p] = A[p][j]; 1657 } 1658 A[0][0]=0.5+(((PetscReal)p)*(((PetscReal)p)+1.)-2.)/6.; 1659 A[p][p]=A[0][0]; 1660 *AA = A; 1661 PetscFunctionReturn(0); 1662 } 1663 1664 /*@C 1665 PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element 1666 1667 Not Collective 1668 1669 Input Parameter: 1670 + n - the number of GLL nodes 1671 . nodes - the GLL nodes 1672 . weights - the GLL weightss 1673 - A - the stiffness element 1674 1675 Level: beginner 1676 1677 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate() 1678 1679 @*/ 1680 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA) 1681 { 1682 PetscErrorCode ierr; 1683 1684 PetscFunctionBegin; 1685 ierr = PetscFree((*AA)[0]);CHKERRQ(ierr); 1686 ierr = PetscFree(*AA);CHKERRQ(ierr); 1687 *AA = NULL; 1688 PetscFunctionReturn(0); 1689 } 1690 1691 /*@C 1692 PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element 1693 1694 Not Collective 1695 1696 Input Parameter: 1697 + n - the number of GLL nodes 1698 . nodes - the GLL nodes 1699 . weights - the GLL weights 1700 1701 Output Parameter: 1702 . AA - the stiffness element 1703 - AAT - the transpose of AA (pass in NULL if you do not need this array) 1704 1705 Level: beginner 1706 1707 Notes: 1708 Destroy this with PetscGaussLobattoLegendreElementGradientDestroy() 1709 1710 You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row oriented 1711 1712 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy() 1713 1714 @*/ 1715 PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT) 1716 { 1717 PetscReal **A, **AT = NULL; 1718 PetscErrorCode ierr; 1719 const PetscReal *gllnodes = nodes; 1720 const PetscInt p = n-1; 1721 PetscReal q,qp,Li, Lj,d0; 1722 PetscInt i,j; 1723 1724 PetscFunctionBegin; 1725 ierr = PetscMalloc1(n,&A);CHKERRQ(ierr); 1726 ierr = PetscMalloc1(n*n,&A[0]);CHKERRQ(ierr); 1727 for (i=1; i<n; i++) A[i] = A[i-1]+n; 1728 1729 if (AAT) { 1730 ierr = PetscMalloc1(n,&AT);CHKERRQ(ierr); 1731 ierr = PetscMalloc1(n*n,&AT[0]);CHKERRQ(ierr); 1732 for (i=1; i<n; i++) AT[i] = AT[i-1]+n; 1733 } 1734 1735 if (n==1) {A[0][0] = 0.;} 1736 d0 = (PetscReal)p*((PetscReal)p+1.)/4.; 1737 for (i=0; i<n; i++) { 1738 for (j=0; j<n; j++) { 1739 A[i][j] = 0.; 1740 qAndLEvaluation(p,gllnodes[i],&q,&qp,&Li); 1741 qAndLEvaluation(p,gllnodes[j],&q,&qp,&Lj); 1742 if (i!=j) A[i][j] = Li/(Lj*(gllnodes[i]-gllnodes[j])); 1743 if ((j==i) && (i==0)) A[i][j] = -d0; 1744 if (j==i && i==p) A[i][j] = d0; 1745 if (AT) AT[j][i] = A[i][j]; 1746 } 1747 } 1748 if (AAT) *AAT = AT; 1749 *AA = A; 1750 PetscFunctionReturn(0); 1751 } 1752 1753 /*@C 1754 PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with PetscGaussLobattoLegendreElementGradientCreate() 1755 1756 Not Collective 1757 1758 Input Parameter: 1759 + n - the number of GLL nodes 1760 . nodes - the GLL nodes 1761 . weights - the GLL weights 1762 . AA - the stiffness element 1763 - AAT - the transpose of the element 1764 1765 Level: beginner 1766 1767 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionCreate() 1768 1769 @*/ 1770 PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT) 1771 { 1772 PetscErrorCode ierr; 1773 1774 PetscFunctionBegin; 1775 ierr = PetscFree((*AA)[0]);CHKERRQ(ierr); 1776 ierr = PetscFree(*AA);CHKERRQ(ierr); 1777 *AA = NULL; 1778 if (*AAT) { 1779 ierr = PetscFree((*AAT)[0]);CHKERRQ(ierr); 1780 ierr = PetscFree(*AAT);CHKERRQ(ierr); 1781 *AAT = NULL; 1782 } 1783 PetscFunctionReturn(0); 1784 } 1785 1786 /*@C 1787 PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element 1788 1789 Not Collective 1790 1791 Input Parameter: 1792 + n - the number of GLL nodes 1793 . nodes - the GLL nodes 1794 . weights - the GLL weightss 1795 1796 Output Parameter: 1797 . AA - the stiffness element 1798 1799 Level: beginner 1800 1801 Notes: 1802 Destroy this with PetscGaussLobattoLegendreElementAdvectionDestroy() 1803 1804 This is the same as the Gradient operator multiplied by the diagonal mass matrix 1805 1806 You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row oriented 1807 1808 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionDestroy() 1809 1810 @*/ 1811 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA) 1812 { 1813 PetscReal **D; 1814 PetscErrorCode ierr; 1815 const PetscReal *gllweights = weights; 1816 const PetscInt glln = n; 1817 PetscInt i,j; 1818 1819 PetscFunctionBegin; 1820 ierr = PetscGaussLobattoLegendreElementGradientCreate(n,nodes,weights,&D,NULL);CHKERRQ(ierr); 1821 for (i=0; i<glln; i++){ 1822 for (j=0; j<glln; j++) { 1823 D[i][j] = gllweights[i]*D[i][j]; 1824 } 1825 } 1826 *AA = D; 1827 PetscFunctionReturn(0); 1828 } 1829 1830 /*@C 1831 PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element 1832 1833 Not Collective 1834 1835 Input Parameter: 1836 + n - the number of GLL nodes 1837 . nodes - the GLL nodes 1838 . weights - the GLL weights 1839 - A - advection 1840 1841 Level: beginner 1842 1843 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementAdvectionCreate() 1844 1845 @*/ 1846 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA) 1847 { 1848 PetscErrorCode ierr; 1849 1850 PetscFunctionBegin; 1851 ierr = PetscFree((*AA)[0]);CHKERRQ(ierr); 1852 ierr = PetscFree(*AA);CHKERRQ(ierr); 1853 *AA = NULL; 1854 PetscFunctionReturn(0); 1855 } 1856 1857 PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA) 1858 { 1859 PetscReal **A; 1860 PetscErrorCode ierr; 1861 const PetscReal *gllweights = weights; 1862 const PetscInt glln = n; 1863 PetscInt i,j; 1864 1865 PetscFunctionBegin; 1866 ierr = PetscMalloc1(glln,&A);CHKERRQ(ierr); 1867 ierr = PetscMalloc1(glln*glln,&A[0]);CHKERRQ(ierr); 1868 for (i=1; i<glln; i++) A[i] = A[i-1]+glln; 1869 if (glln==1) {A[0][0] = 0.;} 1870 for (i=0; i<glln; i++) { 1871 for (j=0; j<glln; j++) { 1872 A[i][j] = 0.; 1873 if (j==i) A[i][j] = gllweights[i]; 1874 } 1875 } 1876 *AA = A; 1877 PetscFunctionReturn(0); 1878 } 1879 1880 PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA) 1881 { 1882 PetscErrorCode ierr; 1883 1884 PetscFunctionBegin; 1885 ierr = PetscFree((*AA)[0]);CHKERRQ(ierr); 1886 ierr = PetscFree(*AA);CHKERRQ(ierr); 1887 *AA = NULL; 1888 PetscFunctionReturn(0); 1889 } 1890 1891