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