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 #undef __FUNCT__ 11 #define __FUNCT__ "MatNullSpaceSetFunction" 12 /*@C 13 MatNullSpaceSetFunction - set a function that removes a null space from a vector 14 out of null spaces. 15 16 Logically Collective on MatNullSpace 17 18 Input Parameters: 19 + sp - the null space object 20 . rem - the function that removes the null space 21 - ctx - context for the remove function 22 23 Level: advanced 24 25 .keywords: PC, null space, create 26 27 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceCreate() 28 @*/ 29 PetscErrorCode MatNullSpaceSetFunction(MatNullSpace sp, PetscErrorCode (*rem)(MatNullSpace,Vec,void*),void *ctx) 30 { 31 PetscFunctionBegin; 32 PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1); 33 sp->remove = rem; 34 sp->rmctx = ctx; 35 PetscFunctionReturn(0); 36 } 37 38 #undef __FUNCT__ 39 #define __FUNCT__ "MatNullSpaceGetVecs" 40 /*@C 41 MatNullSpaceGetVecs - get vectors defining the null space 42 43 Not Collective 44 45 Input Arguments: 46 . sp - null space object 47 48 Output Arguments: 49 + has_cnst - PETSC_TRUE if the null space contains the constant vector, otherwise PETSC_FALSE 50 . n - number of vectors (excluding constant vector) in null space 51 - vecs - orthonormal vectors that span the null space (excluding the constant vector) 52 53 Level: developer 54 55 Notes: 56 These vectors and the array are owned by the MatNullSpace and should not be destroyed or freeded by the caller 57 58 .seealso: MatNullSpaceCreate(), MatGetNullSpace(), MatGetNearNullSpace() 59 @*/ 60 PetscErrorCode MatNullSpaceGetVecs(MatNullSpace sp,PetscBool *has_const,PetscInt *n,const Vec **vecs) 61 { 62 63 PetscFunctionBegin; 64 PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1); 65 if (has_const) *has_const = sp->has_cnst; 66 if (n) *n = sp->n; 67 if (vecs) *vecs = sp->vecs; 68 PetscFunctionReturn(0); 69 } 70 71 #undef __FUNCT__ 72 #define __FUNCT__ "MatNullSpaceCreateRigidBody" 73 /*@ 74 MatNullSpaceCreateRigidBody - create rigid body modes from coordinates 75 76 Collective on Vec 77 78 Input Argument: 79 . coords - block of coordinates of each node, must have block size set 80 81 Output Argument: 82 . sp - the null space 83 84 Level: advanced 85 86 .seealso: MatNullSpaceCreate() 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 #undef __FUNCT__ 165 #define __FUNCT__ "MatNullSpaceView" 166 /*@C 167 MatNullSpaceView - Visualizes a null space object. 168 169 Collective on MatNullSpace 170 171 Input Parameters: 172 + matnull - the null space 173 - viewer - visualization context 174 175 Level: advanced 176 177 Fortran Note: 178 This routine is not supported in Fortran. 179 180 .seealso: MatNullSpaceCreate(), PetscViewerASCIIOpen() 181 @*/ 182 PetscErrorCode MatNullSpaceView(MatNullSpace sp,PetscViewer viewer) 183 { 184 PetscErrorCode ierr; 185 PetscBool iascii; 186 187 PetscFunctionBegin; 188 PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1); 189 if (!viewer) { 190 ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr); 191 } 192 PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2); 193 PetscCheckSameComm(sp,1,viewer,2); 194 195 ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr); 196 if (iascii) { 197 PetscViewerFormat format; 198 PetscInt i; 199 ierr = PetscViewerGetFormat(viewer,&format);CHKERRQ(ierr); 200 ierr = PetscObjectPrintClassNamePrefixType((PetscObject)sp,viewer);CHKERRQ(ierr); 201 ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr); 202 ierr = PetscViewerASCIIPrintf(viewer,"Contains %D vector%s%s\n",sp->n,sp->n==1 ? "" : "s",sp->has_cnst ? " and the constant" : "");CHKERRQ(ierr); 203 if (sp->remove) {ierr = PetscViewerASCIIPrintf(viewer,"Has user-provided removal function\n");CHKERRQ(ierr);} 204 if (!(format == PETSC_VIEWER_ASCII_INFO || format == PETSC_VIEWER_ASCII_INFO_DETAIL)) { 205 for (i=0; i<sp->n; i++) { 206 ierr = VecView(sp->vecs[i],viewer);CHKERRQ(ierr); 207 } 208 } 209 ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr); 210 } 211 PetscFunctionReturn(0); 212 } 213 214 #undef __FUNCT__ 215 #define __FUNCT__ "MatNullSpaceCreate" 216 /*@ 217 MatNullSpaceCreate - Creates a data structure used to project vectors 218 out of null spaces. 219 220 Collective on MPI_Comm 221 222 Input Parameters: 223 + comm - the MPI communicator associated with the object 224 . has_cnst - PETSC_TRUE if the null space contains the constant vector; otherwise PETSC_FALSE 225 . n - number of vectors (excluding constant vector) in null space 226 - vecs - the vectors that span the null space (excluding the constant vector); 227 these vectors must be orthonormal. These vectors are NOT copied, so do not change them 228 after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count 229 for them by one). 230 231 Output Parameter: 232 . SP - the null space context 233 234 Level: advanced 235 236 Notes: See MatNullSpaceSetFunction() as an alternative way of providing the null space information instead of setting vecs. 237 238 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 239 need to pass in a function that eliminates the constant function into MatNullSpaceSetFunction(). 240 241 Users manual sections: 242 . sec_singular 243 244 .keywords: PC, null space, create 245 246 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceSetFunction() 247 @*/ 248 PetscErrorCode MatNullSpaceCreate(MPI_Comm comm,PetscBool has_cnst,PetscInt n,const Vec vecs[],MatNullSpace *SP) 249 { 250 MatNullSpace sp; 251 PetscErrorCode ierr; 252 PetscInt i; 253 254 PetscFunctionBegin; 255 if (n < 0) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of vectors (given %D) cannot be negative",n); 256 if (n) PetscValidPointer(vecs,4); 257 for (i=0; i<n; i++) PetscValidHeaderSpecific(vecs[i],VEC_CLASSID,4); 258 PetscValidPointer(SP,5); 259 #if defined(PETSC_USE_DEBUG) 260 if (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.0) > 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 #endif 285 286 *SP = NULL; 287 ierr = MatInitializePackage();CHKERRQ(ierr); 288 289 ierr = PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr); 290 291 sp->has_cnst = has_cnst; 292 sp->n = n; 293 sp->vecs = 0; 294 sp->alpha = 0; 295 sp->remove = 0; 296 sp->rmctx = 0; 297 298 if (n) { 299 ierr = PetscMalloc1(n,&sp->vecs);CHKERRQ(ierr); 300 ierr = PetscMalloc1(n,&sp->alpha);CHKERRQ(ierr); 301 ierr = PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr); 302 for (i=0; i<n; i++) { 303 ierr = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr); 304 sp->vecs[i] = vecs[i]; 305 } 306 } 307 308 *SP = sp; 309 PetscFunctionReturn(0); 310 } 311 312 #undef __FUNCT__ 313 #define __FUNCT__ "MatNullSpaceDestroy" 314 /*@ 315 MatNullSpaceDestroy - Destroys a data structure used to project vectors 316 out of null spaces. 317 318 Collective on MatNullSpace 319 320 Input Parameter: 321 . sp - the null space context to be destroyed 322 323 Level: advanced 324 325 .keywords: PC, null space, destroy 326 327 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction() 328 @*/ 329 PetscErrorCode MatNullSpaceDestroy(MatNullSpace *sp) 330 { 331 PetscErrorCode ierr; 332 333 PetscFunctionBegin; 334 if (!*sp) PetscFunctionReturn(0); 335 PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1); 336 if (--((PetscObject)(*sp))->refct > 0) {*sp = 0; PetscFunctionReturn(0);} 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 #undef __FUNCT__ 345 #define __FUNCT__ "MatNullSpaceRemove" 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 353 - vec - the vector from which the null space is to be removed 354 355 Level: advanced 356 357 .keywords: PC, null space, remove 358 359 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction() 360 @*/ 361 PetscErrorCode MatNullSpaceRemove(MatNullSpace sp,Vec vec) 362 { 363 PetscScalar sum; 364 PetscInt i,N; 365 PetscErrorCode ierr; 366 367 PetscFunctionBegin; 368 PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1); 369 PetscValidHeaderSpecific(vec,VEC_CLASSID,2); 370 371 if (sp->has_cnst) { 372 ierr = VecGetSize(vec,&N);CHKERRQ(ierr); 373 if (N > 0) { 374 ierr = VecSum(vec,&sum);CHKERRQ(ierr); 375 sum = sum/((PetscScalar)(-1.0*N)); 376 ierr = VecShift(vec,sum);CHKERRQ(ierr); 377 } 378 } 379 380 if (sp->n) { 381 ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr); 382 for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i]; 383 ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr); 384 } 385 386 if (sp->remove) { 387 ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr); 388 } 389 PetscFunctionReturn(0); 390 } 391 392 #undef __FUNCT__ 393 #define __FUNCT__ "MatNullSpaceTest" 394 /*@ 395 MatNullSpaceTest - Tests if the claimed null space is really a 396 null space of a matrix 397 398 Collective on MatNullSpace 399 400 Input Parameters: 401 + sp - the null space context 402 - mat - the matrix 403 404 Output Parameters: 405 . isNull - PETSC_TRUE if the nullspace is valid for this matrix 406 407 Level: advanced 408 409 .keywords: PC, null space, remove 410 411 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction() 412 @*/ 413 PetscErrorCode MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool *isNull) 414 { 415 PetscScalar sum; 416 PetscReal nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON; 417 PetscInt j,n,N; 418 PetscErrorCode ierr; 419 Vec l,r; 420 PetscBool flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE; 421 PetscViewer viewer; 422 423 PetscFunctionBegin; 424 PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1); 425 PetscValidHeaderSpecific(mat,MAT_CLASSID,2); 426 n = sp->n; 427 ierr = PetscOptionsGetBool(((PetscObject)sp)->options,NULL,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr); 428 ierr = PetscOptionsGetBool(((PetscObject)sp)->options,NULL,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr); 429 430 if (n) { 431 ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr); 432 } else { 433 ierr = MatCreateVecs(mat,&l,NULL);CHKERRQ(ierr); 434 } 435 436 ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr); 437 if (sp->has_cnst) { 438 ierr = VecDuplicate(l,&r);CHKERRQ(ierr); 439 ierr = VecGetSize(l,&N);CHKERRQ(ierr); 440 sum = 1.0/N; 441 ierr = VecSet(l,sum);CHKERRQ(ierr); 442 ierr = MatMult(mat,l,r);CHKERRQ(ierr); 443 ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr); 444 if (nrm >= tol) consistent = PETSC_FALSE; 445 if (flg1) { 446 if (consistent) { 447 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr); 448 } else { 449 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr); 450 } 451 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm);CHKERRQ(ierr); 452 } 453 if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);} 454 if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);} 455 ierr = VecDestroy(&r);CHKERRQ(ierr); 456 } 457 458 for (j=0; j<n; j++) { 459 ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr); 460 ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr); 461 if (nrm >= tol) consistent = PETSC_FALSE; 462 if (flg1) { 463 if (consistent) { 464 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr); 465 } else { 466 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr); 467 consistent = PETSC_FALSE; 468 } 469 ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %g\n",j,(double)nrm);CHKERRQ(ierr); 470 } 471 if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);} 472 if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);} 473 } 474 475 if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()"); 476 ierr = VecDestroy(&l);CHKERRQ(ierr); 477 if (isNull) *isNull = consistent; 478 PetscFunctionReturn(0); 479 } 480 481