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