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