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