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 (PetscUnlikelyDebug(n)) { 261 PetscScalar *dots; 262 for (i=0; i<n; i++) { 263 PetscReal norm; 264 ierr = VecNorm(vecs[i],NORM_2,&norm);CHKERRQ(ierr); 265 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); 266 } 267 if (has_cnst) { 268 for (i=0; i<n; i++) { 269 PetscScalar sum; 270 ierr = VecSum(vecs[i],&sum);CHKERRQ(ierr); 271 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)); 272 } 273 } 274 ierr = PetscMalloc1(n-1,&dots);CHKERRQ(ierr); 275 for (i=0; i<n-1; i++) { 276 PetscInt j; 277 ierr = VecMDot(vecs[i],n-i-1,vecs+i+1,dots);CHKERRQ(ierr); 278 for (j=0;j<n-i-1;j++) { 279 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])); 280 } 281 } 282 PetscFree(dots);CHKERRQ(ierr); 283 } 284 285 *SP = NULL; 286 ierr = MatInitializePackage();CHKERRQ(ierr); 287 288 ierr = PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr); 289 290 sp->has_cnst = has_cnst; 291 sp->n = n; 292 sp->vecs = NULL; 293 sp->alpha = NULL; 294 sp->remove = NULL; 295 sp->rmctx = NULL; 296 297 if (n) { 298 ierr = PetscMalloc1(n,&sp->vecs);CHKERRQ(ierr); 299 ierr = PetscMalloc1(n,&sp->alpha);CHKERRQ(ierr); 300 ierr = PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr); 301 for (i=0; i<n; i++) { 302 ierr = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr); 303 sp->vecs[i] = vecs[i]; 304 } 305 } 306 307 *SP = sp; 308 PetscFunctionReturn(0); 309 } 310 311 /*@ 312 MatNullSpaceDestroy - Destroys a data structure used to project vectors 313 out of null spaces. 314 315 Collective on MatNullSpace 316 317 Input Parameter: 318 . sp - the null space context to be destroyed 319 320 Level: advanced 321 322 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction() 323 @*/ 324 PetscErrorCode MatNullSpaceDestroy(MatNullSpace *sp) 325 { 326 PetscErrorCode ierr; 327 PetscInt i; 328 329 PetscFunctionBegin; 330 if (!*sp) PetscFunctionReturn(0); 331 PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1); 332 if (--((PetscObject)(*sp))->refct > 0) {*sp = NULL; PetscFunctionReturn(0);} 333 334 for (i=0; i < (*sp)->n; i++) { 335 ierr = VecLockReadPop((*sp)->vecs[i]);CHKERRQ(ierr); 336 } 337 338 ierr = VecDestroyVecs((*sp)->n,&(*sp)->vecs);CHKERRQ(ierr); 339 ierr = PetscFree((*sp)->alpha);CHKERRQ(ierr); 340 ierr = PetscHeaderDestroy(sp);CHKERRQ(ierr); 341 PetscFunctionReturn(0); 342 } 343 344 /*@C 345 MatNullSpaceRemove - Removes all the components of a null space from a vector. 346 347 Collective on MatNullSpace 348 349 Input Parameters: 350 + sp - the null space context (if this is NULL then no null space is removed) 351 - vec - the vector from which the null space is to be removed 352 353 Level: advanced 354 355 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction() 356 @*/ 357 PetscErrorCode MatNullSpaceRemove(MatNullSpace sp,Vec vec) 358 { 359 PetscScalar sum; 360 PetscInt i,N; 361 PetscErrorCode ierr; 362 363 PetscFunctionBegin; 364 if (!sp) PetscFunctionReturn(0); 365 PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1); 366 PetscValidHeaderSpecific(vec,VEC_CLASSID,2); 367 368 if (sp->has_cnst) { 369 ierr = VecGetSize(vec,&N);CHKERRQ(ierr); 370 if (N > 0) { 371 ierr = VecSum(vec,&sum);CHKERRQ(ierr); 372 sum = sum/((PetscScalar)(-1.0*N)); 373 ierr = VecShift(vec,sum);CHKERRQ(ierr); 374 } 375 } 376 377 if (sp->n) { 378 ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr); 379 for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i]; 380 ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr); 381 } 382 383 if (sp->remove) { 384 ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr); 385 } 386 PetscFunctionReturn(0); 387 } 388 389 /*@ 390 MatNullSpaceTest - Tests if the claimed null space is really a 391 null space of a matrix 392 393 Collective on MatNullSpace 394 395 Input Parameters: 396 + sp - the null space context 397 - mat - the matrix 398 399 Output Parameters: 400 . isNull - PETSC_TRUE if the nullspace is valid for this matrix 401 402 Level: advanced 403 404 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction() 405 @*/ 406 PetscErrorCode MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool *isNull) 407 { 408 PetscScalar sum; 409 PetscReal nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON; 410 PetscInt j,n,N; 411 PetscErrorCode ierr; 412 Vec l,r; 413 PetscBool flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE; 414 PetscViewer viewer; 415 416 PetscFunctionBegin; 417 PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1); 418 PetscValidHeaderSpecific(mat,MAT_CLASSID,2); 419 n = sp->n; 420 ierr = PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr); 421 ierr = PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr); 422 423 if (n) { 424 ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr); 425 } else { 426 ierr = MatCreateVecs(mat,&l,NULL);CHKERRQ(ierr); 427 } 428 429 ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr); 430 if (sp->has_cnst) { 431 ierr = VecDuplicate(l,&r);CHKERRQ(ierr); 432 ierr = VecGetSize(l,&N);CHKERRQ(ierr); 433 sum = 1.0/N; 434 ierr = VecSet(l,sum);CHKERRQ(ierr); 435 ierr = MatMult(mat,l,r);CHKERRQ(ierr); 436 ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr); 437 if (nrm >= tol) consistent = PETSC_FALSE; 438 if (flg1) { 439 if (consistent) { 440 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr); 441 } else { 442 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr); 443 } 444 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm);CHKERRQ(ierr); 445 } 446 if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);} 447 if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);} 448 ierr = VecDestroy(&r);CHKERRQ(ierr); 449 } 450 451 for (j=0; j<n; j++) { 452 ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr); 453 ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr); 454 if (nrm >= tol) consistent = PETSC_FALSE; 455 if (flg1) { 456 if (consistent) { 457 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr); 458 } else { 459 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr); 460 consistent = PETSC_FALSE; 461 } 462 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %g\n",j,(double)nrm);CHKERRQ(ierr); 463 } 464 if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);} 465 if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);} 466 } 467 468 if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()"); 469 ierr = VecDestroy(&l);CHKERRQ(ierr); 470 if (isNull) *isNull = consistent; 471 PetscFunctionReturn(0); 472 } 473