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