xref: /petsc/src/mat/interface/matnull.c (revision 3307d110e72ee4e6d2468971620073eb5ff93529)
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   PetscCall(VecGetBlockSize(coords,&dim));
96   PetscCall(VecGetLocalSize(coords,&n));
97   PetscCall(VecGetSize(coords,&N));
98   n   /= dim;
99   N   /= dim;
100   sN = 1./PetscSqrtReal((PetscReal)N);
101   switch (dim) {
102   case 1:
103     PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)coords),PETSC_TRUE,0,NULL,sp));
104     break;
105   case 2:
106   case 3:
107     nmodes = (dim == 2) ? 3 : 6;
108     PetscCall(VecCreate(PetscObjectComm((PetscObject)coords),&vec[0]));
109     PetscCall(VecSetSizes(vec[0],dim*n,dim*N));
110     PetscCall(VecSetBlockSize(vec[0],dim));
111     PetscCall(VecSetUp(vec[0]));
112     for (i=1; i<nmodes; i++) PetscCall(VecDuplicate(vec[0],&vec[i]));
113     for (i=0; i<nmodes; i++) PetscCall(VecGetArray(vec[i],&v[i]));
114     PetscCall(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++) PetscCall(VecRestoreArray(vec[i],&v[i]));
147     PetscCall(VecRestoreArrayRead(coords,&x));
148     for (i=dim; i<nmodes; i++) {
149       /* Orthonormalize vec[i] against vec[0:i-1] */
150       PetscCall(VecMDot(vec[i],i,vec,dots));
151       for (j=0; j<i; j++) dots[j] *= -1.;
152       PetscCall(VecMAXPY(vec[i],i,dots,vec));
153       PetscCall(VecNormalize(vec[i],NULL));
154     }
155     PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)coords),PETSC_FALSE,nmodes,vec,sp));
156     for (i=0; i<nmodes; i++) PetscCall(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     PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer));
185   }
186   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
187   PetscCheckSameComm(sp,1,viewer,2);
188 
189   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii));
190   if (iascii) {
191     PetscViewerFormat format;
192     PetscInt          i;
193     PetscCall(PetscViewerGetFormat(viewer,&format));
194     PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)sp,viewer));
195     PetscCall(PetscViewerASCIIPushTab(viewer));
196     PetscCall(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) PetscCall(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         PetscCall(VecView(sp->vecs[i],viewer));
201       }
202     }
203     PetscCall(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   PetscCheck(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       PetscCall(VecLockReadPush(vecs[i]));
250     }
251   }
252   if (PetscUnlikelyDebug(n)) {
253     PetscScalar *dots;
254     for (i=0; i<n; i++) {
255       PetscReal norm;
256       PetscCall(VecNorm(vecs[i],NORM_2,&norm));
257       PetscCheck(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         PetscCall(VecSum(vecs[i],&sum));
263         PetscCheck(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     PetscCall(PetscMalloc1(n-1,&dots));
267     for (i=0; i<n-1; i++) {
268       PetscInt j;
269       PetscCall(VecMDot(vecs[i],n-i-1,vecs+i+1,dots));
270       for (j=0;j<n-i-1;j++) {
271         PetscCheck(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     PetscCall(PetscFree(dots));
275   }
276 
277   *SP = NULL;
278   PetscCall(MatInitializePackage());
279 
280   PetscCall(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     PetscCall(PetscMalloc1(n,&sp->vecs));
291     PetscCall(PetscMalloc1(n,&sp->alpha));
292     PetscCall(PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar))));
293     for (i=0; i<n; i++) {
294       PetscCall(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     PetscCall(VecLockReadPop((*sp)->vecs[i]));
327   }
328 
329   PetscCall(VecDestroyVecs((*sp)->n,&(*sp)->vecs));
330   PetscCall(PetscFree((*sp)->alpha));
331   PetscCall(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     PetscCall(VecGetSize(vec,&N));
360     if (N > 0) {
361       PetscCall(VecSum(vec,&sum));
362       sum  = sum/((PetscScalar)(-1.0*N));
363       PetscCall(VecShift(vec,sum));
364     }
365   }
366 
367   if (sp->n) {
368     PetscCall(VecMDot(vec,sp->n,sp->vecs,sp->alpha));
369     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
370     PetscCall(VecMAXPY(vec,sp->n,sp->alpha,sp->vecs));
371   }
372 
373   if (sp->remove) {
374     PetscCall((*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   PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view",&flg1,NULL));
410   PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view_draw",&flg2,NULL));
411 
412   if (n) {
413     PetscCall(VecDuplicate(sp->vecs[0],&l));
414   } else {
415     PetscCall(MatCreateVecs(mat,&l,NULL));
416   }
417 
418   PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer));
419   if (sp->has_cnst) {
420     PetscCall(VecDuplicate(l,&r));
421     PetscCall(VecGetSize(l,&N));
422     sum  = 1.0/PetscSqrtReal(N);
423     PetscCall(VecSet(l,sum));
424     PetscCall(MatMult(mat,l,r));
425     PetscCall(VecNorm(r,NORM_2,&nrm));
426     if (nrm >= tol) consistent = PETSC_FALSE;
427     if (flg1) {
428       if (consistent) {
429         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector"));
430       } else {
431         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector "));
432       }
433       PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm));
434     }
435     if (!consistent && flg1) PetscCall(VecView(r,viewer));
436     if (!consistent && flg2) PetscCall(VecView(r,viewer));
437     PetscCall(VecDestroy(&r));
438   }
439 
440   for (j=0; j<n; j++) {
441     PetscCall((*mat->ops->mult)(mat,sp->vecs[j],l));
442     PetscCall(VecNorm(l,NORM_2,&nrm));
443     if (nrm >= tol) consistent = PETSC_FALSE;
444     if (flg1) {
445       if (consistent) {
446         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %" PetscInt_FMT " is likely null vector",j));
447       } else {
448         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %" PetscInt_FMT " unlikely null vector ",j));
449         consistent = PETSC_FALSE;
450       }
451       PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%" PetscInt_FMT "] || = %g\n",j,(double)nrm));
452     }
453     if (!consistent && flg1) PetscCall(VecView(l,viewer));
454     if (!consistent && flg2) PetscCall(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   PetscCall(VecDestroy(&l));
459   if (isNull) *isNull = consistent;
460   PetscFunctionReturn(0);
461 }
462