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