xref: /petsc/src/mat/interface/matnull.c (revision bef158480efac06de457f7a665168877ab3c2fd7)
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 Arguments:
40 .  sp - null space object
41 
42    Output Arguments:
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 
57   PetscFunctionBegin;
58   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
59   if (has_const) *has_const = sp->has_cnst;
60   if (n) *n = sp->n;
61   if (vecs) *vecs = sp->vecs;
62   PetscFunctionReturn(0);
63 }
64 
65 /*@
66    MatNullSpaceCreateRigidBody - create rigid body modes from coordinates
67 
68    Collective on Vec
69 
70    Input Argument:
71 .  coords - block of coordinates of each node, must have block size set
72 
73    Output Argument:
74 .  sp - the null space
75 
76    Level: advanced
77 
78    Notes:
79      If you are solving an elasticity problem you should likely use this, in conjunction with MatSetNearNullspace(), to provide information that
80      the PCGAMG preconditioner can use to construct a much more efficient preconditioner.
81 
82      If you are solving an elasticity problem with pure Neumann boundary conditions you can use this in conjunction with MatSetNullspace() to
83      provide this information to the linear solver so it can handle the null space appropriately in the linear solution.
84 
85 
86 .seealso: MatNullSpaceCreate(), MatSetNearNullspace(), MatSetNullspace()
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 /*@C
165    MatNullSpaceView - Visualizes a null space object.
166 
167    Collective on MatNullSpace
168 
169    Input Parameters:
170 +  matnull - the null space
171 -  viewer - visualization context
172 
173    Level: advanced
174 
175    Fortran Note:
176    This routine is not supported in Fortran.
177 
178 .seealso: MatNullSpaceCreate(), PetscViewerASCIIOpen()
179 @*/
180 PetscErrorCode MatNullSpaceView(MatNullSpace sp,PetscViewer viewer)
181 {
182   PetscErrorCode ierr;
183   PetscBool      iascii;
184 
185   PetscFunctionBegin;
186   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
187   if (!viewer) {
188     ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
189   }
190   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
191   PetscCheckSameComm(sp,1,viewer,2);
192 
193   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
194   if (iascii) {
195     PetscViewerFormat format;
196     PetscInt          i;
197     ierr = PetscViewerGetFormat(viewer,&format);CHKERRQ(ierr);
198     ierr = PetscObjectPrintClassNamePrefixType((PetscObject)sp,viewer);CHKERRQ(ierr);
199     ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
200     ierr = PetscViewerASCIIPrintf(viewer,"Contains %D vector%s%s\n",sp->n,sp->n==1 ? "" : "s",sp->has_cnst ? " and the constant" : "");CHKERRQ(ierr);
201     if (sp->remove) {ierr = PetscViewerASCIIPrintf(viewer,"Has user-provided removal function\n");CHKERRQ(ierr);}
202     if (!(format == PETSC_VIEWER_ASCII_INFO || format == PETSC_VIEWER_ASCII_INFO_DETAIL)) {
203       for (i=0; i<sp->n; i++) {
204         ierr = VecView(sp->vecs[i],viewer);CHKERRQ(ierr);
205       }
206     }
207     ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
208   }
209   PetscFunctionReturn(0);
210 }
211 
212 /*@C
213    MatNullSpaceCreate - Creates a data structure used to project vectors
214    out of null spaces.
215 
216    Collective
217 
218    Input Parameters:
219 +  comm - the MPI communicator associated with the object
220 .  has_cnst - PETSC_TRUE if the null space contains the constant vector; otherwise PETSC_FALSE
221 .  n - number of vectors (excluding constant vector) in null space
222 -  vecs - the vectors that span the null space (excluding the constant vector);
223           these vectors must be orthonormal. These vectors are NOT copied, so do not change them
224           after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count
225           for them by one).
226 
227    Output Parameter:
228 .  SP - the null space context
229 
230    Level: advanced
231 
232    Notes:
233     See MatNullSpaceSetFunction() as an alternative way of providing the null space information instead of setting vecs.
234 
235       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
236        need to pass in a function that eliminates the constant function into MatNullSpaceSetFunction().
237 
238   Users manual sections:
239 .   sec_singular
240 
241 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceSetFunction()
242 @*/
243 PetscErrorCode  MatNullSpaceCreate(MPI_Comm comm,PetscBool has_cnst,PetscInt n,const Vec vecs[],MatNullSpace *SP)
244 {
245   MatNullSpace   sp;
246   PetscErrorCode ierr;
247   PetscInt       i;
248 
249   PetscFunctionBegin;
250   if (n < 0) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of vectors (given %D) cannot be negative",n);
251   if (n) PetscValidPointer(vecs,4);
252   for (i=0; i<n; i++) PetscValidHeaderSpecific(vecs[i],VEC_CLASSID,4);
253   PetscValidPointer(SP,5);
254   if (n) {
255     for (i=0; i<n; i++) {
256       /* prevent the user from changes values in the vector */
257       ierr = VecLockReadPush(vecs[i]);CHKERRQ(ierr);
258     }
259   }
260   if (PetscUnlikelyDebug(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) > 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 
285   *SP = NULL;
286   ierr = MatInitializePackage();CHKERRQ(ierr);
287 
288   ierr = PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr);
289 
290   sp->has_cnst = has_cnst;
291   sp->n        = n;
292   sp->vecs     = NULL;
293   sp->alpha    = NULL;
294   sp->remove   = NULL;
295   sp->rmctx    = NULL;
296 
297   if (n) {
298     ierr = PetscMalloc1(n,&sp->vecs);CHKERRQ(ierr);
299     ierr = PetscMalloc1(n,&sp->alpha);CHKERRQ(ierr);
300     ierr = PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr);
301     for (i=0; i<n; i++) {
302       ierr        = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr);
303       sp->vecs[i] = vecs[i];
304     }
305   }
306 
307   *SP = sp;
308   PetscFunctionReturn(0);
309 }
310 
311 /*@
312    MatNullSpaceDestroy - Destroys a data structure used to project vectors
313    out of null spaces.
314 
315    Collective on MatNullSpace
316 
317    Input Parameter:
318 .  sp - the null space context to be destroyed
319 
320    Level: advanced
321 
322 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction()
323 @*/
324 PetscErrorCode  MatNullSpaceDestroy(MatNullSpace *sp)
325 {
326   PetscErrorCode ierr;
327   PetscInt       i;
328 
329   PetscFunctionBegin;
330   if (!*sp) PetscFunctionReturn(0);
331   PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1);
332   if (--((PetscObject)(*sp))->refct > 0) {*sp = NULL; PetscFunctionReturn(0);}
333 
334   for (i=0; i < (*sp)->n; i++) {
335     ierr = VecLockReadPop((*sp)->vecs[i]);CHKERRQ(ierr);
336   }
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 /*@C
345    MatNullSpaceRemove - Removes all the components of a null space from a vector.
346 
347    Collective on MatNullSpace
348 
349    Input Parameters:
350 +  sp - the null space context (if this is NULL then no null space is removed)
351 -  vec - the vector from which the null space is to be removed
352 
353    Level: advanced
354 
355 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
356 @*/
357 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
358 {
359   PetscScalar    sum;
360   PetscInt       i,N;
361   PetscErrorCode ierr;
362 
363   PetscFunctionBegin;
364   if (!sp) PetscFunctionReturn(0);
365   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
366   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
367 
368   if (sp->has_cnst) {
369     ierr = VecGetSize(vec,&N);CHKERRQ(ierr);
370     if (N > 0) {
371       ierr = VecSum(vec,&sum);CHKERRQ(ierr);
372       sum  = sum/((PetscScalar)(-1.0*N));
373       ierr = VecShift(vec,sum);CHKERRQ(ierr);
374     }
375   }
376 
377   if (sp->n) {
378     ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr);
379     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
380     ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr);
381   }
382 
383   if (sp->remove) {
384     ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr);
385   }
386   PetscFunctionReturn(0);
387 }
388 
389 /*@
390    MatNullSpaceTest  - Tests if the claimed null space is really a
391      null space of a matrix
392 
393    Collective on MatNullSpace
394 
395    Input Parameters:
396 +  sp - the null space context
397 -  mat - the matrix
398 
399    Output Parameters:
400 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
401 
402    Level: advanced
403 
404 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
405 @*/
406 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
407 {
408   PetscScalar    sum;
409   PetscReal      nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON;
410   PetscInt       j,n,N;
411   PetscErrorCode ierr;
412   Vec            l,r;
413   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
414   PetscViewer    viewer;
415 
416   PetscFunctionBegin;
417   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
418   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
419   n    = sp->n;
420   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr);
421   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr);
422 
423   if (n) {
424     ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr);
425   } else {
426     ierr = MatCreateVecs(mat,&l,NULL);CHKERRQ(ierr);
427   }
428 
429   ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
430   if (sp->has_cnst) {
431     ierr = VecDuplicate(l,&r);CHKERRQ(ierr);
432     ierr = VecGetSize(l,&N);CHKERRQ(ierr);
433     sum  = 1.0/N;
434     ierr = VecSet(l,sum);CHKERRQ(ierr);
435     ierr = MatMult(mat,l,r);CHKERRQ(ierr);
436     ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr);
437     if (nrm >= tol) consistent = PETSC_FALSE;
438     if (flg1) {
439       if (consistent) {
440         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr);
441       } else {
442         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr);
443       }
444       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm);CHKERRQ(ierr);
445     }
446     if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
447     if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
448     ierr = VecDestroy(&r);CHKERRQ(ierr);
449   }
450 
451   for (j=0; j<n; j++) {
452     ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr);
453     ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr);
454     if (nrm >= tol) consistent = PETSC_FALSE;
455     if (flg1) {
456       if (consistent) {
457         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr);
458       } else {
459         ierr       = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr);
460         consistent = PETSC_FALSE;
461       }
462       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %g\n",j,(double)nrm);CHKERRQ(ierr);
463     }
464     if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
465     if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
466   }
467 
468   if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
469   ierr = VecDestroy(&l);CHKERRQ(ierr);
470   if (isNull) *isNull = consistent;
471   PetscFunctionReturn(0);
472 }
473