1 2 /* 3 Routines to project vectors out of null spaces. 4 */ 5 6 #include <petsc/private/matimpl.h> /*I "petscmat.h" I*/ 7 8 PetscClassId MAT_NULLSPACE_CLASSID; 9 10 /*@C 11 MatNullSpaceSetFunction - set a function that removes a null space from a vector 12 out of null spaces. 13 14 Logically Collective on sp 15 16 Input Parameters: 17 + sp - the `MatNullSpace` null space object 18 . rem - the function that removes the null space 19 - ctx - context for the remove function 20 21 Level: advanced 22 23 .seealso: `MatNullSpace`, `MatNullSpaceDestroy()`, `MatNullSpaceRemove()`, `MatSetNullSpace()`, `MatNullSpace`, `MatNullSpaceCreate()` 24 @*/ 25 PetscErrorCode MatNullSpaceSetFunction(MatNullSpace sp, PetscErrorCode (*rem)(MatNullSpace, Vec, void *), void *ctx) 26 { 27 PetscFunctionBegin; 28 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 29 sp->remove = rem; 30 sp->rmctx = ctx; 31 PetscFunctionReturn(0); 32 } 33 34 /*@C 35 MatNullSpaceGetVecs - get the vectors defining the null space 36 37 Not Collective 38 39 Input Parameter: 40 . sp - null space object 41 42 Output Parameters: 43 + has_cnst - `PETSC_TRUE` if the null space contains the constant vector, otherwise `PETSC_FALSE` 44 . n - number of vectors (excluding constant vector) in null space 45 - vecs - orthonormal vectors that span the null space (excluding the constant vector) 46 47 Level: developer 48 49 Note: 50 These vectors and the array are owned by the `MatNullSpace` and should not be destroyed or freeded by the caller 51 52 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatGetNullSpace()`, `MatGetNearNullSpace()` 53 @*/ 54 PetscErrorCode MatNullSpaceGetVecs(MatNullSpace sp, PetscBool *has_const, PetscInt *n, const Vec **vecs) 55 { 56 PetscFunctionBegin; 57 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 58 if (has_const) *has_const = sp->has_cnst; 59 if (n) *n = sp->n; 60 if (vecs) *vecs = sp->vecs; 61 PetscFunctionReturn(0); 62 } 63 64 /*@ 65 MatNullSpaceCreateRigidBody - create rigid body modes from coordinates 66 67 Collective on coords 68 69 Input Parameter: 70 . coords - block of coordinates of each node, must have block size set 71 72 Output Parameter: 73 . sp - the null space 74 75 Level: advanced 76 77 Notes: 78 If you are solving an elasticity problem you should likely use this, in conjunction with `MatSetNearNullSpace()`, to provide information that 79 the `PCGAMG` preconditioner can use to construct a much more efficient preconditioner. 80 81 If you are solving an elasticity problem with pure Neumann boundary conditions you can use this in conjunction with `MatSetNullSpace()` to 82 provide this information to the linear solver so it can handle the null space appropriately in the linear solution. 83 84 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatSetNearNullSpace()`, `MatSetNullSpace()` 85 @*/ 86 PetscErrorCode MatNullSpaceCreateRigidBody(Vec coords, MatNullSpace *sp) 87 { 88 const PetscScalar *x; 89 PetscScalar *v[6], dots[5]; 90 Vec vec[6]; 91 PetscInt n, N, dim, nmodes, i, j; 92 PetscReal sN; 93 94 PetscFunctionBegin; 95 PetscCall(VecGetBlockSize(coords, &dim)); 96 PetscCall(VecGetLocalSize(coords, &n)); 97 PetscCall(VecGetSize(coords, &N)); 98 n /= dim; 99 N /= dim; 100 sN = 1. / PetscSqrtReal((PetscReal)N); 101 switch (dim) { 102 case 1: 103 PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)coords), PETSC_TRUE, 0, NULL, sp)); 104 break; 105 case 2: 106 case 3: 107 nmodes = (dim == 2) ? 3 : 6; 108 PetscCall(VecCreate(PetscObjectComm((PetscObject)coords), &vec[0])); 109 PetscCall(VecSetSizes(vec[0], dim * n, dim * N)); 110 PetscCall(VecSetBlockSize(vec[0], dim)); 111 PetscCall(VecSetUp(vec[0])); 112 for (i = 1; i < nmodes; i++) PetscCall(VecDuplicate(vec[0], &vec[i])); 113 for (i = 0; i < nmodes; i++) PetscCall(VecGetArray(vec[i], &v[i])); 114 PetscCall(VecGetArrayRead(coords, &x)); 115 for (i = 0; i < n; i++) { 116 if (dim == 2) { 117 v[0][i * 2 + 0] = sN; 118 v[0][i * 2 + 1] = 0.; 119 v[1][i * 2 + 0] = 0.; 120 v[1][i * 2 + 1] = sN; 121 /* Rotations */ 122 v[2][i * 2 + 0] = -x[i * 2 + 1]; 123 v[2][i * 2 + 1] = x[i * 2 + 0]; 124 } else { 125 v[0][i * 3 + 0] = sN; 126 v[0][i * 3 + 1] = 0.; 127 v[0][i * 3 + 2] = 0.; 128 v[1][i * 3 + 0] = 0.; 129 v[1][i * 3 + 1] = sN; 130 v[1][i * 3 + 2] = 0.; 131 v[2][i * 3 + 0] = 0.; 132 v[2][i * 3 + 1] = 0.; 133 v[2][i * 3 + 2] = sN; 134 135 v[3][i * 3 + 0] = x[i * 3 + 1]; 136 v[3][i * 3 + 1] = -x[i * 3 + 0]; 137 v[3][i * 3 + 2] = 0.; 138 v[4][i * 3 + 0] = 0.; 139 v[4][i * 3 + 1] = -x[i * 3 + 2]; 140 v[4][i * 3 + 2] = x[i * 3 + 1]; 141 v[5][i * 3 + 0] = x[i * 3 + 2]; 142 v[5][i * 3 + 1] = 0.; 143 v[5][i * 3 + 2] = -x[i * 3 + 0]; 144 } 145 } 146 for (i = 0; i < nmodes; i++) PetscCall(VecRestoreArray(vec[i], &v[i])); 147 PetscCall(VecRestoreArrayRead(coords, &x)); 148 for (i = dim; i < nmodes; i++) { 149 /* Orthonormalize vec[i] against vec[0:i-1] */ 150 PetscCall(VecMDot(vec[i], i, vec, dots)); 151 for (j = 0; j < i; j++) dots[j] *= -1.; 152 PetscCall(VecMAXPY(vec[i], i, dots, vec)); 153 PetscCall(VecNormalize(vec[i], NULL)); 154 } 155 PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)coords), PETSC_FALSE, nmodes, vec, sp)); 156 for (i = 0; i < nmodes; i++) PetscCall(VecDestroy(&vec[i])); 157 } 158 PetscFunctionReturn(0); 159 } 160 161 /*@C 162 MatNullSpaceView - Visualizes a null space object. 163 164 Collective on sp 165 166 Input Parameters: 167 + matnull - the null space 168 - viewer - visualization context 169 170 Level: advanced 171 172 Fortran Note: 173 This routine is not supported in Fortran. 174 175 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `PetscViewerASCIIOpen()` 176 @*/ 177 PetscErrorCode MatNullSpaceView(MatNullSpace sp, PetscViewer viewer) 178 { 179 PetscBool iascii; 180 181 PetscFunctionBegin; 182 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 183 if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp), &viewer)); 184 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2); 185 PetscCheckSameComm(sp, 1, viewer, 2); 186 187 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 188 if (iascii) { 189 PetscViewerFormat format; 190 PetscInt i; 191 PetscCall(PetscViewerGetFormat(viewer, &format)); 192 PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)sp, viewer)); 193 PetscCall(PetscViewerASCIIPushTab(viewer)); 194 PetscCall(PetscViewerASCIIPrintf(viewer, "Contains %" PetscInt_FMT " vector%s%s\n", sp->n, sp->n == 1 ? "" : "s", sp->has_cnst ? " and the constant" : "")); 195 if (sp->remove) PetscCall(PetscViewerASCIIPrintf(viewer, "Has user-provided removal function\n")); 196 if (!(format == PETSC_VIEWER_ASCII_INFO || format == PETSC_VIEWER_ASCII_INFO_DETAIL)) { 197 for (i = 0; i < sp->n; i++) PetscCall(VecView(sp->vecs[i], viewer)); 198 } 199 PetscCall(PetscViewerASCIIPopTab(viewer)); 200 } 201 PetscFunctionReturn(0); 202 } 203 204 /*@C 205 MatNullSpaceCreate - Creates a `MatNullSpace` data structure used to project vectors out of null spaces. 206 207 Collective 208 209 Input Parameters: 210 + comm - the MPI communicator associated with the object 211 . has_cnst - `PETSC_TRUE` if the null space contains the constant vector; otherwise `PETSC_FALSE` 212 . n - number of vectors (excluding constant vector) in null space 213 - vecs - the vectors that span the null space (excluding the constant vector); 214 these vectors must be orthonormal. These vectors are NOT copied, so do not change them 215 after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count 216 for them by one). 217 218 Output Parameter: 219 . SP - the null space context 220 221 Level: advanced 222 223 Notes: 224 See `MatNullSpaceSetFunction()` as an alternative way of providing the null space information instead of setting vecs. 225 226 If has_cnst is `PETSC_TRUE` you do not need to pass a constant vector in as a fourth argument to this routine, nor do you 227 need to pass in a function that eliminates the constant function into `MatNullSpaceSetFunction()`. 228 229 .seealso: `MatNullSpace`, `MatNullSpaceDestroy()`, `MatNullSpaceRemove()`, `MatSetNullSpace()`, `MatNullSpace`, `MatNullSpaceSetFunction()` 230 @*/ 231 PetscErrorCode MatNullSpaceCreate(MPI_Comm comm, PetscBool has_cnst, PetscInt n, const Vec vecs[], MatNullSpace *SP) 232 { 233 MatNullSpace sp; 234 PetscInt i; 235 236 PetscFunctionBegin; 237 PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of vectors (given %" PetscInt_FMT ") cannot be negative", n); 238 if (n) PetscValidPointer(vecs, 4); 239 for (i = 0; i < n; i++) PetscValidHeaderSpecific(vecs[i], VEC_CLASSID, 4); 240 PetscValidPointer(SP, 5); 241 if (n) { 242 for (i = 0; i < n; i++) { 243 /* prevent the user from changes values in the vector */ 244 PetscCall(VecLockReadPush(vecs[i])); 245 } 246 } 247 if (PetscUnlikelyDebug(n)) { 248 PetscScalar *dots; 249 for (i = 0; i < n; i++) { 250 PetscReal norm; 251 PetscCall(VecNorm(vecs[i], NORM_2, &norm)); 252 PetscCheck(PetscAbsReal(norm - 1) <= PETSC_SQRT_MACHINE_EPSILON, PetscObjectComm((PetscObject)vecs[i]), PETSC_ERR_ARG_WRONG, "Vector %" PetscInt_FMT " must have 2-norm of 1.0, it is %g", i, (double)norm); 253 } 254 if (has_cnst) { 255 for (i = 0; i < n; i++) { 256 PetscScalar sum; 257 PetscCall(VecSum(vecs[i], &sum)); 258 PetscCheck(PetscAbsScalar(sum) <= PETSC_SQRT_MACHINE_EPSILON, PetscObjectComm((PetscObject)vecs[i]), PETSC_ERR_ARG_WRONG, "Vector %" PetscInt_FMT " must be orthogonal to constant vector, inner product is %g", i, (double)PetscAbsScalar(sum)); 259 } 260 } 261 PetscCall(PetscMalloc1(n - 1, &dots)); 262 for (i = 0; i < n - 1; i++) { 263 PetscInt j; 264 PetscCall(VecMDot(vecs[i], n - i - 1, vecs + i + 1, dots)); 265 for (j = 0; j < n - i - 1; j++) { 266 PetscCheck(PetscAbsScalar(dots[j]) <= PETSC_SQRT_MACHINE_EPSILON, PetscObjectComm((PetscObject)vecs[i]), PETSC_ERR_ARG_WRONG, "Vector %" PetscInt_FMT " must be orthogonal to vector %" PetscInt_FMT ", inner product is %g", i, i + j + 1, (double)PetscAbsScalar(dots[j])); 267 } 268 } 269 PetscCall(PetscFree(dots)); 270 } 271 272 *SP = NULL; 273 PetscCall(MatInitializePackage()); 274 275 PetscCall(PetscHeaderCreate(sp, MAT_NULLSPACE_CLASSID, "MatNullSpace", "Null space", "Mat", comm, MatNullSpaceDestroy, MatNullSpaceView)); 276 277 sp->has_cnst = has_cnst; 278 sp->n = n; 279 sp->vecs = NULL; 280 sp->alpha = NULL; 281 sp->remove = NULL; 282 sp->rmctx = NULL; 283 284 if (n) { 285 PetscCall(PetscMalloc1(n, &sp->vecs)); 286 PetscCall(PetscMalloc1(n, &sp->alpha)); 287 for (i = 0; i < n; i++) { 288 PetscCall(PetscObjectReference((PetscObject)vecs[i])); 289 sp->vecs[i] = vecs[i]; 290 } 291 } 292 293 *SP = sp; 294 PetscFunctionReturn(0); 295 } 296 297 /*@ 298 MatNullSpaceDestroy - Destroys a data structure used to project vectors out of null spaces. 299 300 Collective on sp 301 302 Input Parameter: 303 . sp - the null space context to be destroyed 304 305 Level: advanced 306 307 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatNullSpaceRemove()`, `MatNullSpaceSetFunction()` 308 @*/ 309 PetscErrorCode MatNullSpaceDestroy(MatNullSpace *sp) 310 { 311 PetscInt i; 312 313 PetscFunctionBegin; 314 if (!*sp) PetscFunctionReturn(0); 315 PetscValidHeaderSpecific((*sp), MAT_NULLSPACE_CLASSID, 1); 316 if (--((PetscObject)(*sp))->refct > 0) { 317 *sp = NULL; 318 PetscFunctionReturn(0); 319 } 320 321 for (i = 0; i < (*sp)->n; i++) PetscCall(VecLockReadPop((*sp)->vecs[i])); 322 323 PetscCall(VecDestroyVecs((*sp)->n, &(*sp)->vecs)); 324 PetscCall(PetscFree((*sp)->alpha)); 325 PetscCall(PetscHeaderDestroy(sp)); 326 PetscFunctionReturn(0); 327 } 328 329 /*@C 330 MatNullSpaceRemove - Removes all the components of a null space from a vector. 331 332 Collective on sp 333 334 Input Parameters: 335 + sp - the null space context (if this is NULL then no null space is removed) 336 - vec - the vector from which the null space is to be removed 337 338 Level: advanced 339 340 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatNullSpaceDestroy()`, `MatNullSpaceSetFunction()` 341 @*/ 342 PetscErrorCode MatNullSpaceRemove(MatNullSpace sp, Vec vec) 343 { 344 PetscScalar sum; 345 PetscInt i, N; 346 347 PetscFunctionBegin; 348 if (!sp) PetscFunctionReturn(0); 349 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 350 PetscValidHeaderSpecific(vec, VEC_CLASSID, 2); 351 352 if (sp->has_cnst) { 353 PetscCall(VecGetSize(vec, &N)); 354 if (N > 0) { 355 PetscCall(VecSum(vec, &sum)); 356 sum = sum / ((PetscScalar)(-1.0 * N)); 357 PetscCall(VecShift(vec, sum)); 358 } 359 } 360 361 if (sp->n) { 362 PetscCall(VecMDot(vec, sp->n, sp->vecs, sp->alpha)); 363 for (i = 0; i < sp->n; i++) sp->alpha[i] = -sp->alpha[i]; 364 PetscCall(VecMAXPY(vec, sp->n, sp->alpha, sp->vecs)); 365 } 366 367 if (sp->remove) PetscCall((*sp->remove)(sp, vec, sp->rmctx)); 368 PetscFunctionReturn(0); 369 } 370 371 /*@ 372 MatNullSpaceTest - Tests if the claimed null space is really a null space of a matrix 373 374 Collective on sp 375 376 Input Parameters: 377 + sp - the null space context 378 - mat - the matrix 379 380 Output Parameters: 381 . isNull - `PETSC_TRUE` if the nullspace is valid for this matrix 382 383 Level: advanced 384 385 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatNullSpaceDestroy()`, `MatNullSpaceSetFunction()` 386 @*/ 387 PetscErrorCode MatNullSpaceTest(MatNullSpace sp, Mat mat, PetscBool *isNull) 388 { 389 PetscScalar sum; 390 PetscReal nrm, tol = 10. * PETSC_SQRT_MACHINE_EPSILON; 391 PetscInt j, n, N; 392 Vec l, r; 393 PetscBool flg1 = PETSC_FALSE, flg2 = PETSC_FALSE, consistent = PETSC_TRUE; 394 PetscViewer viewer; 395 396 PetscFunctionBegin; 397 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 398 PetscValidHeaderSpecific(mat, MAT_CLASSID, 2); 399 n = sp->n; 400 PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options, ((PetscObject)mat)->prefix, "-mat_null_space_test_view", &flg1, NULL)); 401 PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options, ((PetscObject)mat)->prefix, "-mat_null_space_test_view_draw", &flg2, NULL)); 402 403 if (n) { 404 PetscCall(VecDuplicate(sp->vecs[0], &l)); 405 } else { 406 PetscCall(MatCreateVecs(mat, &l, NULL)); 407 } 408 409 PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp), &viewer)); 410 if (sp->has_cnst) { 411 PetscCall(VecDuplicate(l, &r)); 412 PetscCall(VecGetSize(l, &N)); 413 sum = 1.0 / PetscSqrtReal(N); 414 PetscCall(VecSet(l, sum)); 415 PetscCall(MatMult(mat, l, r)); 416 PetscCall(VecNorm(r, NORM_2, &nrm)); 417 if (nrm >= tol) consistent = PETSC_FALSE; 418 if (flg1) { 419 if (consistent) { 420 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Constants are likely null vector")); 421 } else { 422 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Constants are unlikely null vector ")); 423 } 424 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "|| A * 1/N || = %g\n", (double)nrm)); 425 } 426 if (!consistent && flg1) PetscCall(VecView(r, viewer)); 427 if (!consistent && flg2) PetscCall(VecView(r, viewer)); 428 PetscCall(VecDestroy(&r)); 429 } 430 431 for (j = 0; j < n; j++) { 432 PetscCall((*mat->ops->mult)(mat, sp->vecs[j], l)); 433 PetscCall(VecNorm(l, NORM_2, &nrm)); 434 if (nrm >= tol) consistent = PETSC_FALSE; 435 if (flg1) { 436 if (consistent) { 437 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Null vector %" PetscInt_FMT " is likely null vector", j)); 438 } else { 439 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Null vector %" PetscInt_FMT " unlikely null vector ", j)); 440 consistent = PETSC_FALSE; 441 } 442 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "|| A * v[%" PetscInt_FMT "] || = %g\n", j, (double)nrm)); 443 } 444 if (!consistent && flg1) PetscCall(VecView(l, viewer)); 445 if (!consistent && flg2) PetscCall(VecView(l, viewer)); 446 } 447 448 PetscCheck(!sp->remove, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Cannot test a null space provided as a function with MatNullSpaceSetFunction()"); 449 PetscCall(VecDestroy(&l)); 450 if (isNull) *isNull = consistent; 451 PetscFunctionReturn(0); 452 } 453