xref: /petsc/src/mat/interface/matnull.c (revision 28b400f66ebc7ae0049166a2294dfcd3df27e64b)
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 /*@C
11    MatNullSpaceSetFunction - set a function that removes a null space from a vector
12    out of null spaces.
13 
14    Logically Collective on MatNullSpace
15 
16    Input Parameters:
17 +  sp - the null space object
18 .  rem - the function that removes the null space
19 -  ctx - context for the remove function
20 
21    Level: advanced
22 
23 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceCreate()
24 @*/
25 PetscErrorCode  MatNullSpaceSetFunction(MatNullSpace sp, PetscErrorCode (*rem)(MatNullSpace,Vec,void*),void *ctx)
26 {
27   PetscFunctionBegin;
28   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
29   sp->remove = rem;
30   sp->rmctx  = ctx;
31   PetscFunctionReturn(0);
32 }
33 
34 /*@C
35    MatNullSpaceGetVecs - get vectors defining the null space
36 
37    Not Collective
38 
39    Input Parameter:
40 .  sp - null space object
41 
42    Output Parameters:
43 +  has_cnst - PETSC_TRUE if the null space contains the constant vector, otherwise PETSC_FALSE
44 .  n - number of vectors (excluding constant vector) in null space
45 -  vecs - orthonormal vectors that span the null space (excluding the constant vector)
46 
47    Level: developer
48 
49    Notes:
50       These vectors and the array are owned by the MatNullSpace and should not be destroyed or freeded by the caller
51 
52 .seealso: MatNullSpaceCreate(), MatGetNullSpace(), MatGetNearNullSpace()
53 @*/
54 PetscErrorCode MatNullSpaceGetVecs(MatNullSpace sp,PetscBool *has_const,PetscInt *n,const Vec **vecs)
55 {
56   PetscFunctionBegin;
57   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
58   if (has_const) *has_const = sp->has_cnst;
59   if (n) *n = sp->n;
60   if (vecs) *vecs = sp->vecs;
61   PetscFunctionReturn(0);
62 }
63 
64 /*@
65    MatNullSpaceCreateRigidBody - create rigid body modes from coordinates
66 
67    Collective on Vec
68 
69    Input Parameter:
70 .  coords - block of coordinates of each node, must have block size set
71 
72    Output Parameter:
73 .  sp - the null space
74 
75    Level: advanced
76 
77    Notes:
78      If you are solving an elasticity problem you should likely use this, in conjunction with MatSetNearNullspace(), to provide information that
79      the PCGAMG preconditioner can use to construct a much more efficient preconditioner.
80 
81      If you are solving an elasticity problem with pure Neumann boundary conditions you can use this in conjunction with MatSetNullspace() to
82      provide this information to the linear solver so it can handle the null space appropriately in the linear solution.
83 
84 .seealso: MatNullSpaceCreate(), MatSetNearNullspace(), MatSetNullspace()
85 @*/
86 PetscErrorCode MatNullSpaceCreateRigidBody(Vec coords,MatNullSpace *sp)
87 {
88   const PetscScalar *x;
89   PetscScalar       *v[6],dots[5];
90   Vec               vec[6];
91   PetscInt          n,N,dim,nmodes,i,j;
92   PetscReal         sN;
93 
94   PetscFunctionBegin;
95   CHKERRQ(VecGetBlockSize(coords,&dim));
96   CHKERRQ(VecGetLocalSize(coords,&n));
97   CHKERRQ(VecGetSize(coords,&N));
98   n   /= dim;
99   N   /= dim;
100   sN = 1./PetscSqrtReal((PetscReal)N);
101   switch (dim) {
102   case 1:
103     CHKERRQ(MatNullSpaceCreate(PetscObjectComm((PetscObject)coords),PETSC_TRUE,0,NULL,sp));
104     break;
105   case 2:
106   case 3:
107     nmodes = (dim == 2) ? 3 : 6;
108     CHKERRQ(VecCreate(PetscObjectComm((PetscObject)coords),&vec[0]));
109     CHKERRQ(VecSetSizes(vec[0],dim*n,dim*N));
110     CHKERRQ(VecSetBlockSize(vec[0],dim));
111     CHKERRQ(VecSetUp(vec[0]));
112     for (i=1; i<nmodes; i++) CHKERRQ(VecDuplicate(vec[0],&vec[i]));
113     for (i=0; i<nmodes; i++) CHKERRQ(VecGetArray(vec[i],&v[i]));
114     CHKERRQ(VecGetArrayRead(coords,&x));
115     for (i=0; i<n; i++) {
116       if (dim == 2) {
117         v[0][i*2+0] = sN;
118         v[0][i*2+1] = 0.;
119         v[1][i*2+0] = 0.;
120         v[1][i*2+1] = sN;
121         /* Rotations */
122         v[2][i*2+0] = -x[i*2+1];
123         v[2][i*2+1] = x[i*2+0];
124       } else {
125         v[0][i*3+0] = sN;
126         v[0][i*3+1] = 0.;
127         v[0][i*3+2] = 0.;
128         v[1][i*3+0] = 0.;
129         v[1][i*3+1] = sN;
130         v[1][i*3+2] = 0.;
131         v[2][i*3+0] = 0.;
132         v[2][i*3+1] = 0.;
133         v[2][i*3+2] = sN;
134 
135         v[3][i*3+0] = x[i*3+1];
136         v[3][i*3+1] = -x[i*3+0];
137         v[3][i*3+2] = 0.;
138         v[4][i*3+0] = 0.;
139         v[4][i*3+1] = -x[i*3+2];
140         v[4][i*3+2] = x[i*3+1];
141         v[5][i*3+0] = x[i*3+2];
142         v[5][i*3+1] = 0.;
143         v[5][i*3+2] = -x[i*3+0];
144       }
145     }
146     for (i=0; i<nmodes; i++) CHKERRQ(VecRestoreArray(vec[i],&v[i]));
147     CHKERRQ(VecRestoreArrayRead(coords,&x));
148     for (i=dim; i<nmodes; i++) {
149       /* Orthonormalize vec[i] against vec[0:i-1] */
150       CHKERRQ(VecMDot(vec[i],i,vec,dots));
151       for (j=0; j<i; j++) dots[j] *= -1.;
152       CHKERRQ(VecMAXPY(vec[i],i,dots,vec));
153       CHKERRQ(VecNormalize(vec[i],NULL));
154     }
155     CHKERRQ(MatNullSpaceCreate(PetscObjectComm((PetscObject)coords),PETSC_FALSE,nmodes,vec,sp));
156     for (i=0; i<nmodes; i++) CHKERRQ(VecDestroy(&vec[i]));
157   }
158   PetscFunctionReturn(0);
159 }
160 
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   PetscBool      iascii;
180 
181   PetscFunctionBegin;
182   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
183   if (!viewer) {
184     CHKERRQ(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer));
185   }
186   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
187   PetscCheckSameComm(sp,1,viewer,2);
188 
189   CHKERRQ(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii));
190   if (iascii) {
191     PetscViewerFormat format;
192     PetscInt          i;
193     CHKERRQ(PetscViewerGetFormat(viewer,&format));
194     CHKERRQ(PetscObjectPrintClassNamePrefixType((PetscObject)sp,viewer));
195     CHKERRQ(PetscViewerASCIIPushTab(viewer));
196     CHKERRQ(PetscViewerASCIIPrintf(viewer,"Contains %" PetscInt_FMT " vector%s%s\n",sp->n,sp->n==1 ? "" : "s",sp->has_cnst ? " and the constant" : ""));
197     if (sp->remove) CHKERRQ(PetscViewerASCIIPrintf(viewer,"Has user-provided removal function\n"));
198     if (!(format == PETSC_VIEWER_ASCII_INFO || format == PETSC_VIEWER_ASCII_INFO_DETAIL)) {
199       for (i=0; i<sp->n; i++) {
200         CHKERRQ(VecView(sp->vecs[i],viewer));
201       }
202     }
203     CHKERRQ(PetscViewerASCIIPopTab(viewer));
204   }
205   PetscFunctionReturn(0);
206 }
207 
208 /*@C
209    MatNullSpaceCreate - Creates a data structure used to project vectors
210    out of null spaces.
211 
212    Collective
213 
214    Input Parameters:
215 +  comm - the MPI communicator associated with the object
216 .  has_cnst - PETSC_TRUE if the null space contains the constant vector; otherwise PETSC_FALSE
217 .  n - number of vectors (excluding constant vector) in null space
218 -  vecs - the vectors that span the null space (excluding the constant vector);
219           these vectors must be orthonormal. These vectors are NOT copied, so do not change them
220           after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count
221           for them by one).
222 
223    Output Parameter:
224 .  SP - the null space context
225 
226    Level: advanced
227 
228    Notes:
229     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 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceSetFunction()
235 @*/
236 PetscErrorCode  MatNullSpaceCreate(MPI_Comm comm,PetscBool has_cnst,PetscInt n,const Vec vecs[],MatNullSpace *SP)
237 {
238   MatNullSpace   sp;
239   PetscInt       i;
240 
241   PetscFunctionBegin;
242   PetscCheckFalse(n < 0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of vectors (given %" PetscInt_FMT ") cannot be negative",n);
243   if (n) PetscValidPointer(vecs,4);
244   for (i=0; i<n; i++) PetscValidHeaderSpecific(vecs[i],VEC_CLASSID,4);
245   PetscValidPointer(SP,5);
246   if (n) {
247     for (i=0; i<n; i++) {
248       /* prevent the user from changes values in the vector */
249       CHKERRQ(VecLockReadPush(vecs[i]));
250     }
251   }
252   if (PetscUnlikelyDebug(n)) {
253     PetscScalar *dots;
254     for (i=0; i<n; i++) {
255       PetscReal norm;
256       CHKERRQ(VecNorm(vecs[i],NORM_2,&norm));
257       PetscCheckFalse(PetscAbsReal(norm - 1) > PETSC_SQRT_MACHINE_EPSILON,PetscObjectComm((PetscObject)vecs[i]),PETSC_ERR_ARG_WRONG,"Vector %" PetscInt_FMT " must have 2-norm of 1.0, it is %g",i,(double)norm);
258     }
259     if (has_cnst) {
260       for (i=0; i<n; i++) {
261         PetscScalar sum;
262         CHKERRQ(VecSum(vecs[i],&sum));
263         PetscCheckFalse(PetscAbsScalar(sum) > PETSC_SQRT_MACHINE_EPSILON,PetscObjectComm((PetscObject)vecs[i]),PETSC_ERR_ARG_WRONG,"Vector %" PetscInt_FMT " must be orthogonal to constant vector, inner product is %g",i,(double)PetscAbsScalar(sum));
264       }
265     }
266     CHKERRQ(PetscMalloc1(n-1,&dots));
267     for (i=0; i<n-1; i++) {
268       PetscInt j;
269       CHKERRQ(VecMDot(vecs[i],n-i-1,vecs+i+1,dots));
270       for (j=0;j<n-i-1;j++) {
271         PetscCheckFalse(PetscAbsScalar(dots[j]) > PETSC_SQRT_MACHINE_EPSILON,PetscObjectComm((PetscObject)vecs[i]),PETSC_ERR_ARG_WRONG,"Vector %" PetscInt_FMT " must be orthogonal to vector %" PetscInt_FMT ", inner product is %g",i,i+j+1,(double)PetscAbsScalar(dots[j]));
272       }
273     }
274     CHKERRQ(PetscFree(dots));
275   }
276 
277   *SP = NULL;
278   CHKERRQ(MatInitializePackage());
279 
280   CHKERRQ(PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView));
281 
282   sp->has_cnst = has_cnst;
283   sp->n        = n;
284   sp->vecs     = NULL;
285   sp->alpha    = NULL;
286   sp->remove   = NULL;
287   sp->rmctx    = NULL;
288 
289   if (n) {
290     CHKERRQ(PetscMalloc1(n,&sp->vecs));
291     CHKERRQ(PetscMalloc1(n,&sp->alpha));
292     CHKERRQ(PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar))));
293     for (i=0; i<n; i++) {
294       CHKERRQ(PetscObjectReference((PetscObject)vecs[i]));
295       sp->vecs[i] = vecs[i];
296     }
297   }
298 
299   *SP = sp;
300   PetscFunctionReturn(0);
301 }
302 
303 /*@
304    MatNullSpaceDestroy - Destroys a data structure used to project vectors
305    out of null spaces.
306 
307    Collective on MatNullSpace
308 
309    Input Parameter:
310 .  sp - the null space context to be destroyed
311 
312    Level: advanced
313 
314 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction()
315 @*/
316 PetscErrorCode  MatNullSpaceDestroy(MatNullSpace *sp)
317 {
318   PetscInt       i;
319 
320   PetscFunctionBegin;
321   if (!*sp) PetscFunctionReturn(0);
322   PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1);
323   if (--((PetscObject)(*sp))->refct > 0) {*sp = NULL; PetscFunctionReturn(0);}
324 
325   for (i=0; i < (*sp)->n; i++) {
326     CHKERRQ(VecLockReadPop((*sp)->vecs[i]));
327   }
328 
329   CHKERRQ(VecDestroyVecs((*sp)->n,&(*sp)->vecs));
330   CHKERRQ(PetscFree((*sp)->alpha));
331   CHKERRQ(PetscHeaderDestroy(sp));
332   PetscFunctionReturn(0);
333 }
334 
335 /*@C
336    MatNullSpaceRemove - Removes all the components of a null space from a vector.
337 
338    Collective on MatNullSpace
339 
340    Input Parameters:
341 +  sp - the null space context (if this is NULL then no null space is removed)
342 -  vec - the vector from which the null space is to be removed
343 
344    Level: advanced
345 
346 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
347 @*/
348 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
349 {
350   PetscScalar    sum;
351   PetscInt       i,N;
352 
353   PetscFunctionBegin;
354   if (!sp) PetscFunctionReturn(0);
355   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
356   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
357 
358   if (sp->has_cnst) {
359     CHKERRQ(VecGetSize(vec,&N));
360     if (N > 0) {
361       CHKERRQ(VecSum(vec,&sum));
362       sum  = sum/((PetscScalar)(-1.0*N));
363       CHKERRQ(VecShift(vec,sum));
364     }
365   }
366 
367   if (sp->n) {
368     CHKERRQ(VecMDot(vec,sp->n,sp->vecs,sp->alpha));
369     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
370     CHKERRQ(VecMAXPY(vec,sp->n,sp->alpha,sp->vecs));
371   }
372 
373   if (sp->remove) {
374     CHKERRQ((*sp->remove)(sp,vec,sp->rmctx));
375   }
376   PetscFunctionReturn(0);
377 }
378 
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 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
395 @*/
396 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
397 {
398   PetscScalar    sum;
399   PetscReal      nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON;
400   PetscInt       j,n,N;
401   Vec            l,r;
402   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
403   PetscViewer    viewer;
404 
405   PetscFunctionBegin;
406   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
407   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
408   n    = sp->n;
409   CHKERRQ(PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view",&flg1,NULL));
410   CHKERRQ(PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view_draw",&flg2,NULL));
411 
412   if (n) {
413     CHKERRQ(VecDuplicate(sp->vecs[0],&l));
414   } else {
415     CHKERRQ(MatCreateVecs(mat,&l,NULL));
416   }
417 
418   CHKERRQ(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer));
419   if (sp->has_cnst) {
420     CHKERRQ(VecDuplicate(l,&r));
421     CHKERRQ(VecGetSize(l,&N));
422     sum  = 1.0/PetscSqrtReal(N);
423     CHKERRQ(VecSet(l,sum));
424     CHKERRQ(MatMult(mat,l,r));
425     CHKERRQ(VecNorm(r,NORM_2,&nrm));
426     if (nrm >= tol) consistent = PETSC_FALSE;
427     if (flg1) {
428       if (consistent) {
429         CHKERRQ(PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector"));
430       } else {
431         CHKERRQ(PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector "));
432       }
433       CHKERRQ(PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm));
434     }
435     if (!consistent && flg1) CHKERRQ(VecView(r,viewer));
436     if (!consistent && flg2) CHKERRQ(VecView(r,viewer));
437     CHKERRQ(VecDestroy(&r));
438   }
439 
440   for (j=0; j<n; j++) {
441     CHKERRQ((*mat->ops->mult)(mat,sp->vecs[j],l));
442     CHKERRQ(VecNorm(l,NORM_2,&nrm));
443     if (nrm >= tol) consistent = PETSC_FALSE;
444     if (flg1) {
445       if (consistent) {
446         CHKERRQ(PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %" PetscInt_FMT " is likely null vector",j));
447       } else {
448         CHKERRQ(PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %" PetscInt_FMT " unlikely null vector ",j));
449         consistent = PETSC_FALSE;
450       }
451       CHKERRQ(PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%" PetscInt_FMT "] || = %g\n",j,(double)nrm));
452     }
453     if (!consistent && flg1) CHKERRQ(VecView(l,viewer));
454     if (!consistent && flg2) CHKERRQ(VecView(l,viewer));
455   }
456 
457   PetscCheck(!sp->remove,PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
458   CHKERRQ(VecDestroy(&l));
459   if (isNull) *isNull = consistent;
460   PetscFunctionReturn(0);
461 }
462