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 MatNullSpace 15 16 Input Parameters: 17 + sp - the 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: `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 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 Notes: 49 These vectors and the array are owned by the MatNullSpace and should not be destroyed or freeded by the caller 50 51 .seealso: `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 Vec 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: `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 MatNullSpace 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: `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 data structure used to project vectors 200 out of null spaces. 201 202 Collective 203 204 Input Parameters: 205 + comm - the MPI communicator associated with the object 206 . has_cnst - PETSC_TRUE if the null space contains the constant vector; otherwise PETSC_FALSE 207 . n - number of vectors (excluding constant vector) in null space 208 - vecs - the vectors that span the null space (excluding the constant vector); 209 these vectors must be orthonormal. These vectors are NOT copied, so do not change them 210 after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count 211 for them by one). 212 213 Output Parameter: 214 . SP - the null space context 215 216 Level: advanced 217 218 Notes: 219 See MatNullSpaceSetFunction() as an alternative way of providing the null space information instead of setting vecs. 220 221 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 222 need to pass in a function that eliminates the constant function into MatNullSpaceSetFunction(). 223 224 .seealso: `MatNullSpaceDestroy()`, `MatNullSpaceRemove()`, `MatSetNullSpace()`, `MatNullSpace`, `MatNullSpaceSetFunction()` 225 @*/ 226 PetscErrorCode MatNullSpaceCreate(MPI_Comm comm, PetscBool has_cnst, PetscInt n, const Vec vecs[], MatNullSpace *SP) { 227 MatNullSpace sp; 228 PetscInt i; 229 230 PetscFunctionBegin; 231 PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of vectors (given %" PetscInt_FMT ") cannot be negative", n); 232 if (n) PetscValidPointer(vecs, 4); 233 for (i = 0; i < n; i++) PetscValidHeaderSpecific(vecs[i], VEC_CLASSID, 4); 234 PetscValidPointer(SP, 5); 235 if (n) { 236 for (i = 0; i < n; i++) { 237 /* prevent the user from changes values in the vector */ 238 PetscCall(VecLockReadPush(vecs[i])); 239 } 240 } 241 if (PetscUnlikelyDebug(n)) { 242 PetscScalar *dots; 243 for (i = 0; i < n; i++) { 244 PetscReal norm; 245 PetscCall(VecNorm(vecs[i], NORM_2, &norm)); 246 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); 247 } 248 if (has_cnst) { 249 for (i = 0; i < n; i++) { 250 PetscScalar sum; 251 PetscCall(VecSum(vecs[i], &sum)); 252 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)); 253 } 254 } 255 PetscCall(PetscMalloc1(n - 1, &dots)); 256 for (i = 0; i < n - 1; i++) { 257 PetscInt j; 258 PetscCall(VecMDot(vecs[i], n - i - 1, vecs + i + 1, dots)); 259 for (j = 0; j < n - i - 1; j++) { 260 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])); 261 } 262 } 263 PetscCall(PetscFree(dots)); 264 } 265 266 *SP = NULL; 267 PetscCall(MatInitializePackage()); 268 269 PetscCall(PetscHeaderCreate(sp, MAT_NULLSPACE_CLASSID, "MatNullSpace", "Null space", "Mat", comm, MatNullSpaceDestroy, MatNullSpaceView)); 270 271 sp->has_cnst = has_cnst; 272 sp->n = n; 273 sp->vecs = NULL; 274 sp->alpha = NULL; 275 sp->remove = NULL; 276 sp->rmctx = NULL; 277 278 if (n) { 279 PetscCall(PetscMalloc1(n, &sp->vecs)); 280 PetscCall(PetscMalloc1(n, &sp->alpha)); 281 PetscCall(PetscLogObjectMemory((PetscObject)sp, n * (sizeof(Vec) + sizeof(PetscScalar)))); 282 for (i = 0; i < n; i++) { 283 PetscCall(PetscObjectReference((PetscObject)vecs[i])); 284 sp->vecs[i] = vecs[i]; 285 } 286 } 287 288 *SP = sp; 289 PetscFunctionReturn(0); 290 } 291 292 /*@ 293 MatNullSpaceDestroy - Destroys a data structure used to project vectors 294 out of null spaces. 295 296 Collective on MatNullSpace 297 298 Input Parameter: 299 . sp - the null space context to be destroyed 300 301 Level: advanced 302 303 .seealso: `MatNullSpaceCreate()`, `MatNullSpaceRemove()`, `MatNullSpaceSetFunction()` 304 @*/ 305 PetscErrorCode MatNullSpaceDestroy(MatNullSpace *sp) { 306 PetscInt i; 307 308 PetscFunctionBegin; 309 if (!*sp) PetscFunctionReturn(0); 310 PetscValidHeaderSpecific((*sp), MAT_NULLSPACE_CLASSID, 1); 311 if (--((PetscObject)(*sp))->refct > 0) { 312 *sp = NULL; 313 PetscFunctionReturn(0); 314 } 315 316 for (i = 0; i < (*sp)->n; i++) PetscCall(VecLockReadPop((*sp)->vecs[i])); 317 318 PetscCall(VecDestroyVecs((*sp)->n, &(*sp)->vecs)); 319 PetscCall(PetscFree((*sp)->alpha)); 320 PetscCall(PetscHeaderDestroy(sp)); 321 PetscFunctionReturn(0); 322 } 323 324 /*@C 325 MatNullSpaceRemove - Removes all the components of a null space from a vector. 326 327 Collective on MatNullSpace 328 329 Input Parameters: 330 + sp - the null space context (if this is NULL then no null space is removed) 331 - vec - the vector from which the null space is to be removed 332 333 Level: advanced 334 335 .seealso: `MatNullSpaceCreate()`, `MatNullSpaceDestroy()`, `MatNullSpaceSetFunction()` 336 @*/ 337 PetscErrorCode MatNullSpaceRemove(MatNullSpace sp, Vec vec) { 338 PetscScalar sum; 339 PetscInt i, N; 340 341 PetscFunctionBegin; 342 if (!sp) PetscFunctionReturn(0); 343 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 344 PetscValidHeaderSpecific(vec, VEC_CLASSID, 2); 345 346 if (sp->has_cnst) { 347 PetscCall(VecGetSize(vec, &N)); 348 if (N > 0) { 349 PetscCall(VecSum(vec, &sum)); 350 sum = sum / ((PetscScalar)(-1.0 * N)); 351 PetscCall(VecShift(vec, sum)); 352 } 353 } 354 355 if (sp->n) { 356 PetscCall(VecMDot(vec, sp->n, sp->vecs, sp->alpha)); 357 for (i = 0; i < sp->n; i++) sp->alpha[i] = -sp->alpha[i]; 358 PetscCall(VecMAXPY(vec, sp->n, sp->alpha, sp->vecs)); 359 } 360 361 if (sp->remove) PetscCall((*sp->remove)(sp, vec, sp->rmctx)); 362 PetscFunctionReturn(0); 363 } 364 365 /*@ 366 MatNullSpaceTest - Tests if the claimed null space is really a 367 null space of a matrix 368 369 Collective on MatNullSpace 370 371 Input Parameters: 372 + sp - the null space context 373 - mat - the matrix 374 375 Output Parameters: 376 . isNull - PETSC_TRUE if the nullspace is valid for this matrix 377 378 Level: advanced 379 380 .seealso: `MatNullSpaceCreate()`, `MatNullSpaceDestroy()`, `MatNullSpaceSetFunction()` 381 @*/ 382 PetscErrorCode MatNullSpaceTest(MatNullSpace sp, Mat mat, PetscBool *isNull) { 383 PetscScalar sum; 384 PetscReal nrm, tol = 10. * PETSC_SQRT_MACHINE_EPSILON; 385 PetscInt j, n, N; 386 Vec l, r; 387 PetscBool flg1 = PETSC_FALSE, flg2 = PETSC_FALSE, consistent = PETSC_TRUE; 388 PetscViewer viewer; 389 390 PetscFunctionBegin; 391 PetscValidHeaderSpecific(sp, MAT_NULLSPACE_CLASSID, 1); 392 PetscValidHeaderSpecific(mat, MAT_CLASSID, 2); 393 n = sp->n; 394 PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options, ((PetscObject)mat)->prefix, "-mat_null_space_test_view", &flg1, NULL)); 395 PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options, ((PetscObject)mat)->prefix, "-mat_null_space_test_view_draw", &flg2, NULL)); 396 397 if (n) { 398 PetscCall(VecDuplicate(sp->vecs[0], &l)); 399 } else { 400 PetscCall(MatCreateVecs(mat, &l, NULL)); 401 } 402 403 PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp), &viewer)); 404 if (sp->has_cnst) { 405 PetscCall(VecDuplicate(l, &r)); 406 PetscCall(VecGetSize(l, &N)); 407 sum = 1.0 / PetscSqrtReal(N); 408 PetscCall(VecSet(l, sum)); 409 PetscCall(MatMult(mat, l, r)); 410 PetscCall(VecNorm(r, NORM_2, &nrm)); 411 if (nrm >= tol) consistent = PETSC_FALSE; 412 if (flg1) { 413 if (consistent) { 414 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Constants are likely null vector")); 415 } else { 416 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Constants are unlikely null vector ")); 417 } 418 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "|| A * 1/N || = %g\n", (double)nrm)); 419 } 420 if (!consistent && flg1) PetscCall(VecView(r, viewer)); 421 if (!consistent && flg2) PetscCall(VecView(r, viewer)); 422 PetscCall(VecDestroy(&r)); 423 } 424 425 for (j = 0; j < n; j++) { 426 PetscCall((*mat->ops->mult)(mat, sp->vecs[j], l)); 427 PetscCall(VecNorm(l, NORM_2, &nrm)); 428 if (nrm >= tol) consistent = PETSC_FALSE; 429 if (flg1) { 430 if (consistent) { 431 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Null vector %" PetscInt_FMT " is likely null vector", j)); 432 } else { 433 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "Null vector %" PetscInt_FMT " unlikely null vector ", j)); 434 consistent = PETSC_FALSE; 435 } 436 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp), "|| A * v[%" PetscInt_FMT "] || = %g\n", j, (double)nrm)); 437 } 438 if (!consistent && flg1) PetscCall(VecView(l, viewer)); 439 if (!consistent && flg2) PetscCall(VecView(l, viewer)); 440 } 441 442 PetscCheck(!sp->remove, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Cannot test a null space provided as a function with MatNullSpaceSetFunction()"); 443 PetscCall(VecDestroy(&l)); 444 if (isNull) *isNull = consistent; 445 PetscFunctionReturn(0); 446 } 447