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 for (i = 0; i < n; i++) { 281 PetscCall(PetscObjectReference((PetscObject)vecs[i])); 282 sp->vecs[i] = vecs[i]; 283 } 284 } 285 286 *SP = sp; 287 PetscFunctionReturn(0); 288 } 289 290 /*@ 291 MatNullSpaceDestroy - Destroys a data structure used to project vectors out of null spaces. 292 293 Collective on sp 294 295 Input Parameter: 296 . sp - the null space context to be destroyed 297 298 Level: advanced 299 300 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatNullSpaceRemove()`, `MatNullSpaceSetFunction()` 301 @*/ 302 PetscErrorCode MatNullSpaceDestroy(MatNullSpace *sp) { 303 PetscInt i; 304 305 PetscFunctionBegin; 306 if (!*sp) PetscFunctionReturn(0); 307 PetscValidHeaderSpecific((*sp), MAT_NULLSPACE_CLASSID, 1); 308 if (--((PetscObject)(*sp))->refct > 0) { 309 *sp = NULL; 310 PetscFunctionReturn(0); 311 } 312 313 for (i = 0; i < (*sp)->n; i++) PetscCall(VecLockReadPop((*sp)->vecs[i])); 314 315 PetscCall(VecDestroyVecs((*sp)->n, &(*sp)->vecs)); 316 PetscCall(PetscFree((*sp)->alpha)); 317 PetscCall(PetscHeaderDestroy(sp)); 318 PetscFunctionReturn(0); 319 } 320 321 /*@C 322 MatNullSpaceRemove - Removes all the components of a null space from a vector. 323 324 Collective on sp 325 326 Input Parameters: 327 + sp - the null space context (if this is NULL then no null space is removed) 328 - vec - the vector from which the null space is to be removed 329 330 Level: advanced 331 332 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatNullSpaceDestroy()`, `MatNullSpaceSetFunction()` 333 @*/ 334 PetscErrorCode MatNullSpaceRemove(MatNullSpace sp, Vec vec) { 335 PetscScalar sum; 336 PetscInt i, N; 337 338 PetscFunctionBegin; 339 if (!sp) PetscFunctionReturn(0); 340 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 341 PetscValidHeaderSpecific(vec, VEC_CLASSID, 2); 342 343 if (sp->has_cnst) { 344 PetscCall(VecGetSize(vec, &N)); 345 if (N > 0) { 346 PetscCall(VecSum(vec, &sum)); 347 sum = sum / ((PetscScalar)(-1.0 * N)); 348 PetscCall(VecShift(vec, sum)); 349 } 350 } 351 352 if (sp->n) { 353 PetscCall(VecMDot(vec, sp->n, sp->vecs, sp->alpha)); 354 for (i = 0; i < sp->n; i++) sp->alpha[i] = -sp->alpha[i]; 355 PetscCall(VecMAXPY(vec, sp->n, sp->alpha, sp->vecs)); 356 } 357 358 if (sp->remove) PetscCall((*sp->remove)(sp, vec, sp->rmctx)); 359 PetscFunctionReturn(0); 360 } 361 362 /*@ 363 MatNullSpaceTest - Tests if the claimed null space is really a null space of a matrix 364 365 Collective on sp 366 367 Input Parameters: 368 + sp - the null space context 369 - mat - the matrix 370 371 Output Parameters: 372 . isNull - `PETSC_TRUE` if the nullspace is valid for this matrix 373 374 Level: advanced 375 376 .seealso: `MatNullSpace`, `MatNullSpaceCreate()`, `MatNullSpaceDestroy()`, `MatNullSpaceSetFunction()` 377 @*/ 378 PetscErrorCode MatNullSpaceTest(MatNullSpace sp, Mat mat, PetscBool *isNull) { 379 PetscScalar sum; 380 PetscReal nrm, tol = 10. * PETSC_SQRT_MACHINE_EPSILON; 381 PetscInt j, n, N; 382 Vec l, r; 383 PetscBool flg1 = PETSC_FALSE, flg2 = PETSC_FALSE, consistent = PETSC_TRUE; 384 PetscViewer viewer; 385 386 PetscFunctionBegin; 387 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 388 PetscValidHeaderSpecific(mat, MAT_CLASSID, 2); 389 n = sp->n; 390 PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options, ((PetscObject)mat)->prefix, "-mat_null_space_test_view", &flg1, NULL)); 391 PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options, ((PetscObject)mat)->prefix, "-mat_null_space_test_view_draw", &flg2, NULL)); 392 393 if (n) { 394 PetscCall(VecDuplicate(sp->vecs[0], &l)); 395 } else { 396 PetscCall(MatCreateVecs(mat, &l, NULL)); 397 } 398 399 PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp), &viewer)); 400 if (sp->has_cnst) { 401 PetscCall(VecDuplicate(l, &r)); 402 PetscCall(VecGetSize(l, &N)); 403 sum = 1.0 / PetscSqrtReal(N); 404 PetscCall(VecSet(l, sum)); 405 PetscCall(MatMult(mat, l, r)); 406 PetscCall(VecNorm(r, NORM_2, &nrm)); 407 if (nrm >= tol) consistent = PETSC_FALSE; 408 if (flg1) { 409 if (consistent) { 410 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Constants are likely null vector")); 411 } else { 412 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Constants are unlikely null vector ")); 413 } 414 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "|| A * 1/N || = %g\n", (double)nrm)); 415 } 416 if (!consistent && flg1) PetscCall(VecView(r, viewer)); 417 if (!consistent && flg2) PetscCall(VecView(r, viewer)); 418 PetscCall(VecDestroy(&r)); 419 } 420 421 for (j = 0; j < n; j++) { 422 PetscCall((*mat->ops->mult)(mat, sp->vecs[j], l)); 423 PetscCall(VecNorm(l, NORM_2, &nrm)); 424 if (nrm >= tol) consistent = PETSC_FALSE; 425 if (flg1) { 426 if (consistent) { 427 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Null vector %" PetscInt_FMT " is likely null vector", j)); 428 } else { 429 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Null vector %" PetscInt_FMT " unlikely null vector ", j)); 430 consistent = PETSC_FALSE; 431 } 432 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "|| A * v[%" PetscInt_FMT "] || = %g\n", j, (double)nrm)); 433 } 434 if (!consistent && flg1) PetscCall(VecView(l, viewer)); 435 if (!consistent && flg2) PetscCall(VecView(l, viewer)); 436 } 437 438 PetscCheck(!sp->remove, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Cannot test a null space provided as a function with MatNullSpaceSetFunction()"); 439 PetscCall(VecDestroy(&l)); 440 if (isNull) *isNull = consistent; 441 PetscFunctionReturn(0); 442 } 443