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