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