xref: /petsc/src/mat/interface/matnull.c (revision 0e03b746557e2551025fde0294144c0532d12f68)
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 defined(PETSC_USE_DEBUG)
261   if (n) {
262     PetscScalar *dots;
263     for (i=0; i<n; i++) {
264       PetscReal norm;
265       ierr = VecNorm(vecs[i],NORM_2,&norm);CHKERRQ(ierr);
266       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);
267     }
268     if (has_cnst) {
269       for (i=0; i<n; i++) {
270         PetscScalar sum;
271         ierr = VecSum(vecs[i],&sum);CHKERRQ(ierr);
272         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));
273       }
274     }
275     ierr = PetscMalloc1(n-1,&dots);CHKERRQ(ierr);
276     for (i=0; i<n-1; i++) {
277       PetscInt j;
278       ierr = VecMDot(vecs[i],n-i-1,vecs+i+1,dots);CHKERRQ(ierr);
279       for (j=0;j<n-i-1;j++) {
280         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]));
281       }
282     }
283     PetscFree(dots);CHKERRQ(ierr);
284   }
285 #endif
286 
287   *SP = NULL;
288   ierr = MatInitializePackage();CHKERRQ(ierr);
289 
290   ierr = PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr);
291 
292   sp->has_cnst = has_cnst;
293   sp->n        = n;
294   sp->vecs     = 0;
295   sp->alpha    = 0;
296   sp->remove   = 0;
297   sp->rmctx    = 0;
298 
299   if (n) {
300     ierr = PetscMalloc1(n,&sp->vecs);CHKERRQ(ierr);
301     ierr = PetscMalloc1(n,&sp->alpha);CHKERRQ(ierr);
302     ierr = PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr);
303     for (i=0; i<n; i++) {
304       ierr        = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr);
305       sp->vecs[i] = vecs[i];
306     }
307   }
308 
309   *SP = sp;
310   PetscFunctionReturn(0);
311 }
312 
313 /*@
314    MatNullSpaceDestroy - Destroys a data structure used to project vectors
315    out of null spaces.
316 
317    Collective on MatNullSpace
318 
319    Input Parameter:
320 .  sp - the null space context to be destroyed
321 
322    Level: advanced
323 
324 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction()
325 @*/
326 PetscErrorCode  MatNullSpaceDestroy(MatNullSpace *sp)
327 {
328   PetscErrorCode ierr;
329   PetscInt       i;
330 
331   PetscFunctionBegin;
332   if (!*sp) PetscFunctionReturn(0);
333   PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1);
334   if (--((PetscObject)(*sp))->refct > 0) {*sp = 0; PetscFunctionReturn(0);}
335 
336   for (i=0; i < (*sp)->n; i++) {
337     ierr = VecLockReadPop((*sp)->vecs[i]);CHKERRQ(ierr);
338   }
339 
340   ierr = VecDestroyVecs((*sp)->n,&(*sp)->vecs);CHKERRQ(ierr);
341   ierr = PetscFree((*sp)->alpha);CHKERRQ(ierr);
342   ierr = PetscHeaderDestroy(sp);CHKERRQ(ierr);
343   PetscFunctionReturn(0);
344 }
345 
346 /*@C
347    MatNullSpaceRemove - Removes all the components of a null space from a vector.
348 
349    Collective on MatNullSpace
350 
351    Input Parameters:
352 +  sp - the null space context (if this is NULL then no null space is removed)
353 -  vec - the vector from which the null space is to be removed
354 
355    Level: advanced
356 
357 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
358 @*/
359 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
360 {
361   PetscScalar    sum;
362   PetscInt       i,N;
363   PetscErrorCode ierr;
364 
365   PetscFunctionBegin;
366   if (!sp) PetscFunctionReturn(0);
367   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
368   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
369 
370   if (sp->has_cnst) {
371     ierr = VecGetSize(vec,&N);CHKERRQ(ierr);
372     if (N > 0) {
373       ierr = VecSum(vec,&sum);CHKERRQ(ierr);
374       sum  = sum/((PetscScalar)(-1.0*N));
375       ierr = VecShift(vec,sum);CHKERRQ(ierr);
376     }
377   }
378 
379   if (sp->n) {
380     ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr);
381     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
382     ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr);
383   }
384 
385   if (sp->remove) {
386     ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr);
387   }
388   PetscFunctionReturn(0);
389 }
390 
391 /*@
392    MatNullSpaceTest  - Tests if the claimed null space is really a
393      null space of a matrix
394 
395    Collective on MatNullSpace
396 
397    Input Parameters:
398 +  sp - the null space context
399 -  mat - the matrix
400 
401    Output Parameters:
402 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
403 
404    Level: advanced
405 
406 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
407 @*/
408 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
409 {
410   PetscScalar    sum;
411   PetscReal      nrm,tol = 10. * PETSC_SQRT_MACHINE_EPSILON;
412   PetscInt       j,n,N;
413   PetscErrorCode ierr;
414   Vec            l,r;
415   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
416   PetscViewer    viewer;
417 
418   PetscFunctionBegin;
419   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
420   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
421   n    = sp->n;
422   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr);
423   ierr = PetscOptionsGetBool(((PetscObject)sp)->options,((PetscObject)mat)->prefix,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr);
424 
425   if (n) {
426     ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr);
427   } else {
428     ierr = MatCreateVecs(mat,&l,NULL);CHKERRQ(ierr);
429   }
430 
431   ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
432   if (sp->has_cnst) {
433     ierr = VecDuplicate(l,&r);CHKERRQ(ierr);
434     ierr = VecGetSize(l,&N);CHKERRQ(ierr);
435     sum  = 1.0/N;
436     ierr = VecSet(l,sum);CHKERRQ(ierr);
437     ierr = MatMult(mat,l,r);CHKERRQ(ierr);
438     ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr);
439     if (nrm >= tol) consistent = PETSC_FALSE;
440     if (flg1) {
441       if (consistent) {
442         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr);
443       } else {
444         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr);
445       }
446       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm);CHKERRQ(ierr);
447     }
448     if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
449     if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
450     ierr = VecDestroy(&r);CHKERRQ(ierr);
451   }
452 
453   for (j=0; j<n; j++) {
454     ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr);
455     ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr);
456     if (nrm >= tol) consistent = PETSC_FALSE;
457     if (flg1) {
458       if (consistent) {
459         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr);
460       } else {
461         ierr       = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr);
462         consistent = PETSC_FALSE;
463       }
464       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %g\n",j,(double)nrm);CHKERRQ(ierr);
465     }
466     if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
467     if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
468   }
469 
470   if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
471   ierr = VecDestroy(&l);CHKERRQ(ierr);
472   if (isNull) *isNull = consistent;
473   PetscFunctionReturn(0);
474 }
475 
476