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