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