xref: /petsc/src/mat/interface/matnull.c (revision e8f1478504fc744590f99974189eaf6dec42c56e)
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 .keywords: PC, null space, create
24 
25 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceCreate()
26 @*/
27 PetscErrorCode  MatNullSpaceSetFunction(MatNullSpace sp, PetscErrorCode (*rem)(MatNullSpace,Vec,void*),void *ctx)
28 {
29   PetscFunctionBegin;
30   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
31   sp->remove = rem;
32   sp->rmctx  = ctx;
33   PetscFunctionReturn(0);
34 }
35 
36 /*@C
37    MatNullSpaceGetVecs - get vectors defining the null space
38 
39    Not Collective
40 
41    Input Arguments:
42 .  sp - null space object
43 
44    Output Arguments:
45 +  has_cnst - PETSC_TRUE if the null space contains the constant vector, otherwise PETSC_FALSE
46 .  n - number of vectors (excluding constant vector) in null space
47 -  vecs - orthonormal vectors that span the null space (excluding the constant vector)
48 
49    Level: developer
50 
51    Notes:
52       These vectors and the array are owned by the MatNullSpace and should not be destroyed or freeded by the caller
53 
54 .seealso: MatNullSpaceCreate(), MatGetNullSpace(), MatGetNearNullSpace()
55 @*/
56 PetscErrorCode MatNullSpaceGetVecs(MatNullSpace sp,PetscBool *has_const,PetscInt *n,const Vec **vecs)
57 {
58 
59   PetscFunctionBegin;
60   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
61   if (has_const) *has_const = sp->has_cnst;
62   if (n) *n = sp->n;
63   if (vecs) *vecs = sp->vecs;
64   PetscFunctionReturn(0);
65 }
66 
67 /*@
68    MatNullSpaceCreateRigidBody - create rigid body modes from coordinates
69 
70    Collective on Vec
71 
72    Input Argument:
73 .  coords - block of coordinates of each node, must have block size set
74 
75    Output Argument:
76 .  sp - the null space
77 
78    Level: advanced
79 
80    Notes:
81     If you are solving an elasticity problems you should likely use this, in conjunction with ee MatSetNearNullspace(), to provide information that
82            the PCGAMG preconditioner can use to construct a much more efficient preconditioner.
83 
84            If you are solving an elasticity problem with pure Neumann boundary conditions you can use this in conjunction with MatSetNullspace() to
85            provide this information to the linear solver so it can handle the null space appropriately in the linear solution.
86 
87 
88 .seealso: MatNullSpaceCreate(), MatSetNearNullspace(), MatSetNullspace()
89 @*/
90 PetscErrorCode MatNullSpaceCreateRigidBody(Vec coords,MatNullSpace *sp)
91 {
92   PetscErrorCode    ierr;
93   const PetscScalar *x;
94   PetscScalar       *v[6],dots[5];
95   Vec               vec[6];
96   PetscInt          n,N,dim,nmodes,i,j;
97   PetscReal         sN;
98 
99   PetscFunctionBegin;
100   ierr = VecGetBlockSize(coords,&dim);CHKERRQ(ierr);
101   ierr = VecGetLocalSize(coords,&n);CHKERRQ(ierr);
102   ierr = VecGetSize(coords,&N);CHKERRQ(ierr);
103   n   /= dim;
104   N   /= dim;
105   sN = 1./PetscSqrtReal((PetscReal)N);
106   switch (dim) {
107   case 1:
108     ierr = MatNullSpaceCreate(PetscObjectComm((PetscObject)coords),PETSC_TRUE,0,NULL,sp);CHKERRQ(ierr);
109     break;
110   case 2:
111   case 3:
112     nmodes = (dim == 2) ? 3 : 6;
113     ierr   = VecCreate(PetscObjectComm((PetscObject)coords),&vec[0]);CHKERRQ(ierr);
114     ierr   = VecSetSizes(vec[0],dim*n,dim*N);CHKERRQ(ierr);
115     ierr   = VecSetBlockSize(vec[0],dim);CHKERRQ(ierr);
116     ierr   = VecSetUp(vec[0]);CHKERRQ(ierr);
117     for (i=1; i<nmodes; i++) {ierr = VecDuplicate(vec[0],&vec[i]);CHKERRQ(ierr);}
118     for (i=0; i<nmodes; i++) {ierr = VecGetArray(vec[i],&v[i]);CHKERRQ(ierr);}
119     ierr = VecGetArrayRead(coords,&x);CHKERRQ(ierr);
120     for (i=0; i<n; i++) {
121       if (dim == 2) {
122         v[0][i*2+0] = sN;
123         v[0][i*2+1] = 0.;
124         v[1][i*2+0] = 0.;
125         v[1][i*2+1] = sN;
126         /* Rotations */
127         v[2][i*2+0] = -x[i*2+1];
128         v[2][i*2+1] = x[i*2+0];
129       } else {
130         v[0][i*3+0] = sN;
131         v[0][i*3+1] = 0.;
132         v[0][i*3+2] = 0.;
133         v[1][i*3+0] = 0.;
134         v[1][i*3+1] = sN;
135         v[1][i*3+2] = 0.;
136         v[2][i*3+0] = 0.;
137         v[2][i*3+1] = 0.;
138         v[2][i*3+2] = sN;
139 
140         v[3][i*3+0] = x[i*3+1];
141         v[3][i*3+1] = -x[i*3+0];
142         v[3][i*3+2] = 0.;
143         v[4][i*3+0] = 0.;
144         v[4][i*3+1] = -x[i*3+2];
145         v[4][i*3+2] = x[i*3+1];
146         v[5][i*3+0] = x[i*3+2];
147         v[5][i*3+1] = 0.;
148         v[5][i*3+2] = -x[i*3+0];
149       }
150     }
151     for (i=0; i<nmodes; i++) {ierr = VecRestoreArray(vec[i],&v[i]);CHKERRQ(ierr);}
152     ierr = VecRestoreArrayRead(coords,&x);CHKERRQ(ierr);
153     for (i=dim; i<nmodes; i++) {
154       /* Orthonormalize vec[i] against vec[0:i-1] */
155       ierr = VecMDot(vec[i],i,vec,dots);CHKERRQ(ierr);
156       for (j=0; j<i; j++) dots[j] *= -1.;
157       ierr = VecMAXPY(vec[i],i,dots,vec);CHKERRQ(ierr);
158       ierr = VecNormalize(vec[i],NULL);CHKERRQ(ierr);
159     }
160     ierr = MatNullSpaceCreate(PetscObjectComm((PetscObject)coords),PETSC_FALSE,nmodes,vec,sp);CHKERRQ(ierr);
161     for (i=0; i<nmodes; i++) {ierr = VecDestroy(&vec[i]);CHKERRQ(ierr);}
162   }
163   PetscFunctionReturn(0);
164 }
165 
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 /*@C
215    MatNullSpaceCreate - Creates a data structure used to project vectors
216    out of null spaces.
217 
218    Collective on MPI_Comm
219 
220    Input Parameters:
221 +  comm - the MPI communicator associated with the object
222 .  has_cnst - PETSC_TRUE if the null space contains the constant vector; otherwise PETSC_FALSE
223 .  n - number of vectors (excluding constant vector) in null space
224 -  vecs - the vectors that span the null space (excluding the constant vector);
225           these vectors must be orthonormal. These vectors are NOT copied, so do not change them
226           after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count
227           for them by one).
228 
229    Output Parameter:
230 .  SP - the null space context
231 
232    Level: advanced
233 
234    Notes:
235     See MatNullSpaceSetFunction() as an alternative way of providing the null space information instead of setting vecs.
236 
237       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
238        need to pass in a function that eliminates the constant function into MatNullSpaceSetFunction().
239 
240   Users manual sections:
241 .   sec_singular
242 
243 .keywords: PC, null space, create
244 
245 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceSetFunction()
246 @*/
247 PetscErrorCode  MatNullSpaceCreate(MPI_Comm comm,PetscBool has_cnst,PetscInt n,const Vec vecs[],MatNullSpace *SP)
248 {
249   MatNullSpace   sp;
250   PetscErrorCode ierr;
251   PetscInt       i;
252 
253   PetscFunctionBegin;
254   if (n < 0) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of vectors (given %D) cannot be negative",n);
255   if (n) PetscValidPointer(vecs,4);
256   for (i=0; i<n; i++) PetscValidHeaderSpecific(vecs[i],VEC_CLASSID,4);
257   PetscValidPointer(SP,5);
258   if (n) {
259     for (i=0; i<n; i++) {
260       /* prevent the user from changes values in the vector */
261       ierr = VecLockPush(vecs[i]);CHKERRQ(ierr);
262     }
263   }
264 #if defined(PETSC_USE_DEBUG)
265   if (n) {
266     PetscScalar *dots;
267     for (i=0; i<n; i++) {
268       PetscReal norm;
269       ierr = VecNorm(vecs[i],NORM_2,&norm);CHKERRQ(ierr);
270       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);
271     }
272     if (has_cnst) {
273       for (i=0; i<n; i++) {
274         PetscScalar sum;
275         ierr = VecSum(vecs[i],&sum);CHKERRQ(ierr);
276         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));
277       }
278     }
279     ierr = PetscMalloc1(n-1,&dots);CHKERRQ(ierr);
280     for (i=0; i<n-1; i++) {
281       PetscInt j;
282       ierr = VecMDot(vecs[i],n-i-1,vecs+i+1,dots);CHKERRQ(ierr);
283       for (j=0;j<n-i-1;j++) {
284         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]));
285       }
286     }
287     PetscFree(dots);CHKERRQ(ierr);
288   }
289 #endif
290 
291   *SP = NULL;
292   ierr = MatInitializePackage();CHKERRQ(ierr);
293 
294   ierr = PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr);
295 
296   sp->has_cnst = has_cnst;
297   sp->n        = n;
298   sp->vecs     = 0;
299   sp->alpha    = 0;
300   sp->remove   = 0;
301   sp->rmctx    = 0;
302 
303   if (n) {
304     ierr = PetscMalloc1(n,&sp->vecs);CHKERRQ(ierr);
305     ierr = PetscMalloc1(n,&sp->alpha);CHKERRQ(ierr);
306     ierr = PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr);
307     for (i=0; i<n; i++) {
308       ierr        = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr);
309       sp->vecs[i] = vecs[i];
310     }
311   }
312 
313   *SP = sp;
314   PetscFunctionReturn(0);
315 }
316 
317 /*@
318    MatNullSpaceDestroy - Destroys a data structure used to project vectors
319    out of null spaces.
320 
321    Collective on MatNullSpace
322 
323    Input Parameter:
324 .  sp - the null space context to be destroyed
325 
326    Level: advanced
327 
328 .keywords: PC, null space, destroy
329 
330 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction()
331 @*/
332 PetscErrorCode  MatNullSpaceDestroy(MatNullSpace *sp)
333 {
334   PetscErrorCode ierr;
335   PetscInt       i;
336 
337   PetscFunctionBegin;
338   if (!*sp) PetscFunctionReturn(0);
339   PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1);
340   if (--((PetscObject)(*sp))->refct > 0) {*sp = 0; PetscFunctionReturn(0);}
341 
342   for (i=0; i < (*sp)->n; i++) {
343     ierr = VecLockPop((*sp)->vecs[i]);CHKERRQ(ierr);
344   }
345 
346   ierr = VecDestroyVecs((*sp)->n,&(*sp)->vecs);CHKERRQ(ierr);
347   ierr = PetscFree((*sp)->alpha);CHKERRQ(ierr);
348   ierr = PetscHeaderDestroy(sp);CHKERRQ(ierr);
349   PetscFunctionReturn(0);
350 }
351 
352 /*@C
353    MatNullSpaceRemove - Removes all the components of a null space from a vector.
354 
355    Collective on MatNullSpace
356 
357    Input Parameters:
358 +  sp - the null space context (if this is NULL then no null space is removed)
359 -  vec - the vector from which the null space is to be removed
360 
361    Level: advanced
362 
363 .keywords: PC, null space, remove
364 
365 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
366 @*/
367 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
368 {
369   PetscScalar    sum;
370   PetscInt       i,N;
371   PetscErrorCode ierr;
372 
373   PetscFunctionBegin;
374   if (!sp) PetscFunctionReturn(0);
375   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
376   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
377 
378   if (sp->has_cnst) {
379     ierr = VecGetSize(vec,&N);CHKERRQ(ierr);
380     if (N > 0) {
381       ierr = VecSum(vec,&sum);CHKERRQ(ierr);
382       sum  = sum/((PetscScalar)(-1.0*N));
383       ierr = VecShift(vec,sum);CHKERRQ(ierr);
384     }
385   }
386 
387   if (sp->n) {
388     ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr);
389     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
390     ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr);
391   }
392 
393   if (sp->remove) {
394     ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr);
395   }
396   PetscFunctionReturn(0);
397 }
398 
399 /*@
400    MatNullSpaceTest  - Tests if the claimed null space is really a
401      null space of a matrix
402 
403    Collective on MatNullSpace
404 
405    Input Parameters:
406 +  sp - the null space context
407 -  mat - the matrix
408 
409    Output Parameters:
410 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
411 
412    Level: advanced
413 
414 .keywords: PC, null space, remove
415 
416 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
417 @*/
418 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
419 {
420   PetscScalar    sum;
421   PetscReal      nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON;
422   PetscInt       j,n,N;
423   PetscErrorCode ierr;
424   Vec            l,r;
425   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
426   PetscViewer    viewer;
427 
428   PetscFunctionBegin;
429   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
430   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
431   n    = sp->n;
432   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,NULL,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr);
433   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,NULL,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr);
434 
435   if (n) {
436     ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr);
437   } else {
438     ierr = MatCreateVecs(mat,&l,NULL);CHKERRQ(ierr);
439   }
440 
441   ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
442   if (sp->has_cnst) {
443     ierr = VecDuplicate(l,&r);CHKERRQ(ierr);
444     ierr = VecGetSize(l,&N);CHKERRQ(ierr);
445     sum  = 1.0/N;
446     ierr = VecSet(l,sum);CHKERRQ(ierr);
447     ierr = MatMult(mat,l,r);CHKERRQ(ierr);
448     ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr);
449     if (nrm >= tol) consistent = PETSC_FALSE;
450     if (flg1) {
451       if (consistent) {
452         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr);
453       } else {
454         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr);
455       }
456       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm);CHKERRQ(ierr);
457     }
458     if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
459     if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
460     ierr = VecDestroy(&r);CHKERRQ(ierr);
461   }
462 
463   for (j=0; j<n; j++) {
464     ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr);
465     ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr);
466     if (nrm >= tol) consistent = PETSC_FALSE;
467     if (flg1) {
468       if (consistent) {
469         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr);
470       } else {
471         ierr       = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr);
472         consistent = PETSC_FALSE;
473       }
474       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %g\n",j,(double)nrm);CHKERRQ(ierr);
475     }
476     if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
477     if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
478   }
479 
480   if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
481   ierr = VecDestroy(&l);CHKERRQ(ierr);
482   if (isNull) *isNull = consistent;
483   PetscFunctionReturn(0);
484 }
485 
486