Actual source code: dainterp.c

  1: /*$Id: dainterp.c,v 1.22 2001/04/10 19:37:23 bsmith Exp $*/
  2: 
  3: /*
  4:   Code for interpolating between grids represented by DAs
  5: */

  7: #include "src/dm/da/daimpl.h"    /*I   "petscda.h"   I*/
  8: #include "petscmg.h"

 10: int DMGetInterpolationScale(DM dac,DM daf,Mat mat,Vec *scale)
 11: {
 12:   int    ierr;
 13:   Vec    fine;
 14:   Scalar one = 1.0;

 17:   DMCreateGlobalVector(daf,&fine);
 18:   DMCreateGlobalVector(dac,scale);
 19:   VecSet(&one,fine);
 20:   MatRestrict(mat,fine,*scale);
 21:   VecDestroy(fine);
 22:   VecReciprocal(*scale);
 23:   return(0);
 24: }

 26: int DAGetInterpolation_1D_dof(DA dac,DA daf,Mat *A)
 27: {
 28:   int            ierr,i,i_start,m_f,Mx,*idx_f;
 29:   int            m_ghost,*idx_c,m_ghost_c;
 30:   int            row,col,i_start_ghost,mx,m_c,nc,ratio;
 31:   int            i_c,i_start_c,i_start_ghost_c,cols[2],dof;
 32:   Scalar         v[2],x;
 33:   Mat            mat;
 34:   DAPeriodicType pt;

 37:   DAGetInfo(dac,0,&Mx,0,0,0,0,0,0,0,&pt,0);
 38:   DAGetInfo(daf,0,&mx,0,0,0,0,0,&dof,0,0,0);
 39:   if (pt == DA_XPERIODIC) {
 40:     ratio = mx/Mx;
 41:     if (ratio*Mx != mx) SETERRQ2(1,"Ratio between levels: mx/Mx  must be integer: mx %d Mx %d",mx,Mx);
 42:   } else {
 43:     ratio = (mx-1)/(Mx-1);
 44:     if (ratio*(Mx-1) != mx-1) SETERRQ2(1,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %d Mx %d",mx,Mx);
 45:   }

 47:   DAGetCorners(daf,&i_start,0,0,&m_f,0,0);
 48:   DAGetGhostCorners(daf,&i_start_ghost,0,0,&m_ghost,0,0);
 49:   DAGetGlobalIndices(daf,PETSC_NULL,&idx_f);

 51:   DAGetCorners(dac,&i_start_c,0,0,&m_c,0,0);
 52:   DAGetGhostCorners(dac,&i_start_ghost_c,0,0,&m_ghost_c,0,0);
 53:   DAGetGlobalIndices(dac,PETSC_NULL,&idx_c);

 55:   /* create interpolation matrix */
 56:   MatCreateMPIAIJ(dac->comm,m_f,m_c,mx,Mx,2,0,0,0,&mat);
 57:   MatSetOption(mat,MAT_COLUMNS_SORTED);

 59:   /* loop over local fine grid nodes setting interpolation for those*/
 60:   for (i=i_start; i<i_start+m_f; i++) {
 61:     /* convert to local "natural" numbering and then to PETSc global numbering */
 62:     row    = idx_f[dof*(i-i_start_ghost)]/dof;

 64:     i_c = (i/ratio);    /* coarse grid node to left of fine grid node */

 66:     /* 
 67:          Only include those interpolation points that are truly 
 68:          nonzero. Note this is very important for final grid lines
 69:          in x direction; since they have no right neighbor
 70:     */
 71:     x  = ((double)(i - i_c*ratio))/((double)ratio);
 72:     /* printf("i j %d %d %g %gn",i,j,x,y); */
 73:     nc = 0;
 74:       /* one left and below; or we are right on it */
 75:     col      = dof*(i_c-i_start_ghost_c);
 76:     cols[nc] = idx_c[col]/dof;
 77:     v[nc++]  = - x + 1.0;
 78:     /* one right? */
 79:     if (i_c*ratio != i) {
 80:       cols[nc] = idx_c[col+dof]/dof;
 81:       v[nc++]  = x;
 82:     }
 83:     MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
 84:   }
 85:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
 86:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);
 87:   MatCreateMAIJ(mat,dof,A);
 88:   MatDestroy(mat);
 89:   PetscLogFlops(5*m_f);
 90:   return(0);
 91: }


 94: /*   dof degree of freedom per node, nonperiodic */
 95: int DAGetInterpolation_2D_dof(DA dac,DA daf,Mat *A)
 96: {
 97:   int            ierr,i,j,i_start,j_start,m_f,n_f,Mx,My,*idx_f,dof;
 98:   int            m_ghost,n_ghost,*idx_c,m_ghost_c,n_ghost_c,*dnz,*onz;
 99:   int            row,col,i_start_ghost,j_start_ghost,cols[4],mx,m_c,my,nc,ratioi,ratioj;
100:   int            i_c,j_c,i_start_c,j_start_c,n_c,i_start_ghost_c,j_start_ghost_c;
101:   int            size_c,size_f,rank_f,col_shift,col_scale;
102:   Scalar         v[4],x,y;
103:   Mat            mat;
104:   DAPeriodicType pt;


108:   DAGetInfo(dac,0,&Mx,&My,0,0,0,0,0,0,&pt,0);
109:   DAGetInfo(daf,0,&mx,&my,0,0,0,0,&dof,0,0,0);
110:   if (DAXPeriodic(pt)){
111:     ratioi = mx/Mx;
112:     if (ratioi*Mx != mx) SETERRQ2(1,"Ratio between levels: mx/Mx  must be integer: mx %d Mx %d",mx,Mx);
113:   } else {
114:     ratioi = (mx-1)/(Mx-1);
115:     if (ratioi*(Mx-1) != mx-1) SETERRQ2(1,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %d Mx %d",mx,Mx);
116:   }
117:   if (DAYPeriodic(pt)){
118:     ratioj = my/My;
119:     if (ratioj*My != my) SETERRQ2(1,"Ratio between levels: my/My  must be integer: my %d My %d",my,My);
120:   } else {
121:     ratioj = (my-1)/(My-1);
122:     if (ratioj*(My-1) != my-1) SETERRQ2(1,"Ratio between levels: (my - 1)/(My - 1) must be integer: my %d My %d",my,My);
123:   }


126:   DAGetCorners(daf,&i_start,&j_start,0,&m_f,&n_f,0);
127:   DAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,0,&m_ghost,&n_ghost,0);
128:   DAGetGlobalIndices(daf,PETSC_NULL,&idx_f);

130:   DAGetCorners(dac,&i_start_c,&j_start_c,0,&m_c,&n_c,0);
131:   DAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,0,&m_ghost_c,&n_ghost_c,0);
132:   DAGetGlobalIndices(dac,PETSC_NULL,&idx_c);

