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