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