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