xref: /petsc/src/mat/interface/matnull.c (revision 6858538e710279fe46cd8279ab34c98b10293bbd) !
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) PetscCall((*sp->remove)(sp,vec,sp->rmctx));
374   PetscFunctionReturn(0);
375 }
376 
377 /*@
378    MatNullSpaceTest  - Tests if the claimed null space is really a
379      null space of a matrix
380 
381    Collective on MatNullSpace
382 
383    Input Parameters:
384 +  sp - the null space context
385 -  mat - the matrix
386 
387    Output Parameters:
388 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
389 
390    Level: advanced
391 
392 .seealso: `MatNullSpaceCreate()`, `MatNullSpaceDestroy()`, `MatNullSpaceSetFunction()`
393 @*/
394 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
395 {
396   PetscScalar    sum;
397   PetscReal      nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON;
398   PetscInt       j,n,N;
399   Vec            l,r;
400   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
401   PetscViewer    viewer;
402 
403   PetscFunctionBegin;
404   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
405   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
406   n    = sp->n;
407   PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view",&flg1,NULL));
408   PetscCall(PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view_draw",&flg2,NULL));
409 
410   if (n) {
411     PetscCall(VecDuplicate(sp->vecs[0],&l));
412   } else {
413     PetscCall(MatCreateVecs(mat,&l,NULL));
414   }
415 
416   PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer));
417   if (sp->has_cnst) {
418     PetscCall(VecDuplicate(l,&r));
419     PetscCall(VecGetSize(l,&N));
420     sum  = 1.0/PetscSqrtReal(N);
421     PetscCall(VecSet(l,sum));
422     PetscCall(MatMult(mat,l,r));
423     PetscCall(VecNorm(r,NORM_2,&nrm));
424     if (nrm >= tol) consistent = PETSC_FALSE;
425     if (flg1) {
426       if (consistent) {
427         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector"));
428       } else {
429         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector "));
430       }
431       PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm));
432     }
433     if (!consistent && flg1) PetscCall(VecView(r,viewer));
434     if (!consistent && flg2) PetscCall(VecView(r,viewer));
435     PetscCall(VecDestroy(&r));
436   }
437 
438   for (j=0; j<n; j++) {
439     PetscCall((*mat->ops->mult)(mat,sp->vecs[j],l));
440     PetscCall(VecNorm(l,NORM_2,&nrm));
441     if (nrm >= tol) consistent = PETSC_FALSE;
442     if (flg1) {
443       if (consistent) {
444         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %" PetscInt_FMT " is likely null vector",j));
445       } else {
446         PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %" PetscInt_FMT " unlikely null vector ",j));
447         consistent = PETSC_FALSE;
448       }
449       PetscCall(PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%" PetscInt_FMT "] || = %g\n",j,(double)nrm));
450     }
451     if (!consistent && flg1) PetscCall(VecView(l,viewer));
452     if (!consistent && flg2) PetscCall(VecView(l,viewer));
453   }
454 
455   PetscCheck(!sp->remove,PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
456   PetscCall(VecDestroy(&l));
457   if (isNull) *isNull = consistent;
458   PetscFunctionReturn(0);
459 }
460