134:   /*
135:      Used for handling a coarse DA that lives on 1/4 the processors of the fine DA.
136:      The coarse vector is then duplicated 4 times (each time it lives on 1/4 of the 
137:      processors). It's effective length is hence 4 times its normal length, this is
138:      why the col_scale is multiplied by the interpolation matrix column sizes.
139:      sol_shift allows each set of 1/4 processors do its own interpolation using ITS
140:      copy of the coarse vector. A bit of a hack but you do better.

142:      In the standard case when size_f == size_c col_scale == 1 and col_shift == 0
143:   */
144:   MPI_Comm_size(dac->comm,&size_c);
145:   MPI_Comm_size(daf->comm,&size_f);
146:   MPI_Comm_rank(daf->comm,&rank_f);
147:   col_scale = size_f/size_c;
148:   col_shift = Mx*My*(rank_f/size_c);

150:   MatPreallocateInitialize(daf->comm,m_f*n_f,col_scale*m_c*n_c,dnz,onz);
151:   for (j=j_start; j<j_start+n_f; j++) {
152:     for (i=i_start; i<i_start+m_f; i++) {
153:       /* convert to local "natural" numbering and then to PETSc global numbering */
154:       row    = idx_f[dof*(m_ghost*(j-j_start_ghost) + (i-i_start_ghost))]/dof;

156:       i_c = (i/ratioi);    /* coarse grid node to left of fine grid node */
157:       j_c = (j/ratioj);    /* coarse grid node below fine grid node */

159:       /* 
160:          Only include those interpolation points that are truly 
161:          nonzero. Note this is very important for final grid lines
162:          in x and y directions; since they have no right/top neighbors
163:       */
164:       nc = 0;
165:       /* one left and below; or we are right on it */
166:       col        = dof*(m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
167:       cols[nc++] = col_shift + idx_c[col]/dof;
168:       /* one right and below */
169:       if (i_c*ratioi != i) {
170:         cols[nc++] = col_shift + idx_c[col+dof]/dof;
171:       }
172:       /* one left and above */
173:       if (j_c*ratioj != j) {
174:         cols[nc++] = col_shift + idx_c[col+m_ghost_c*dof]/dof;
175:       }
176:       /* one right and above */
177:       if (j_c*ratioi != j && i_c*ratioj != i) {
178:         cols[nc++] = col_shift + idx_c[col+(m_ghost_c+1)*dof]/dof;
179:       }
180:       MatPreallocateSet(row,nc,cols,dnz,onz);
181:     }
182:   }
183:   MatCreateMPIAIJ(daf->comm,m_f*n_f,col_scale*m_c*n_c,mx*my,col_scale*Mx*My,0,dnz,0,onz,&mat);
184:   MatPreallocateFinalize(dnz,onz);
185:   MatSetOption(mat,MAT_COLUMNS_SORTED);

187:   /* loop over local fine grid nodes setting interpolation for those*/
188:   for (j=j_start; j<j_start+n_f; j++) {
189:     for (i=i_start; i<i_start+m_f; i++) {
190:       /* convert to local "natural" numbering and then to PETSc global numbering */
191:       row    = idx_f[dof*(m_ghost*(j-j_start_ghost) + (i-i_start_ghost))]/dof;

193:       i_c = (i/ratioi);    /* coarse grid node to left of fine grid node */
194:       j_c = (j/ratioj);    /* coarse grid node below fine grid node */

196:       /* 
197:          Only include those interpolation points that are truly 
198:          nonzero. Note this is very important for final grid lines
199:          in x and y directions; since they have no right/top neighbors
200:       */
201:       x  = ((double)(i - i_c*ratioi))/((double)ratioi);
202:       y  = ((double)(j - j_c*ratioj))/((double)ratioj);
203:       /* printf("i j %d %d %g %gn",i,j,x,y); */
204:       nc = 0;
205:       /* one left and below; or we are right on it */
206:       if (j_c < j_start_ghost_c) SETERRQ3(1,"Processor's coarse DA must lie over fine DAn
207:     j_start %d j_c %d j_start_ghost_c %d",j_start,j_c,j_start_ghost_c);
208:       if (i_c < i_start_ghost_c) SETERRQ3(1,"Processor's coarse DA must lie over fine DAn
209:     i_start %d i_c %d i_start_ghost_c %d",i_start,i_c,i_start_ghost_c);
210:       col      = dof*(m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
211:       cols[nc] = col_shift + idx_c[col]/dof;
212:       v[nc++]  = x*y - x - y + 1.0;
213:       /* one right and below */
214:       if (i_c*ratioi != i) {
215:         cols[nc] = col_shift + idx_c[col+dof]/dof;
216:         v[nc++]  = -x*y + x;
217:       }
218:       /* one left and above */
219:       if (j_c*ratioj != j) {
220:         cols[nc] = col_shift + idx_c[col+m_ghost_c*dof]/dof;
221:         v[nc++]  = -x*y + y;
222:       }
223:       /* one right and above */
224:       if (j_c*ratioj != j && i_c*ratioi != i) {
225:         cols[nc] = col_shift + idx_c[col+(m_ghost_c+1)*dof]/dof;
226:         v[nc++]  = x*y;
227:       }
228:       MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
229:     }
230:   }
231:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
232:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);
233:   MatCreateMAIJ(mat,dof,A);
234:   MatDestroy(mat);
235:   PetscLogFlops(13*m_f*n_f);
236:   return(0);
237: }


240: /*   dof degree of freedom per node, nonperiodic */
241: int DAGetInterpolation_3D_dof(DA dac,DA daf,Mat *A)
242: {
243:   int            ierr,i,j,i_start,j_start,m_f,n_f,Mx,My,*idx_f,dof,l;
244:   int            m_ghost,n_ghost,*idx_c,m_ghost_c,n_ghost_c,Mz,mz;
245:   int            row,col,i_start_ghost,j_start_ghost,cols[8],mx,m_c,my,nc,ratioi,ratioj,ratiok;
246:   int            i_c,j_c,i_start_c,j_start_c,n_c,i_start_ghost_c,j_start_ghost_c;
247:   int            l_start,p_f,l_start_ghost,p_ghost,l_start_c,p_c;
248:   int            l_start_ghost_c,p_ghost_c,l_c,*dnz,*onz;
249:   Scalar         v[8],x,y,z;
250:   Mat            mat;
251:   DAPeriodicType pt;

254:   DAGetInfo(dac,0,&Mx,&My,&Mz,0,0,0,0,0,&pt,0);
255:   DAGetInfo(daf,0,&mx,&my,&mz,0,0,0,&dof,0,0,0);
256:   if (DAXPeriodic(pt)){
257:     ratioi = mx/Mx;
258:     if (ratioi*Mx != mx) SETERRQ2(1,"Ratio between levels: mx/Mx  must be integer: mx %d Mx %d",mx,Mx);
259:   } else {
260:     ratioi = (mx-1)/(Mx-1);
261:     if (ratioi*(Mx-1) != mx-1) SETERRQ2(1,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %d Mx %d",mx,Mx);
262:   }
263:   if (DAYPeriodic(pt)){
264:     ratioj = my/My;
265:     if (ratioj*My != my) SETERRQ2(1,"Ratio between levels: my/My  must be integer: my %d My %d",my,My);
266:   } else {
267:     ratioj = (my-1)/(My-1);
268:     if (ratioj*(My-1) != my-1) SETERRQ2(1,"Ratio between levels: (my - 1)/(My - 1) must be integer: my %d My %d",my,My);
269:   }
270:   if (DAZPeriodic(pt)){
271:     ratiok = mz/Mz;
272:     if (ratiok*Mz != mz) SETERRQ2(1,"Ratio between levels: mz/Mz  must be integer: mz %d Mz %d",mz,Mz);
273:   } else {
274:     ratiok = (mz-1)/(Mz-1);
275:     if (ratiok*(Mz-1) != mz-1) SETERRQ2(1,"Ratio between levels: (mz - 1)/(Mz - 1) must be integer: mz %d Mz %d",mz,Mz);
276:   }

278:   DAGetCorners(daf,&i_start,&j_start,&l_start,&m_f,&n_f,&p_f);
279:   DAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,&l_start_ghost,&m_ghost,&n_ghost,&p_ghost);
280:   DAGetGlobalIndices(daf,PETSC_NULL,&idx_f);

282:   DAGetCorners(dac,&i_start_c,&j_start_c,&l_start_c,&m_c,&n_c,&p_c);
283:   DAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,&l_start_ghost_c,&m_ghost_c,&n_ghost_c,&p_ghost_c);
284:   DAGetGlobalIndices(dac,PETSC_NULL,&idx_c);

286:   /* create interpolation matrix, determining exact preallocation */
287:   MatPreallocateInitialize(dac->comm,m_f*n_f*p_f,m_c*n_c*p_c,dnz,onz);
288:   /* loop over local fine grid nodes counting interpolating points */
289:   for (l=l_start; l<l_start+p_f; l++) {
290:     for (j=j_start; j<j_start+n_f; j++) {
291:       for (i=i_start; i<i_start+m_f; i++) {
292:         /* convert to local "natural" numbering and then to PETSc global numbering */
293:         row = idx_f[dof*(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))]/dof;
294:         i_c = (i/ratioi);
295:         j_c = (j/ratioj);
296:         l_c = (l/ratiok);

298:         /* 
299:          Only include those interpolation points that are truly 
300:          nonzero. Note this is very important for final grid lines
301:          in x and y directions; since they have no right/top neighbors
302:         */
303:         nc       = 0;
304:         col      = dof*(m_ghost_c*n_ghost_c*(l_c-l_start_ghost_c) + m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
305:         cols[nc++] = idx_c[col]/dof;
306:         if (i_c*ratioi != i) {
307:           cols[nc++] = idx_c[col+dof]/dof;
308:         }
309:         if (j_c*ratioj != j) {
310:           cols[nc++] = idx_c[col+m_ghost_c*dof]/dof;
311:         }
312:         if (l_c*ratiok != l) {
313:           cols[nc++] = idx_c[col+m_ghost_c*n_ghost_c*dof]/dof;
314:         }
315:         if (j_c*ratioj != j && i_c*ratioi != i) {
316:           cols[nc++] = idx_c[col+(m_ghost_c+1)*dof]/dof;
317:         }
318:         if (j_c*ratioj != j && l_c*ratiok != l) {
319:           cols[nc++] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c)*dof]/dof;
320:         }
321:         if (i_c*ratioi != i && l_c*ratiok != l) {
322:           cols[nc++] = idx_c[col+(m_ghost_c*n_ghost_c+1)*dof]/dof;
323:         }
324:         if (i_c*ratioi != i && l_c*ratiok != l && j_c*ratioj != j) {
325:           cols[nc++] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c+1)*dof]/dof;
326:         }
327:         MatPreallocateSet(row,nc,cols,dnz,onz);
328:       }
329:     }
330:   }
331:   MatCreateMPIAIJ(dac->comm,m_f*n_f*p_f,m_c*n_c*p_c,mx*my*mz,Mx*My*Mz,0,dnz,0,onz,&mat);
332:   MatPreallocateFinalize(dnz,onz);
333:   MatSetOption(mat,MAT_COLUMNS_SORTED);

