xref: /petsc/src/mat/interface/matnull.c (revision 493de4004d8a6b5b16751267168cdab7e3bbd94a)
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 #undef __FUNCT__
11 #define __FUNCT__ "MatNullSpaceSetFunction"
12 /*@C
13    MatNullSpaceSetFunction - set a function that removes a null space from a vector
14    out of null spaces.
15 
16    Logically Collective on MatNullSpace
17 
18    Input Parameters:
19 +  sp - the null space object
20 .  rem - the function that removes the null space
21 -  ctx - context for the remove function
22 
23    Level: advanced
24 
25 .keywords: PC, null space, create
26 
27 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceCreate()
28 @*/
29 PetscErrorCode  MatNullSpaceSetFunction(MatNullSpace sp, PetscErrorCode (*rem)(MatNullSpace,Vec,void*),void *ctx)
30 {
31   PetscFunctionBegin;
32   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
33   sp->remove = rem;
34   sp->rmctx  = ctx;
35   PetscFunctionReturn(0);
36 }
37 
38 #undef __FUNCT__
39 #define __FUNCT__ "MatNullSpaceGetVecs"
40 /*@C
41    MatNullSpaceGetVecs - get vectors defining the null space
42 
43    Not Collective
44 
45    Input Arguments:
46 .  sp - null space object
47 
48    Output Arguments:
49 +  has_cnst - PETSC_TRUE if the null space contains the constant vector, otherwise PETSC_FALSE
50 .  n - number of vectors (excluding constant vector) in null space
51 -  vecs - orthonormal vectors that span the null space (excluding the constant vector)
52 
53    Level: developer
54 
55    Notes:
56       These vectors and the array are owned by the MatNullSpace and should not be destroyed or freeded by the caller
57 
58 .seealso: MatNullSpaceCreate(), MatGetNullSpace(), MatGetNearNullSpace()
59 @*/
60 PetscErrorCode MatNullSpaceGetVecs(MatNullSpace sp,PetscBool *has_const,PetscInt *n,const Vec **vecs)
61 {
62 
63   PetscFunctionBegin;
64   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
65   if (has_const) *has_const = sp->has_cnst;
66   if (n) *n = sp->n;
67   if (vecs) *vecs = sp->vecs;
68   PetscFunctionReturn(0);
69 }
70 
71 #undef __FUNCT__
72 #define __FUNCT__ "MatNullSpaceCreateRigidBody"
73 /*@
74    MatNullSpaceCreateRigidBody - create rigid body modes from coordinates
75 
76    Collective on Vec
77 
78    Input Argument:
79 .  coords - block of coordinates of each node, must have block size set
80 
81    Output Argument:
82 .  sp - the null space
83 
84    Level: advanced
85 
86 .seealso: MatNullSpaceCreate()
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 #undef __FUNCT__
165 #define __FUNCT__ "MatNullSpaceView"
166 /*@C
167    MatNullSpaceView - Visualizes a null space object.
168 
169    Collective on MatNullSpace
170 
171    Input Parameters:
172 +  matnull - the null space
173 -  viewer - visualization context
174 
175    Level: advanced
176 
177    Fortran Note:
178    This routine is not supported in Fortran.
179 
180 .seealso: MatNullSpaceCreate(), PetscViewerASCIIOpen()
181 @*/
182 PetscErrorCode MatNullSpaceView(MatNullSpace sp,PetscViewer viewer)
183 {
184   PetscErrorCode ierr;
185   PetscBool      iascii;
186 
187   PetscFunctionBegin;
188   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
189   if (!viewer) {
190     ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
191   }
192   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
193   PetscCheckSameComm(sp,1,viewer,2);
194 
195   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
196   if (iascii) {
197     PetscViewerFormat format;
198     PetscInt          i;
199     ierr = PetscViewerGetFormat(viewer,&format);CHKERRQ(ierr);
200     ierr = PetscObjectPrintClassNamePrefixType((PetscObject)sp,viewer);CHKERRQ(ierr);
201     ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
202     ierr = PetscViewerASCIIPrintf(viewer,"Contains %D vector%s%s\n",sp->n,sp->n==1 ? "" : "s",sp->has_cnst ? " and the constant" : "");CHKERRQ(ierr);
203     if (sp->remove) {ierr = PetscViewerASCIIPrintf(viewer,"Has user-provided removal function\n");CHKERRQ(ierr);}
204     if (!(format == PETSC_VIEWER_ASCII_INFO || format == PETSC_VIEWER_ASCII_INFO_DETAIL)) {
205       for (i=0; i<sp->n; i++) {
206         ierr = VecView(sp->vecs[i],viewer);CHKERRQ(ierr);
207       }
208     }
209     ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
210   }
211   PetscFunctionReturn(0);
212 }
213 
214 #undef __FUNCT__
215 #define __FUNCT__ "MatNullSpaceCreate"
216 /*@
217    MatNullSpaceCreate - Creates a data structure used to project vectors
218    out of null spaces.
219 
220    Collective on MPI_Comm
221 
222    Input Parameters:
223 +  comm - the MPI communicator associated with the object
224 .  has_cnst - PETSC_TRUE if the null space contains the constant vector; otherwise PETSC_FALSE
225 .  n - number of vectors (excluding constant vector) in null space
226 -  vecs - the vectors that span the null space (excluding the constant vector);
227           these vectors must be orthonormal. These vectors are NOT copied, so do not change them
228           after this call. You should free the array that you pass in and destroy the vectors (this will reduce the reference count
229           for them by one).
230 
231    Output Parameter:
232 .  SP - the null space context
233 
234    Level: advanced
235 
236    Notes: See MatNullSpaceSetFunction() as an alternative way of providing the null space information instead of setting vecs.
237 
238       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
239        need to pass in a function that eliminates the constant function into MatNullSpaceSetFunction().
240 
241   Users manual sections:
242 .   sec_singular
243 
244 .keywords: PC, null space, create
245 
246 .seealso: MatNullSpaceDestroy(), MatNullSpaceRemove(), MatSetNullSpace(), MatNullSpace, MatNullSpaceSetFunction()
247 @*/
248 PetscErrorCode  MatNullSpaceCreate(MPI_Comm comm,PetscBool has_cnst,PetscInt n,const Vec vecs[],MatNullSpace *SP)
249 {
250   MatNullSpace   sp;
251   PetscErrorCode ierr;
252   PetscInt       i;
253 
254   PetscFunctionBegin;
255   if (n < 0) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of vectors (given %D) cannot be negative",n);
256   if (n) PetscValidPointer(vecs,4);
257   for (i=0; i<n; i++) PetscValidHeaderSpecific(vecs[i],VEC_CLASSID,4);
258   PetscValidPointer(SP,5);
259 
260   *SP = NULL;
261   ierr = MatInitializePackage();CHKERRQ(ierr);
262 
263   ierr = PetscHeaderCreate(sp,MAT_NULLSPACE_CLASSID,"MatNullSpace","Null space","Mat",comm,MatNullSpaceDestroy,MatNullSpaceView);CHKERRQ(ierr);
264 
265   sp->has_cnst = has_cnst;
266   sp->n        = n;
267   sp->vecs     = 0;
268   sp->alpha    = 0;
269   sp->remove   = 0;
270   sp->rmctx    = 0;
271 
272   if (n) {
273     ierr = PetscMalloc1(n,&sp->vecs);CHKERRQ(ierr);
274     ierr = PetscMalloc1(n,&sp->alpha);CHKERRQ(ierr);
275     ierr = PetscLogObjectMemory((PetscObject)sp,n*(sizeof(Vec)+sizeof(PetscScalar)));CHKERRQ(ierr);
276     for (i=0; i<n; i++) {
277       ierr        = PetscObjectReference((PetscObject)vecs[i]);CHKERRQ(ierr);
278       sp->vecs[i] = vecs[i];
279     }
280   }
281 
282   *SP = sp;
283   PetscFunctionReturn(0);
284 }
285 
286 #undef __FUNCT__
287 #define __FUNCT__ "MatNullSpaceDestroy"
288 /*@
289    MatNullSpaceDestroy - Destroys a data structure used to project vectors
290    out of null spaces.
291 
292    Collective on MatNullSpace
293 
294    Input Parameter:
295 .  sp - the null space context to be destroyed
296 
297    Level: advanced
298 
299 .keywords: PC, null space, destroy
300 
301 .seealso: MatNullSpaceCreate(), MatNullSpaceRemove(), MatNullSpaceSetFunction()
302 @*/
303 PetscErrorCode  MatNullSpaceDestroy(MatNullSpace *sp)
304 {
305   PetscErrorCode ierr;
306 
307   PetscFunctionBegin;
308   if (!*sp) PetscFunctionReturn(0);
309   PetscValidHeaderSpecific((*sp),MAT_NULLSPACE_CLASSID,1);
310   if (--((PetscObject)(*sp))->refct > 0) {*sp = 0; PetscFunctionReturn(0);}
311 
312   ierr = VecDestroyVecs((*sp)->n,&(*sp)->vecs);CHKERRQ(ierr);
313   ierr = PetscFree((*sp)->alpha);CHKERRQ(ierr);
314   ierr = PetscHeaderDestroy(sp);CHKERRQ(ierr);
315   PetscFunctionReturn(0);
316 }
317 
318 #undef __FUNCT__
319 #define __FUNCT__ "MatNullSpaceRemove"
320 /*@C
321    MatNullSpaceRemove - Removes all the components of a null space from a vector.
322 
323    Collective on MatNullSpace
324 
325    Input Parameters:
326 +  sp - the null space context
327 -  vec - the vector from which the null space is to be removed
328 
329    Level: advanced
330 
331 .keywords: PC, null space, remove
332 
333 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
334 @*/
335 PetscErrorCode  MatNullSpaceRemove(MatNullSpace sp,Vec vec)
336 {
337   PetscScalar    sum;
338   PetscInt       i,N;
339   PetscErrorCode ierr;
340 
341   PetscFunctionBegin;
342   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
343   PetscValidHeaderSpecific(vec,VEC_CLASSID,2);
344 
345   if (sp->has_cnst) {
346     ierr = VecGetSize(vec,&N);CHKERRQ(ierr);
347     if (N > 0) {
348       ierr = VecSum(vec,&sum);CHKERRQ(ierr);
349       sum  = sum/((PetscScalar)(-1.0*N));
350       ierr = VecShift(vec,sum);CHKERRQ(ierr);
351     }
352   }
353 
354   if (sp->n) {
355     ierr = VecMDot(vec,sp->n,sp->vecs,sp->alpha);CHKERRQ(ierr);
356     for (i=0; i<sp->n; i++) sp->alpha[i] = -sp->alpha[i];
357     ierr = VecMAXPY(vec,sp->n,sp->alpha,sp->vecs);CHKERRQ(ierr);
358   }
359 
360   if (sp->remove) {
361     ierr = (*sp->remove)(sp,vec,sp->rmctx);CHKERRQ(ierr);
362   }
363   PetscFunctionReturn(0);
364 }
365 
366 #undef __FUNCT__
367 #define __FUNCT__ "MatNullSpaceTest"
368 /*@
369    MatNullSpaceTest  - Tests if the claimed null space is really a
370      null space of a matrix
371 
372    Collective on MatNullSpace
373 
374    Input Parameters:
375 +  sp - the null space context
376 -  mat - the matrix
377 
378    Output Parameters:
379 .  isNull - PETSC_TRUE if the nullspace is valid for this matrix
380 
381    Level: advanced
382 
383 .keywords: PC, null space, remove
384 
385 .seealso: MatNullSpaceCreate(), MatNullSpaceDestroy(), MatNullSpaceSetFunction()
386 @*/
387 PetscErrorCode  MatNullSpaceTest(MatNullSpace sp,Mat mat,PetscBool  *isNull)
388 {
389   PetscScalar    sum;
390   PetscReal      nrm;
391   PetscInt       j,n,N;
392   PetscErrorCode ierr;
393   Vec            l,r;
394   PetscBool      flg1 = PETSC_FALSE,flg2 = PETSC_FALSE,consistent = PETSC_TRUE;
395   PetscViewer    viewer;
396 
397   PetscFunctionBegin;
398   PetscValidHeaderSpecific(sp,MAT_NULLSPACE_CLASSID,1);
399   PetscValidHeaderSpecific(mat,MAT_CLASSID,2);
400   n    = sp->n;
401   ierr = PetscOptionsGetBool(NULL,"-mat_null_space_test_view",&flg1,NULL);CHKERRQ(ierr);
402   ierr = PetscOptionsGetBool(NULL,"-mat_null_space_test_view_draw",&flg2,NULL);CHKERRQ(ierr);
403 
404   if (n) {
405     ierr = VecDuplicate(sp->vecs[0],&l);CHKERRQ(ierr);
406   } else {
407     ierr = MatCreateVecs(mat,&l,NULL);CHKERRQ(ierr);
408   }
409 
410   ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)sp),&viewer);CHKERRQ(ierr);
411   if (sp->has_cnst) {
412     ierr = VecDuplicate(l,&r);CHKERRQ(ierr);
413     ierr = VecGetSize(l,&N);CHKERRQ(ierr);
414     sum  = 1.0/N;
415     ierr = VecSet(l,sum);CHKERRQ(ierr);
416     ierr = MatMult(mat,l,r);CHKERRQ(ierr);
417     ierr = VecNorm(r,NORM_2,&nrm);CHKERRQ(ierr);
418     if (nrm >= 1.e-7) consistent = PETSC_FALSE;
419     if (flg1) {
420       if (consistent) {
421         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are likely null vector");CHKERRQ(ierr);
422       } else {
423         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Constants are unlikely null vector ");CHKERRQ(ierr);
424       }
425       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * 1/N || = %g\n",(double)nrm);CHKERRQ(ierr);
426     }
427     if (!consistent && flg1) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
428     if (!consistent && flg2) {ierr = VecView(r,viewer);CHKERRQ(ierr);}
429     ierr = VecDestroy(&r);CHKERRQ(ierr);
430   }
431 
432   for (j=0; j<n; j++) {
433     ierr = (*mat->ops->mult)(mat,sp->vecs[j],l);CHKERRQ(ierr);
434     ierr = VecNorm(l,NORM_2,&nrm);CHKERRQ(ierr);
435     if (nrm >= 1.e-7) consistent = PETSC_FALSE;
436     if (flg1) {
437       if (consistent) {
438         ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D is likely null vector",j);CHKERRQ(ierr);
439       } else {
440         ierr       = PetscPrintf(PetscObjectComm((PetscObject)sp),"Null vector %D unlikely null vector ",j);CHKERRQ(ierr);
441         consistent = PETSC_FALSE;
442       }
443       ierr = PetscPrintf(PetscObjectComm((PetscObject)sp),"|| A * v[%D] || = %g\n",j,(double)nrm);CHKERRQ(ierr);
444     }
445     if (!consistent && flg1) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
446     if (!consistent && flg2) {ierr = VecView(l,viewer);CHKERRQ(ierr);}
447   }
448 
449   if (sp->remove) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot test a null space provided as a function with MatNullSpaceSetFunction()");
450   ierr = VecDestroy(&l);CHKERRQ(ierr);
451   if (isNull) *isNull = consistent;
452   PetscFunctionReturn(0);
453 }
454 
455