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