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