335:   /* loop over local fine grid nodes setting interpolation for those*/
336:   for (l=l_start; l<l_start+p_f; l++) {
337:     for (j=j_start; j<j_start+n_f; j++) {
338:       for (i=i_start; i<i_start+m_f; i++) {
339:         /* convert to local "natural" numbering and then to PETSc global numbering */
340:         row = idx_f[dof*(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))]/dof;

342:         i_c = (i/ratioi);
343:         j_c = (j/ratioj);
344:         l_c = (l/ratiok);

346:         /* 
347:            Only include those interpolation points that are truly 
348:            nonzero. Note this is very important for final grid lines
349:            in x and y directions; since they have no right/top neighbors
350:         */
351:         x  = ((double)(i - i_c*ratioi))/((double)ratioi);
352:         y  = ((double)(j - j_c*ratioj))/((double)ratioj);
353:         z  = ((double)(l - l_c*ratiok))/((double)ratiok);
354:         /* printf("i j l %d %d %d %g %g %gn",i,j,l,x,y,z); */
355:         nc = 0;
356:         /* one left and below; or we are right on it */
357:         col      = dof*(m_ghost_c*n_ghost_c*(l_c-l_start_ghost_c)+m_ghost_c*(j_c-j_start_ghost_c)+(i_c-i_start_ghost_c));

359:         cols[nc] = idx_c[col]/dof;
360:         v[nc++]  = .125*(1. - (2.0*x-1.))*(1. - (2.0*y-1.))*(1. - (2.0*z-1.));

362:         if (i_c*ratioi != i) {
363:           cols[nc] = idx_c[col+dof]/dof;
364:           v[nc++]  = .125*(1. + (2.0*x-1.))*(1. - (2.0*y-1.))*(1. - (2.0*z-1.));
365:         }

367:         if (j_c*ratioj != j) {
368:           cols[nc] = idx_c[col+m_ghost_c*dof]/dof;
369:           v[nc++]  = .125*(1. - (2.0*x-1.))*(1. + (2.0*y-1.))*(1. - (2.0*z-1.));
370:         }

372:         if (l_c*ratiok != l) {
373:           cols[nc] = idx_c[col+m_ghost_c*n_ghost_c*dof]/dof;
374:           v[nc++]  = .125*(1. - (2.0*x-1.))*(1. - (2.0*y-1.))*(1. + (2.0*z-1.));
375:         }

377:         if (j_c*ratioj != j && i_c*ratioi != i) {
378:           cols[nc] = idx_c[col+(m_ghost_c+1)*dof]/dof;
379:           v[nc++]  = .125*(1. + (2.0*x-1.))*(1. + (2.0*y-1.))*(1. - (2.0*z-1.));
380:         }

382:         if (j_c*ratioj != j && l_c*ratiok != l) {
383:           cols[nc] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c)*dof]/dof;
384:           v[nc++]  = .125*(1. - (2.0*x-1.))*(1. + (2.0*y-1.))*(1. + (2.0*z-1.));
385:         }

