xref: /petsc/src/mat/interface/matnull.c (revision b00a91154f763f12aa55f3d53a3f2776f15f49e3)
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   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 -  out - if this is requested (not NULL) then this is a vector with the null space removed otherwise
322          the removal is done in-place (in vec)
323 
324    Note: The user is not responsible for the vector returned and should not destroy it.
325 
326    Level: advanced
327 
328 .keywords: PC, null space, remove
329 
330 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
331 @*/
332 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
333 {
334   PetscScalar    sum;
335   PetscInt       i,N;
336   PetscErrorCode ierr;
337 
338   PetscFunctionBegin;
339   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
340   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
341 
342   if (sp->has_cnst) {
343     ierr = VecGetSize(vec,&N);CHKERRQ(ierr);
344     if (N > 0) {
345       ierr = VecSum(vec,&sum);CHKERRQ(ierr);
346       sum  = sum/((PetscScalar)(-1.0*N));
347       ierr = VecShift(vec,sum);CHKERRQ(ierr);
348     }
349   }
350 
351   if (sp->n) {
352     ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr);
353     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
354     ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr);
355   }
356 
357   if (sp->remove) {
358     ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr);
359   }
360   PetscFunctionReturn(0);
361 }
362 
363 #undef __FUNCT__
364 #define __FUNCT__ "MatNullSpaceTest"
365 /*@
366    MatNullSpaceTest  - Tests if the claimed null space is really a
367      null space of a matrix
368 
369    Collective on MatNullSpace
370 
371    Input Parameters:
372 +  sp - the null space context
373 -  mat - the matrix
374 
375    Output Parameters:
376 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
377 
378    Level: advanced
379 
380 .keywords: PC, null space, remove
381 
382 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
383 @*/
384 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
385 {
386   PetscScalar    sum;
387   PetscReal      nrm;
388   PetscInt       j,n,N;
389   PetscErrorCode ierr;
390   Vec            l,r;
391   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
392   PetscViewer    viewer;
393 
394   PetscFunctionBegin;
395   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
396   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
397   n    = sp->n;
398   ierr = PetscOptionsGetBool(NULL,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr);
399   ierr = PetscOptionsGetBool(NULL,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr);
400 
401   if (n) {
402     ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr);
403   } else {
404     ierr = MatGetVecs(mat,&l,NULL);CHKERRQ(ierr);
405   }
406 
407   ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
408   if (sp->has_cnst) {
409     ierr = VecDuplicate(l,&r);CHKERRQ(ierr);
410     ierr = VecGetSize(l,&N);CHKERRQ(ierr);
411     sum  = 1.0/N;
412     ierr = VecSet(l,sum);CHKERRQ(ierr);
413     ierr = MatMult(mat,l,r);CHKERRQ(ierr);
414     ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr);
415     if (nrm >= 1.e-7) consistent = PETSC_FALSE;
416     if (flg1) {
417       if (consistent) {
418         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr);
419       } else {
420         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr);
421       }
422       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %G\n",nrm);CHKERRQ(ierr);
423     }
424     if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
425     if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
426     ierr = VecDestroy(&r);CHKERRQ(ierr);
427   }
428 
429   for (j=0; j<n; j++) {
430     ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr);
431     ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr);
432     if (nrm >= 1.e-7) consistent = PETSC_FALSE;
433     if (flg1) {
434       if (consistent) {
435         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr);
436       } else {
437         ierr       = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr);
438         consistent = PETSC_FALSE;
439       }
440       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %G\n",j,nrm);CHKERRQ(ierr);
441     }
442     if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
443     if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
444   }
445 
446   if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
447   ierr = VecDestroy(&l);CHKERRQ(ierr);
448   if (isNull) *isNull = consistent;
449   PetscFunctionReturn(0);
450 }
451 
452