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