387:         if (i_c*ratioi != i && l_c*ratiok != l) {
388:           cols[nc] = idx_c[col+(m_ghost_c*n_ghost_c+1)*dof]/dof;
389:           v[nc++]  = .125*(1. + (2.0*x-1.))*(1. - (2.0*y-1.))*(1. + (2.0*z-1.));
390:         }

392:         if (i_c*ratioi != i && l_c*ratiok != l && j_c*ratioj != j) {
393:           cols[nc] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c+1)*dof]/dof;
394:           v[nc++]  = .125*(1. + (2.0*x-1.))*(1. + (2.0*y-1.))*(1. + (2.0*z-1.));
395:         }
396:         MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
397:       }
398:     }
399:   }
400:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
401:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);

403:   MatCreateMAIJ(mat,dof,A);
404:   MatDestroy(mat);
405:   PetscLogFlops(13*m_f*n_f);
406:   return(0);
407: }

409: /*@C
410:    DAGetInterpolation - Gets an interpolation matrix that maps between 
411:    grids associated with two DAs.

413:    Collective on DA

415:    Input Parameters:
416: +  dac - the coarse grid DA
417: -  daf - the fine grid DA

419:    Output Parameters:
420: +  A - the interpolation matrix
421: -  scale - a scaling vector used to scale the coarse grid restricted vector before applying the 
422:            grid function or grid Jacobian to it.

424:    Level: intermediate

426: .keywords: interpolation, restriction, multigrid 

428: .seealso: DARefine()
429: @*/
430: int DAGetInterpolation(DA dac,DA daf,Mat *A,Vec *scale)
431: {
432:   int            ierr,dimc,Mc,Nc,Pc,mc,nc,pc,dofc,sc,dimf,Mf,Nf,Pf,mf,nf,pf,doff,sf;
433:   DAPeriodicType wrapc,wrapf;
434:   DAStencilType  stc,stf;


440:   DAGetInfo(dac,&dimc,&Mc,&Nc,&Pc,&mc,&nc,&pc,&dofc,&sc,&wrapc,&stc);
441:   DAGetInfo(daf,&dimf,&Mf,&Nf,&Pf,&mf,&nf,&pf,&doff,&sf,&wrapf,&stf);
442:   if (dimc != dimf) SETERRQ2(1,"Dimensions of DA do not match %d %d",dimc,dimf);
443:   /* if (mc != mf) SETERRQ2(1,"Processor dimensions of DA in X %d %d do not match",mc,mf);
444:      if (nc != nf) SETERRQ2(1,"Processor dimensions of DA in Y %d %d do not match",nc,nf);
445:      if (pc != pf) SETERRQ2(1,"Processor dimensions of DA in Z %d %d do not match",pc,pf); */
446:   if (dofc != doff) SETERRQ2(1,"DOF of DA do not match %d %d",dofc,doff);
447:   if (sc != sf) SETERRQ2(1,"Stencil width of DA do not match %d %d",sc,sf);
448:   if (wrapc != wrapf) SETERRQ(1,"Periodic type different in two DAs");
449:   if (stc != stf) SETERRQ(1,"Stencil type different in two DAs");

451:   if (dimc == 1){
452:     DAGetInterpolation_1D_dof(dac,daf,A);
453:   } else if (dimc == 2){
454:     DAGetInterpolation_2D_dof(dac,daf,A);
455:   } else if (dimc == 3){
456:     DAGetInterpolation_3D_dof(dac,daf,A);
457:   } else {
458:     SETERRQ(1,"No support for this DA yet");
459:   }

461:   if (scale) {
462:     DMGetInterpolationScale((DM)dac,(DM)daf,*A,scale);
463:   }
464:   return(0);
465: }