xref: /petsc/src/mat/interface/matnull.c (revision ba1e01c44f2f740c97c9026fe63e360558b7709b)
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) {
190     ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
191   }
192   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
193   PetscCheckSameComm(sp,1,viewer,2);
194 
195   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
196   if (iascii) {
197     PetscViewerFormat format;
198     PetscInt          i;
199     ierr = PetscViewerGetFormat(viewer,&format);CHKERRQ(ierr);
200     ierr = PetscObjectPrintClassNamePrefixType((PetscObject)sp,viewer);CHKERRQ(ierr);
201     ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
202     ierr = PetscViewerASCIIPrintf(viewer,"Contains %D vector%s%s\n",sp->n,sp->n==1 ? "" : "s",sp->has_cnst ? " and the constant" : "");CHKERRQ(ierr);
203     if (sp->remove) {ierr = PetscViewerASCIIPrintf(viewer,"Has user-provided removal function\n");CHKERRQ(ierr);}
204     if (!(format == PETSC_VIEWER_ASCII_INFO || format == PETSC_VIEWER_ASCII_INFO_DETAIL)) {
205       for (i=0; i<sp->n; i++) {
206         ierr = VecView(sp->vecs[i],viewer);CHKERRQ(ierr);
207       }
208     }
209     ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
210   }
211   PetscFunctionReturn(0);
212 }
213 
214 #undef __FUNCT__
215 #define __FUNCT__ "MatNullSpaceCreate"
216 /*@
217    MatNullSpaceCreate - Creates a data structure used to project vectors
218    out of null spaces.
219 
220    Collective on MPI_Comm
221 
222    Input Parameters:
223 +  comm - the MPI communicator associated with the object
224 .  has_cnst - PETSC_TRUE if the null space contains the constant vector; otherwise PETSC_FALSE
225 .  n - number of vectors (excluding constant vector) in null space
226 -  vecs - the vectors that span the null space (excluding the constant vector);
227           these vectors must be orthonormal. These vectors are NOT copied, so do not change them
228           after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count
229           for them by one).
230 
231    Output Parameter:
232 .  SP - the null space context
233 
234    Level: advanced
235 
236    Notes: See MatNullSpaceSetFunction() as an alternative way of providing the null space information instead of setting vecs.
237 
238       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
239        need to pass in a function that eliminates the constant function into MatNullSpaceSetFunction().
240 
241   Users manual sections:
242 .   sec_singular
243 
244 .keywords: PC, null space, create
245 
246 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceSetFunction()
247 @*/
248 PetscErrorCode  MatNullSpaceCreate(MPI_Comm comm,PetscBool has_cnst,PetscInt n,const Vec vecs[],MatNullSpace *SP)
249 {
250   MatNullSpace   sp;
251   PetscErrorCode ierr;
252   PetscInt       i;
253 
254   PetscFunctionBegin;
255   if (n < 0) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of vectors (given %D) cannot be negative",n);
256   if (n) PetscValidPointer(vecs,4);
257   for (i=0; i<n; i++) PetscValidHeaderSpecific(vecs[i],VEC_CLASSID,4);
258   PetscValidPointer(SP,5);
259   if (n) {
260     for (i=0; i<n; i++) {
261       /* prevent the user from changes values in the vector */
262       ierr = VecLockPush(vecs[i]);CHKERRQ(ierr);
263     }
264   }
265 #if defined(PETSC_USE_DEBUG)
266   if (n) {
267     PetscScalar *dots;
268     for (i=0; i<n; i++) {
269       PetscReal norm;
270       ierr = VecNorm(vecs[i],NORM_2,&norm);CHKERRQ(ierr);
271       if (PetscAbsReal(norm - 1.0) > PETSC_SQRT_MACHINE_EPSILON) SETERRQ2(PetscObjectComm((PetscObject)vecs[i]),PETSC_ERR_ARG_WRONG,"Vector %D must have 2-norm of 1.0, it is %g",i,(double)norm);
272     }
273     if (has_cnst) {
274       for (i=0; i<n; i++) {
275         PetscScalar sum;
276         ierr = VecSum(vecs[i],&sum);CHKERRQ(ierr);
277         if (PetscAbsScalar(sum) > PETSC_SQRT_MACHINE_EPSILON) SETERRQ2(PetscObjectComm((PetscObject)vecs[i]),PETSC_ERR_ARG_WRONG,"Vector %D must be orthogonal to constant vector, inner product is %g",i,(double)PetscAbsScalar(sum));
278       }
279     }
280     ierr = PetscMalloc1(n-1,&dots);CHKERRQ(ierr);
281     for (i=0; i<n-1; i++) {
282       PetscInt j;
283       ierr = VecMDot(vecs[i],n-i-1,vecs+i+1,dots);CHKERRQ(ierr);
284       for (j=0;j<n-i-1;j++) {
285         if (PetscAbsScalar(dots[j]) > PETSC_SQRT_MACHINE_EPSILON) SETERRQ3(PetscObjectComm((PetscObject)vecs[i]),PETSC_ERR_ARG_WRONG,"Vector %D must be orthogonal to vector %D, inner product is %g",i,i+j+1,(double)PetscAbsScalar(dots[j]));
286       }
287     }
288     PetscFree(dots);CHKERRQ(ierr);
289   }
290 #endif
291 
292   *SP = NULL;
293   ierr = MatInitializePackage();CHKERRQ(ierr);
294 
295   ierr = PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr);
296 
297   sp->has_cnst = has_cnst;
298   sp->n        = n;
299   sp->vecs     = 0;
300   sp->alpha    = 0;
301   sp->remove   = 0;
302   sp->rmctx    = 0;
303 
304   if (n) {
305     ierr = PetscMalloc1(n,&sp->vecs);CHKERRQ(ierr);
306     ierr = PetscMalloc1(n,&sp->alpha);CHKERRQ(ierr);
307     ierr = PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr);
308     for (i=0; i<n; i++) {
309       ierr        = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr);
310       sp->vecs[i] = vecs[i];
311     }
312   }
313 
314   *SP = sp;
315   PetscFunctionReturn(0);
316 }
317 
318 #undef __FUNCT__
319 #define __FUNCT__ "MatNullSpaceDestroy"
320 /*@
321    MatNullSpaceDestroy - Destroys a data structure used to project vectors
322    out of null spaces.
323 
324    Collective on MatNullSpace
325 
326    Input Parameter:
327 .  sp - the null space context to be destroyed
328 
329    Level: advanced
330 
331 .keywords: PC, null space, destroy
332 
333 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction()
334 @*/
335 PetscErrorCode  MatNullSpaceDestroy(MatNullSpace *sp)
336 {
337   PetscErrorCode ierr;
338   PetscInt       i;
339 
340   PetscFunctionBegin;
341   if (!*sp) PetscFunctionReturn(0);
342   PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1);
343   if (--((PetscObject)(*sp))->refct > 0) {*sp = 0; PetscFunctionReturn(0);}
344 
345   for (i=0; i < (*sp)->n; i++) {
346     ierr = VecLockPop((*sp)->vecs[i]);CHKERRQ(ierr);
347   }
348 
349   ierr = VecDestroyVecs((*sp)->n,&(*sp)->vecs);CHKERRQ(ierr);
350   ierr = PetscFree((*sp)->alpha);CHKERRQ(ierr);
351   ierr = PetscHeaderDestroy(sp);CHKERRQ(ierr);
352   PetscFunctionReturn(0);
353 }
354 
355 #undef __FUNCT__
356 #define __FUNCT__ "MatNullSpaceRemove"
357 /*@C
358    MatNullSpaceRemove - Removes all the components of a null space from a vector.
359 
360    Collective on MatNullSpace
361 
362    Input Parameters:
363 +  sp - the null space context
364 -  vec - the vector from which the null space is to be removed
365 
366    Level: advanced
367 
368 .keywords: PC, null space, remove
369 
370 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
371 @*/
372 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
373 {
374   PetscScalar    sum;
375   PetscInt       i,N;
376   PetscErrorCode ierr;
377 
378   PetscFunctionBegin;
379   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
380   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
381 
382   if (sp->has_cnst) {
383     ierr = VecGetSize(vec,&N);CHKERRQ(ierr);
384     if (N > 0) {
385       ierr = VecSum(vec,&sum);CHKERRQ(ierr);
386       sum  = sum/((PetscScalar)(-1.0*N));
387       ierr = VecShift(vec,sum);CHKERRQ(ierr);
388     }
389   }
390 
391   if (sp->n) {
392     ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr);
393     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
394     ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr);
395   }
396 
397   if (sp->remove) {
398     ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr);
399   }
400   PetscFunctionReturn(0);
401 }
402 
403 #undef __FUNCT__
404 #define __FUNCT__ "MatNullSpaceTest"
405 /*@
406    MatNullSpaceTest  - Tests if the claimed null space is really a
407      null space of a matrix
408 
409    Collective on MatNullSpace
410 
411    Input Parameters:
412 +  sp - the null space context
413 -  mat - the matrix
414 
415    Output Parameters:
416 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
417 
418    Level: advanced
419 
420 .keywords: PC, null space, remove
421 
422 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
423 @*/
424 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
425 {
426   PetscScalar    sum;
427   PetscReal      nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON;
428   PetscInt       j,n,N;
429   PetscErrorCode ierr;
430   Vec            l,r;
431   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
432   PetscViewer    viewer;
433 
434   PetscFunctionBegin;
435   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
436   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
437   n    = sp->n;
438   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,NULL,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr);
439   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,NULL,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr);
440 
441   if (n) {
442     ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr);
443   } else {
444     ierr = MatCreateVecs(mat,&l,NULL);CHKERRQ(ierr);
445   }
446 
447   ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
448   if (sp->has_cnst) {
449     ierr = VecDuplicate(l,&r);CHKERRQ(ierr);
450     ierr = VecGetSize(l,&N);CHKERRQ(ierr);
451     sum  = 1.0/N;
452     ierr = VecSet(l,sum);CHKERRQ(ierr);
453     ierr = MatMult(mat,l,r);CHKERRQ(ierr);
454     ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr);
455     if (nrm >= tol) consistent = PETSC_FALSE;
456     if (flg1) {
457       if (consistent) {
458         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr);
459       } else {
460         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr);
461       }
462       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm);CHKERRQ(ierr);
463     }
464     if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
465     if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
466     ierr = VecDestroy(&r);CHKERRQ(ierr);
467   }
468 
469   for (j=0; j<n; j++) {
470     ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr);
471     ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr);
472     if (nrm >= tol) consistent = PETSC_FALSE;
473     if (flg1) {
474       if (consistent) {
475         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr);
476       } else {
477         ierr       = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr);
478         consistent = PETSC_FALSE;
479       }
480       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %g\n",j,(double)nrm);CHKERRQ(ierr);
481     }
482     if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
483     if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
484   }
485 
486   if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
487   ierr = VecDestroy(&l);CHKERRQ(ierr);
488   if (isNull) *isNull = consistent;
489   PetscFunctionReturn(0);
490 }
491 
492