xref: /petsc/src/mat/interface/matnull.c (revision 22a7b760d1bac7490ac2e75bf367dc393fb2e18c)
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[3];
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:dim] */
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 #if !defined(PETSC_USE_DYNAMIC_LIBRARIES)
255   ierr = MatInitializePackage();CHKERRQ(ierr);
256 #endif
257 
258   ierr = PetscHeaderCreate(sp,_p_MatNullSpace,int,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr);
259 
260   sp->has_cnst = has_cnst;
261   sp->n        = n;
262   sp->vecs     = 0;
263   sp->alpha    = 0;
264   sp->remove   = 0;
265   sp->rmctx    = 0;
266 
267   if (n) {
268     ierr = PetscMalloc(n*sizeof(Vec),&sp->vecs);CHKERRQ(ierr);
269     ierr = PetscMalloc(n*sizeof(PetscScalar),&sp->alpha);CHKERRQ(ierr);
270     ierr = PetscLogObjectMemory(sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr);
271     for (i=0; i<n; i++) {
272       ierr        = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr);
273       sp->vecs[i] = vecs[i];
274     }
275   }
276 
277   *SP = sp;
278   PetscFunctionReturn(0);
279 }
280 
281 #undef __FUNCT__
282 #define __FUNCT__ "MatNullSpaceDestroy"
283 /*@
284    MatNullSpaceDestroy - Destroys a data structure used to project vectors
285    out of null spaces.
286 
287    Collective on MatNullSpace
288 
289    Input Parameter:
290 .  sp - the null space context to be destroyed
291 
292    Level: advanced
293 
294 .keywords: PC, null space, destroy
295 
296 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction()
297 @*/
298 PetscErrorCode  MatNullSpaceDestroy(MatNullSpace *sp)
299 {
300   PetscErrorCode ierr;
301 
302   PetscFunctionBegin;
303   if (!*sp) PetscFunctionReturn(0);
304   PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1);
305   if (--((PetscObject)(*sp))->refct > 0) {*sp = 0; PetscFunctionReturn(0);}
306 
307   ierr = VecDestroyVecs((*sp)->n,&(*sp)->vecs);CHKERRQ(ierr);
308   ierr = PetscFree((*sp)->alpha);CHKERRQ(ierr);
309   ierr = PetscHeaderDestroy(sp);CHKERRQ(ierr);
310   PetscFunctionReturn(0);
311 }
312 
313 #undef __FUNCT__
314 #define __FUNCT__ "MatNullSpaceRemove"
315 /*@C
316    MatNullSpaceRemove - Removes all the components of a null space from a vector.
317 
318    Collective on MatNullSpace
319 
320    Input Parameters:
321 +  sp - the null space context
322 .  vec - the vector from which the null space is to be removed
323 -  out - if this is requested (not NULL) then this is a vector with the null space removed otherwise
324          the removal is done in-place (in vec)
325 
326    Note: The user is not responsible for the vector returned and should not destroy it.
327 
328    Level: advanced
329 
330 .keywords: PC, null space, remove
331 
332 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
333 @*/
334 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
335 {
336   PetscScalar    sum;
337   PetscInt       i,N;
338   PetscErrorCode ierr;
339 
340   PetscFunctionBegin;
341   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
342   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
343 
344   if (sp->has_cnst) {
345     ierr = VecGetSize(vec,&N);CHKERRQ(ierr);
346     if (N > 0) {
347       ierr = VecSum(vec,&sum);CHKERRQ(ierr);
348       sum  = sum/((PetscScalar)(-1.0*N));
349       ierr = VecShift(vec,sum);CHKERRQ(ierr);
350     }
351   }
352 
353   if (sp->n) {
354     ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr);
355     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
356     ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr);
357   }
358 
359   if (sp->remove) {
360     ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr);
361   }
362   PetscFunctionReturn(0);
363 }
364 
365 #undef __FUNCT__
366 #define __FUNCT__ "MatNullSpaceTest"
367 /*@
368    MatNullSpaceTest  - Tests if the claimed null space is really a
369      null space of a matrix
370 
371    Collective on MatNullSpace
372 
373    Input Parameters:
374 +  sp - the null space context
375 -  mat - the matrix
376 
377    Output Parameters:
378 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
379 
380    Level: advanced
381 
382 .keywords: PC, null space, remove
383 
384 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
385 @*/
386 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
387 {
388   PetscScalar    sum;
389   PetscReal      nrm;
390   PetscInt       j,n,N;
391   PetscErrorCode ierr;
392   Vec            l,r;
393   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
394   PetscViewer    viewer;
395 
396   PetscFunctionBegin;
397   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
398   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
399   n    = sp->n;
400   ierr = PetscOptionsGetBool(NULL,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr);
401   ierr = PetscOptionsGetBool(NULL,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr);
402 
403   if (n) {
404     ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr);
405   } else {
406     ierr = MatGetVecs(mat,&l,NULL);CHKERRQ(ierr);
407   }
408 
409   ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
410   if (sp->has_cnst) {
411     ierr = VecDuplicate(l,&r);CHKERRQ(ierr);
412     ierr = VecGetSize(l,&N);CHKERRQ(ierr);
413     sum  = 1.0/N;
414     ierr = VecSet(l,sum);CHKERRQ(ierr);
415     ierr = MatMult(mat,l,r);CHKERRQ(ierr);
416     ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr);
417     if (nrm >= 1.e-7) consistent = PETSC_FALSE;
418     if (flg1) {
419       if (consistent) {
420         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr);
421       } else {
422         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr);
423       }
424       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %G\n",nrm);CHKERRQ(ierr);
425     }
426     if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
427     if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
428     ierr = VecDestroy(&r);CHKERRQ(ierr);
429   }
430 
431   for (j=0; j<n; j++) {
432     ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr);
433     ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr);
434     if (nrm >= 1.e-7) consistent = PETSC_FALSE;
435     if (flg1) {
436       if (consistent) {
437         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr);
438       } else {
439         ierr       = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr);
440         consistent = PETSC_FALSE;
441       }
442       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %G\n",j,nrm);CHKERRQ(ierr);
443     }
444     if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
445     if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
446   }
447 
448   if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
449   ierr = VecDestroy(&l);CHKERRQ(ierr);
450   if (isNull) *isNull = consistent;
451   PetscFunctionReturn(0);
452 }
453 
454