Date: 2022-03-04T17:38:24+00:00

Actual source code: dainterp.c

      2: /*
      3:   Code for interpolating between grids represented by DMDAs
      4: */

      6: /*
      7:       For linear elements there are two branches of code to compute the interpolation. They should compute the same results but may not. The "new version" does
      8:    not work for periodic domains, the old does. Change NEWVERSION to 1 to compile in the new version. Eventually when we are sure the two produce identical results
      9:    we will remove/merge the new version. Based on current tests, these both produce the same results. We are leaving NEWVERSION for now in the code since some
     10:    consider it cleaner, but old version is turned on since it handles periodic case.
     11: */
     12: #define NEWVERSION 0

     14: #include <petsc/private/dmdaimpl.h>

     16: /*
     17:    Since the interpolation uses MATMAIJ for dof > 0 we convert request for non-MATAIJ baseded matrices to MATAIJ.
     18:    This is a bit of a hack, the reason for it is partially because -dm_mat_type defines the
     19:    matrix type for both the operator matrices and the interpolation matrices so that users
     20:    can select matrix types of base MATAIJ for accelerators
     21: */
     22: static PetscErrorCode ConvertToAIJ(MatType intype,MatType *outtype)
     23: {
     25:   PetscInt       i;
     26:   char           const *types[3] = {MATAIJ,MATSEQAIJ,MATMPIAIJ};
     27:   PetscBool      flg;

     30:   *outtype = MATAIJ;
     31:   for (i=0; i<3; i++) {
     32:     PetscStrbeginswith(intype,types[i],&flg);
     33:     if (flg) {
     34:       *outtype = intype;
     35:       return(0);
     36:     }
     37:   }
     38:   return(0);
     39: }

     41: PetscErrorCode DMCreateInterpolation_DA_1D_Q1(DM dac,DM daf,Mat *A)
     42: {
     43:   PetscErrorCode         ierr;
     44:   PetscInt               i,i_start,m_f,Mx;
     45:   const PetscInt         *idx_f,*idx_c;
     46:   PetscInt               m_ghost,m_ghost_c;
     47:   PetscInt               row,col,i_start_ghost,mx,m_c,nc,ratio;
     48:   PetscInt               i_c,i_start_c,i_start_ghost_c,cols[2],dof;
     49:   PetscScalar            v[2],x;
     50:   Mat                    mat;
     51:   DMBoundaryType         bx;
     52:   ISLocalToGlobalMapping ltog_f,ltog_c;
     53:   MatType                mattype;

     56:   DMDAGetInfo(dac,NULL,&Mx,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&bx,NULL,NULL,NULL);
     57:   DMDAGetInfo(daf,NULL,&mx,NULL,NULL,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
     58:   if (bx == DM_BOUNDARY_PERIODIC) {
     59:     ratio = mx/Mx;
     60:     if (ratio*Mx != mx) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mx/Mx  must be integer: mx %D Mx %D",mx,Mx);
     61:   } else {
     62:     ratio = (mx-1)/(Mx-1);
     63:     if (ratio*(Mx-1) != mx-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %D Mx %D",mx,Mx);
     64:   }

     66:   DMDAGetCorners(daf,&i_start,NULL,NULL,&m_f,NULL,NULL);
     67:   DMDAGetGhostCorners(daf,&i_start_ghost,NULL,NULL,&m_ghost,NULL,NULL);
     68:   DMGetLocalToGlobalMapping(daf,&ltog_f);
     69:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

     71:   DMDAGetCorners(dac,&i_start_c,NULL,NULL,&m_c,NULL,NULL);
     72:   DMDAGetGhostCorners(dac,&i_start_ghost_c,NULL,NULL,&m_ghost_c,NULL,NULL);
     73:   DMGetLocalToGlobalMapping(dac,&ltog_c);
     74:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

     76:   /* create interpolation matrix */
     77:   MatCreate(PetscObjectComm((PetscObject)dac),&mat);
     78: #if defined(PETSC_HAVE_CUDA)
     79:   /*
     80:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
     81:      we don't want the original unconverted matrix copied to the GPU
     82:    */
     83:   if (dof > 1) {
     84:     MatBindToCPU(mat,PETSC_TRUE);
     85:   }
     86:   #endif
     87:   MatSetSizes(mat,m_f,m_c,mx,Mx);
     88:   ConvertToAIJ(dac->mattype,&mattype);
     89:   MatSetType(mat,mattype);
     90:   MatSeqAIJSetPreallocation(mat,2,NULL);
     91:   MatMPIAIJSetPreallocation(mat,2,NULL,1,NULL);

     93:   /* loop over local fine grid nodes setting interpolation for those*/
     94:   if (!NEWVERSION) {

     96:     for (i=i_start; i<i_start+m_f; i++) {
     97:       /* convert to local "natural" numbering and then to PETSc global numbering */
     98:       row = idx_f[i-i_start_ghost];

    100:       i_c = (i/ratio);    /* coarse grid node to left of fine grid node */
    101:       if (i_c < i_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    102:                                           i_start %D i_c %D i_start_ghost_c %D",i_start,i_c,i_start_ghost_c);

    104:       /*
    105:        Only include those interpolation points that are truly
    106:        nonzero. Note this is very important for final grid lines
    107:        in x direction; since they have no right neighbor
    108:        */
    109:       x  = ((PetscReal)(i - i_c*ratio))/((PetscReal)ratio);
    110:       nc = 0;
    111:       /* one left and below; or we are right on it */
    112:       col      = (i_c-i_start_ghost_c);
    113:       cols[nc] = idx_c[col];
    114:       v[nc++]  = -x + 1.0;
    115:       /* one right? */
    116:       if (i_c*ratio != i) {
    117:         cols[nc] = idx_c[col+1];
    118:         v[nc++]  = x;
    119:       }
    120:       MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
    121:     }

    123:   } else {
    124:     PetscScalar *xi;
    125:     PetscInt    li,nxi,n;
    126:     PetscScalar Ni[2];

    128:     /* compute local coordinate arrays */
    129:     nxi  = ratio + 1;
    130:     PetscMalloc1(nxi,&xi);
    131:     for (li=0; li<nxi; li++) {
    132:       xi[li] = -1.0 + (PetscScalar)li*(2.0/(PetscScalar)(nxi-1));
    133:     }

    135:     for (i=i_start; i<i_start+m_f; i++) {
    136:       /* convert to local "natural" numbering and then to PETSc global numbering */
    137:       row = idx_f[(i-i_start_ghost)];

    139:       i_c = (i/ratio);    /* coarse grid node to left of fine grid node */
    140:       if (i_c < i_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    141:                                           i_start %D i_c %D i_start_ghost_c %D",i_start,i_c,i_start_ghost_c);

    143:       /* remainders */
    144:       li = i - ratio * (i/ratio);
    145:       if (i==mx-1) li = nxi-1;

    147:       /* corners */
    148:       col     = (i_c-i_start_ghost_c);
    149:       cols[0] = idx_c[col];
    150:       Ni[0]   = 1.0;
    151:       if ((li==0) || (li==nxi-1)) {
    152:         MatSetValue(mat,row,cols[0],Ni[0],INSERT_VALUES);
    153:         continue;
    154:       }

    156:       /* edges + interior */
    157:       /* remainders */
    158:       if (i==mx-1) i_c--;

    160:       col     = (i_c-i_start_ghost_c);
    161:       cols[0] = idx_c[col]; /* one left and below; or we are right on it */
    162:       cols[1] = idx_c[col+1];

    164:       Ni[0] = 0.5*(1.0-xi[li]);
    165:       Ni[1] = 0.5*(1.0+xi[li]);
    166:       for (n=0; n<2; n++) {
    167:         if (PetscAbsScalar(Ni[n])<1.0e-32) cols[n]=-1;
    168:       }
    169:       MatSetValues(mat,1,&row,2,cols,Ni,INSERT_VALUES);
    170:     }
    171:     PetscFree(xi);
    172:   }
    173:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    174:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);
    175:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
    176:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);
    177:   MatCreateMAIJ(mat,dof,A);
    178:   MatDestroy(&mat);
    179:   return(0);
    180: }

    182: PetscErrorCode DMCreateInterpolation_DA_1D_Q0(DM dac,DM daf,Mat *A)
    183: {
    184:   PetscErrorCode         ierr;
    185:   PetscInt               i,i_start,m_f,Mx;
    186:   const PetscInt         *idx_f,*idx_c;
    187:   ISLocalToGlobalMapping ltog_f,ltog_c;
    188:   PetscInt               m_ghost,m_ghost_c;
    189:   PetscInt               row,col,i_start_ghost,mx,m_c,nc,ratio;
    190:   PetscInt               i_c,i_start_c,i_start_ghost_c,cols[2],dof;
    191:   PetscScalar            v[2],x;
    192:   Mat                    mat;
    193:   DMBoundaryType         bx;
    194:   MatType                mattype;

    197:   DMDAGetInfo(dac,NULL,&Mx,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&bx,NULL,NULL,NULL);
    198:   DMDAGetInfo(daf,NULL,&mx,NULL,NULL,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
    199:   if (bx == DM_BOUNDARY_PERIODIC) {
    200:     if (!Mx) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be positive",Mx);
    201:     ratio = mx/Mx;
    202:     if (ratio*Mx != mx) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mx/Mx  must be integer: mx %D Mx %D",mx,Mx);
    203:   } else {
    204:     if (Mx < 2) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be greater than 1",Mx);
    205:     ratio = (mx-1)/(Mx-1);
    206:     if (ratio*(Mx-1) != mx-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %D Mx %D",mx,Mx);
    207:   }

    209:   DMDAGetCorners(daf,&i_start,NULL,NULL,&m_f,NULL,NULL);
    210:   DMDAGetGhostCorners(daf,&i_start_ghost,NULL,NULL,&m_ghost,NULL,NULL);
    211:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    212:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    214:   DMDAGetCorners(dac,&i_start_c,NULL,NULL,&m_c,NULL,NULL);
    215:   DMDAGetGhostCorners(dac,&i_start_ghost_c,NULL,NULL,&m_ghost_c,NULL,NULL);
    216:   DMGetLocalToGlobalMapping(dac,&ltog_c);
    217:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

    219:   /* create interpolation matrix */
    220:   MatCreate(PetscObjectComm((PetscObject)dac),&mat);
    221: #if defined(PETSC_HAVE_CUDA)
    222:   /*
    223:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
    224:      we don't want the original unconverted matrix copied to the GPU
    225:    */
    226:   if (dof > 1) {
    227:     MatBindToCPU(mat,PETSC_TRUE);
    228:   }
    229:   #endif
    230:   MatSetSizes(mat,m_f,m_c,mx,Mx);
    231:   ConvertToAIJ(dac->mattype,&mattype);
    232:   MatSetType(mat,mattype);
    233:   MatSeqAIJSetPreallocation(mat,2,NULL);
    234:   MatMPIAIJSetPreallocation(mat,2,NULL,0,NULL);

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

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

    243:     /*
    244:          Only include those interpolation points that are truly
    245:          nonzero. Note this is very important for final grid lines
    246:          in x direction; since they have no right neighbor
    247:     */
    248:     x  = ((PetscReal)(i - i_c*ratio))/((PetscReal)ratio);
    249:     nc = 0;
    250:     /* one left and below; or we are right on it */
    251:     col      = (i_c-i_start_ghost_c);
    252:     cols[nc] = idx_c[col];
    253:     v[nc++]  = -x + 1.0;
    254:     /* one right? */
    255:     if (i_c*ratio != i) {
    256:       cols[nc] = idx_c[col+1];
    257:       v[nc++]  = x;
    258:     }
    259:     MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
    260:   }
    261:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    262:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);
    263:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
    264:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);
    265:   MatCreateMAIJ(mat,dof,A);
    266:   MatDestroy(&mat);
    267:   PetscLogFlops(5.0*m_f);
    268:   return(0);
    269: }

    271: PetscErrorCode DMCreateInterpolation_DA_2D_Q1(DM dac,DM daf,Mat *A)
    272: {
    273:   PetscErrorCode         ierr;
    274:   PetscInt               i,j,i_start,j_start,m_f,n_f,Mx,My,dof;
    275:   const PetscInt         *idx_c,*idx_f;
    276:   ISLocalToGlobalMapping ltog_f,ltog_c;
    277:   PetscInt               m_ghost,n_ghost,m_ghost_c,n_ghost_c,*dnz,*onz;
    278:   PetscInt               row,col,i_start_ghost,j_start_ghost,cols[4],mx,m_c,my,nc,ratioi,ratioj;
    279:   PetscInt               i_c,j_c,i_start_c,j_start_c,n_c,i_start_ghost_c,j_start_ghost_c,col_shift,col_scale;
    280:   PetscMPIInt            size_c,size_f,rank_f;
    281:   PetscScalar            v[4],x,y;
    282:   Mat                    mat;
    283:   DMBoundaryType         bx,by;
    284:   MatType                mattype;

    287:   DMDAGetInfo(dac,NULL,&Mx,&My,NULL,NULL,NULL,NULL,NULL,NULL,&bx,&by,NULL,NULL);
    288:   DMDAGetInfo(daf,NULL,&mx,&my,NULL,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
    289:   if (bx == DM_BOUNDARY_PERIODIC) {
    290:     if (!Mx) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be positive",Mx);
    291:     ratioi = mx/Mx;
    292:     if (ratioi*Mx != mx) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mx/Mx  must be integer: mx %D Mx %D",mx,Mx);
    293:   } else {
    294:     if (Mx < 2) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be greater than 1",Mx);
    295:     ratioi = (mx-1)/(Mx-1);
    296:     if (ratioi*(Mx-1) != mx-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %D Mx %D",mx,Mx);
    297:   }
    298:   if (by == DM_BOUNDARY_PERIODIC) {
    299:     if (!My) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of y coarse grid points %D must be positive",My);
    300:     ratioj = my/My;
    301:     if (ratioj*My != my) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: my/My  must be integer: my %D My %D",my,My);
    302:   } else {
    303:     if (My < 2) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of y coarse grid points %D must be greater than 1",My);
    304:     ratioj = (my-1)/(My-1);
    305:     if (ratioj*(My-1) != my-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (my - 1)/(My - 1) must be integer: my %D My %D",my,My);
    306:   }

    308:   DMDAGetCorners(daf,&i_start,&j_start,NULL,&m_f,&n_f,NULL);
    309:   DMDAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,NULL,&m_ghost,&n_ghost,NULL);
    310:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    311:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    313:   DMDAGetCorners(dac,&i_start_c,&j_start_c,NULL,&m_c,&n_c,NULL);
    314:   DMDAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,NULL,&m_ghost_c,&n_ghost_c,NULL);
    315:   DMGetLocalToGlobalMapping(dac,&ltog_c);
    316:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

    318:   /*
    319:    Used for handling a coarse DMDA that lives on 1/4 the processors of the fine DMDA.
    320:    The coarse vector is then duplicated 4 times (each time it lives on 1/4 of the
    321:    processors). It's effective length is hence 4 times its normal length, this is
    322:    why the col_scale is multiplied by the interpolation matrix column sizes.
    323:    sol_shift allows each set of 1/4 processors do its own interpolation using ITS
    324:    copy of the coarse vector. A bit of a hack but you do better.

    326:    In the standard case when size_f == size_c col_scale == 1 and col_shift == 0
    327:    */
    328:   MPI_Comm_size(PetscObjectComm((PetscObject)dac),&size_c);
    329:   MPI_Comm_size(PetscObjectComm((PetscObject)daf),&size_f);
    330:   MPI_Comm_rank(PetscObjectComm((PetscObject)daf),&rank_f);
    331:   col_scale = size_f/size_c;
    332:   col_shift = Mx*My*(rank_f/size_c);

    334:   MatPreallocateInitialize(PetscObjectComm((PetscObject)daf),m_f*n_f,col_scale*m_c*n_c,dnz,onz);
    335:   for (j=j_start; j<j_start+n_f; j++) {
    336:     for (i=i_start; i<i_start+m_f; i++) {
    337:       /* convert to local "natural" numbering and then to PETSc global numbering */
    338:       row = idx_f[(m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

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

    343:       if (j_c < j_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    344:                                           j_start %D j_c %D j_start_ghost_c %D",j_start,j_c,j_start_ghost_c);
    345:       if (i_c < i_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    346:                                           i_start %D i_c %D i_start_ghost_c %D",i_start,i_c,i_start_ghost_c);

    348:       /*
    349:        Only include those interpolation points that are truly
    350:        nonzero. Note this is very important for final grid lines
    351:        in x and y directions; since they have no right/top neighbors
    352:        */
    353:       nc = 0;
    354:       /* one left and below; or we are right on it */
    355:       col        = (m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
    356:       cols[nc++] = col_shift + idx_c[col];
    357:       /* one right and below */
    358:       if (i_c*ratioi != i) cols[nc++] = col_shift + idx_c[col+1];
    359:       /* one left and above */
    360:       if (j_c*ratioj != j) cols[nc++] = col_shift + idx_c[col+m_ghost_c];
    361:       /* one right and above */
    362:       if (i_c*ratioi != i && j_c*ratioj != j) cols[nc++] = col_shift + idx_c[col+(m_ghost_c+1)];
    363:       MatPreallocateSet(row,nc,cols,dnz,onz);
    364:     }
    365:   }
    366:   MatCreate(PetscObjectComm((PetscObject)daf),&mat);
    367: #if defined(PETSC_HAVE_CUDA)
    368:   /*
    369:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
    370:      we don't want the original unconverted matrix copied to the GPU
    371:   */
    372:   if (dof > 1) {
    373:     MatBindToCPU(mat,PETSC_TRUE);
    374:   }
    375: #endif
    376:   MatSetSizes(mat,m_f*n_f,col_scale*m_c*n_c,mx*my,col_scale*Mx*My);
    377:   ConvertToAIJ(dac->mattype,&mattype);
    378:   MatSetType(mat,mattype);
    379:   MatSeqAIJSetPreallocation(mat,0,dnz);
    380:   MatMPIAIJSetPreallocation(mat,0,dnz,0,onz);
    381:   MatPreallocateFinalize(dnz,onz);

    383:   /* loop over local fine grid nodes setting interpolation for those*/
    384:   if (!NEWVERSION) {

    386:     for (j=j_start; j<j_start+n_f; j++) {
    387:       for (i=i_start; i<i_start+m_f; i++) {
    388:         /* convert to local "natural" numbering and then to PETSc global numbering */
    389:         row = idx_f[(m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

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

    394:         /*
    395:          Only include those interpolation points that are truly
    396:          nonzero. Note this is very important for final grid lines
    397:          in x and y directions; since they have no right/top neighbors
    398:          */
    399:         x = ((PetscReal)(i - i_c*ratioi))/((PetscReal)ratioi);
    400:         y = ((PetscReal)(j - j_c*ratioj))/((PetscReal)ratioj);

    402:         nc = 0;
    403:         /* one left and below; or we are right on it */
    404:         col      = (m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
    405:         cols[nc] = col_shift + idx_c[col];
    406:         v[nc++]  = x*y - x - y + 1.0;
    407:         /* one right and below */
    408:         if (i_c*ratioi != i) {
    409:           cols[nc] = col_shift + idx_c[col+1];
    410:           v[nc++]  = -x*y + x;
    411:         }
    412:         /* one left and above */
    413:         if (j_c*ratioj != j) {
    414:           cols[nc] = col_shift + idx_c[col+m_ghost_c];
    415:           v[nc++]  = -x*y + y;
    416:         }
    417:         /* one right and above */
    418:         if (j_c*ratioj != j && i_c*ratioi != i) {
    419:           cols[nc] = col_shift + idx_c[col+(m_ghost_c+1)];
    420:           v[nc++]  = x*y;
    421:         }
    422:         MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
    423:       }
    424:     }

    426:   } else {
    427:     PetscScalar Ni[4];
    428:     PetscScalar *xi,*eta;
    429:     PetscInt    li,nxi,lj,neta;

    431:     /* compute local coordinate arrays */
    432:     nxi  = ratioi + 1;
    433:     neta = ratioj + 1;
    434:     PetscMalloc1(nxi,&xi);
    435:     PetscMalloc1(neta,&eta);
    436:     for (li=0; li<nxi; li++) {
    437:       xi[li] = -1.0 + (PetscScalar)li*(2.0/(PetscScalar)(nxi-1));
    438:     }
    439:     for (lj=0; lj<neta; lj++) {
    440:       eta[lj] = -1.0 + (PetscScalar)lj*(2.0/(PetscScalar)(neta-1));
    441:     }

    443:     /* loop over local fine grid nodes setting interpolation for those*/
    444:     for (j=j_start; j<j_start+n_f; j++) {
    445:       for (i=i_start; i<i_start+m_f; i++) {
    446:         /* convert to local "natural" numbering and then to PETSc global numbering */
    447:         row = idx_f[(m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

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

    452:         /* remainders */
    453:         li = i - ratioi * (i/ratioi);
    454:         if (i==mx-1) li = nxi-1;
    455:         lj = j - ratioj * (j/ratioj);
    456:         if (j==my-1) lj = neta-1;

    458:         /* corners */
    459:         col     = (m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
    460:         cols[0] = col_shift + idx_c[col]; /* left, below */
    461:         Ni[0]   = 1.0;
    462:         if ((li==0) || (li==nxi-1)) {
    463:           if ((lj==0) || (lj==neta-1)) {
    464:             MatSetValue(mat,row,cols[0],Ni[0],INSERT_VALUES);
    465:             continue;
    466:           }
    467:         }

    469:         /* edges + interior */
    470:         /* remainders */
    471:         if (i==mx-1) i_c--;
    472:         if (j==my-1) j_c--;

    474:         col     = (m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
    475:         cols[0] = col_shift + idx_c[col]; /* left, below */
    476:         cols[1] = col_shift + idx_c[col+1]; /* right, below */
    477:         cols[2] = col_shift + idx_c[col+m_ghost_c]; /* left, above */
    478:         cols[3] = col_shift + idx_c[col+(m_ghost_c+1)]; /* right, above */

    480:         Ni[0] = 0.25*(1.0-xi[li])*(1.0-eta[lj]);
    481:         Ni[1] = 0.25*(1.0+xi[li])*(1.0-eta[lj]);
    482:         Ni[2] = 0.25*(1.0-xi[li])*(1.0+eta[lj]);
    483:         Ni[3] = 0.25*(1.0+xi[li])*(1.0+eta[lj]);

    485:         nc = 0;
    486:         if (PetscAbsScalar(Ni[0])<1.0e-32) cols[0]=-1;
    487:         if (PetscAbsScalar(Ni[1])<1.0e-32) cols[1]=-1;
    488:         if (PetscAbsScalar(Ni[2])<1.0e-32) cols[2]=-1;
    489:         if (PetscAbsScalar(Ni[3])<1.0e-32) cols[3]=-1;

    491:         MatSetValues(mat,1,&row,4,cols,Ni,INSERT_VALUES);
    492:       }
    493:     }
    494:     PetscFree(xi);
    495:     PetscFree(eta);
    496:   }
    497:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    498:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);
    499:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
    500:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);
    501:   MatCreateMAIJ(mat,dof,A);
    502:   MatDestroy(&mat);
    503:   return(0);
    504: }

    506: /*
    507:        Contributed by Andrei Draganescu <aidraga@sandia.gov>
    508: */
    509: PetscErrorCode DMCreateInterpolation_DA_2D_Q0(DM dac,DM daf,Mat *A)
    510: {
    511:   PetscErrorCode         ierr;
    512:   PetscInt               i,j,i_start,j_start,m_f,n_f,Mx,My,dof;
    513:   const PetscInt         *idx_c,*idx_f;
    514:   ISLocalToGlobalMapping ltog_f,ltog_c;
    515:   PetscInt               m_ghost,n_ghost,m_ghost_c,n_ghost_c,*dnz,*onz;
    516:   PetscInt               row,col,i_start_ghost,j_start_ghost,cols[4],mx,m_c,my,nc,ratioi,ratioj;
    517:   PetscInt               i_c,j_c,i_start_c,j_start_c,n_c,i_start_ghost_c,j_start_ghost_c,col_shift,col_scale;
    518:   PetscMPIInt            size_c,size_f,rank_f;
    519:   PetscScalar            v[4];
    520:   Mat                    mat;
    521:   DMBoundaryType         bx,by;
    522:   MatType                mattype;

    525:   DMDAGetInfo(dac,NULL,&Mx,&My,NULL,NULL,NULL,NULL,NULL,NULL,&bx,&by,NULL,NULL);
    526:   DMDAGetInfo(daf,NULL,&mx,&my,NULL,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
    527:   if (!Mx) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be positive",Mx);
    528:   if (!My) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of y coarse grid points %D must be positive",My);
    529:   ratioi = mx/Mx;
    530:   ratioj = my/My;
    531:   if (ratioi*Mx != mx) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Fine grid points must be multiple of coarse grid points in x");
    532:   if (ratioj*My != my) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Fine grid points must be multiple of coarse grid points in y");
    533:   if (ratioi != 2) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Coarsening factor in x must be 2");
    534:   if (ratioj != 2) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Coarsening factor in y must be 2");

    536:   DMDAGetCorners(daf,&i_start,&j_start,NULL,&m_f,&n_f,NULL);
    537:   DMDAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,NULL,&m_ghost,&n_ghost,NULL);
    538:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    539:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    541:   DMDAGetCorners(dac,&i_start_c,&j_start_c,NULL,&m_c,&n_c,NULL);
    542:   DMDAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,NULL,&m_ghost_c,&n_ghost_c,NULL);
    543:   DMGetLocalToGlobalMapping(dac,&ltog_c);
    544:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

    546:   /*
    547:      Used for handling a coarse DMDA that lives on 1/4 the processors of the fine DMDA.
    548:      The coarse vector is then duplicated 4 times (each time it lives on 1/4 of the
    549:      processors). It's effective length is hence 4 times its normal length, this is
    550:      why the col_scale is multiplied by the interpolation matrix column sizes.
    551:      sol_shift allows each set of 1/4 processors do its own interpolation using ITS
    552:      copy of the coarse vector. A bit of a hack but you do better.

    554:      In the standard case when size_f == size_c col_scale == 1 and col_shift == 0
    555:   */
    556:   MPI_Comm_size(PetscObjectComm((PetscObject)dac),&size_c);
    557:   MPI_Comm_size(PetscObjectComm((PetscObject)daf),&size_f);
    558:   MPI_Comm_rank(PetscObjectComm((PetscObject)daf),&rank_f);
    559:   col_scale = size_f/size_c;
    560:   col_shift = Mx*My*(rank_f/size_c);

    562:   MatPreallocateInitialize(PetscObjectComm((PetscObject)daf),m_f*n_f,col_scale*m_c*n_c,dnz,onz);
    563:   for (j=j_start; j<j_start+n_f; j++) {
    564:     for (i=i_start; i<i_start+m_f; i++) {
    565:       /* convert to local "natural" numbering and then to PETSc global numbering */
    566:       row = idx_f[(m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

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

    571:       if (j_c < j_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    572:     j_start %D j_c %D j_start_ghost_c %D",j_start,j_c,j_start_ghost_c);
    573:       if (i_c < i_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    574:     i_start %D i_c %D i_start_ghost_c %D",i_start,i_c,i_start_ghost_c);

    576:       /*
    577:          Only include those interpolation points that are truly
    578:          nonzero. Note this is very important for final grid lines
    579:          in x and y directions; since they have no right/top neighbors
    580:       */
    581:       nc = 0;
    582:       /* one left and below; or we are right on it */
    583:       col        = (m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
    584:       cols[nc++] = col_shift + idx_c[col];
    585:       MatPreallocateSet(row,nc,cols,dnz,onz);
    586:     }
    587:   }
    588:   MatCreate(PetscObjectComm((PetscObject)daf),&mat);
    589: #if defined(PETSC_HAVE_CUDA)
    590:   /*
    591:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
    592:      we don't want the original unconverted matrix copied to the GPU
    593:   */
    594:   if (dof > 1) {
    595:     MatBindToCPU(mat,PETSC_TRUE);
    596:   }
    597:   #endif
    598:   MatSetSizes(mat,m_f*n_f,col_scale*m_c*n_c,mx*my,col_scale*Mx*My);
    599:   ConvertToAIJ(dac->mattype,&mattype);
    600:   MatSetType(mat,mattype);
    601:   MatSeqAIJSetPreallocation(mat,0,dnz);
    602:   MatMPIAIJSetPreallocation(mat,0,dnz,0,onz);
    603:   MatPreallocateFinalize(dnz,onz);

    605:   /* loop over local fine grid nodes setting interpolation for those*/
    606:   for (j=j_start; j<j_start+n_f; j++) {
    607:     for (i=i_start; i<i_start+m_f; i++) {
    608:       /* convert to local "natural" numbering and then to PETSc global numbering */
    609:       row = idx_f[(m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

    611:       i_c = (i/ratioi);    /* coarse grid node to left of fine grid node */
    612:       j_c = (j/ratioj);    /* coarse grid node below fine grid node */
    613:       nc  = 0;
    614:       /* one left and below; or we are right on it */
    615:       col      = (m_ghost_c*(j_c-j_start_ghost_c) + (i_c-i_start_ghost_c));
    616:       cols[nc] = col_shift + idx_c[col];
    617:       v[nc++]  = 1.0;

    619:       MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
    620:     }
    621:   }
    622:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    623:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);
    624:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
    625:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);
    626:   MatCreateMAIJ(mat,dof,A);
    627:   MatDestroy(&mat);
    628:   PetscLogFlops(13.0*m_f*n_f);
    629:   return(0);
    630: }

    632: /*
    633:        Contributed by Jianming Yang <jianming-yang@uiowa.edu>
    634: */
    635: PetscErrorCode DMCreateInterpolation_DA_3D_Q0(DM dac,DM daf,Mat *A)
    636: {
    637:   PetscErrorCode         ierr;
    638:   PetscInt               i,j,l,i_start,j_start,l_start,m_f,n_f,p_f,Mx,My,Mz,dof;
    639:   const PetscInt         *idx_c,*idx_f;
    640:   ISLocalToGlobalMapping ltog_f,ltog_c;
    641:   PetscInt               m_ghost,n_ghost,p_ghost,m_ghost_c,n_ghost_c,p_ghost_c,nc,*dnz,*onz;
    642:   PetscInt               row,col,i_start_ghost,j_start_ghost,l_start_ghost,cols[8],mx,m_c,my,n_c,mz,p_c,ratioi,ratioj,ratiol;
    643:   PetscInt               i_c,j_c,l_c,i_start_c,j_start_c,l_start_c,i_start_ghost_c,j_start_ghost_c,l_start_ghost_c,col_shift,col_scale;
    644:   PetscMPIInt            size_c,size_f,rank_f;
    645:   PetscScalar            v[8];
    646:   Mat                    mat;
    647:   DMBoundaryType         bx,by,bz;
    648:   MatType                mattype;

    651:   DMDAGetInfo(dac,NULL,&Mx,&My,&Mz,NULL,NULL,NULL,NULL,NULL,&bx,&by,&bz,NULL);
    652:   if (!Mx) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be positive",Mx);
    653:   if (!My) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of y coarse grid points %D must be positive",My);
    654:   if (!Mz) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of z coarse grid points %D must be positive",Mz);
    655:   DMDAGetInfo(daf,NULL,&mx,&my,&mz,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
    656:   ratioi = mx/Mx;
    657:   ratioj = my/My;
    658:   ratiol = mz/Mz;
    659:   if (ratioi*Mx != mx) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Fine grid points must be multiple of coarse grid points in x");
    660:   if (ratioj*My != my) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Fine grid points must be multiple of coarse grid points in y");
    661:   if (ratiol*Mz != mz) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Fine grid points must be multiple of coarse grid points in z");
    662:   if (ratioi != 2 && ratioi != 1) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Coarsening factor in x must be 1 or 2");
    663:   if (ratioj != 2 && ratioj != 1) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Coarsening factor in y must be 1 or 2");
    664:   if (ratiol != 2 && ratiol != 1) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_WRONG,"Coarsening factor in z must be 1 or 2");

    666:   DMDAGetCorners(daf,&i_start,&j_start,&l_start,&m_f,&n_f,&p_f);
    667:   DMDAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,&l_start_ghost,&m_ghost,&n_ghost,&p_ghost);
    668:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    669:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    671:   DMDAGetCorners(dac,&i_start_c,&j_start_c,&l_start_c,&m_c,&n_c,&p_c);
    672:   DMDAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,&l_start_ghost_c,&m_ghost_c,&n_ghost_c,&p_ghost_c);
    673:   DMGetLocalToGlobalMapping(dac,&ltog_c);
    674:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

    676:   /*
    677:      Used for handling a coarse DMDA that lives on 1/4 the processors of the fine DMDA.
    678:      The coarse vector is then duplicated 4 times (each time it lives on 1/4 of the
    679:      processors). It's effective length is hence 4 times its normal length, this is
    680:      why the col_scale is multiplied by the interpolation matrix column sizes.
    681:      sol_shift allows each set of 1/4 processors do its own interpolation using ITS
    682:      copy of the coarse vector. A bit of a hack but you do better.

    684:      In the standard case when size_f == size_c col_scale == 1 and col_shift == 0
    685:   */
    686:   MPI_Comm_size(PetscObjectComm((PetscObject)dac),&size_c);
    687:   MPI_Comm_size(PetscObjectComm((PetscObject)daf),&size_f);
    688:   MPI_Comm_rank(PetscObjectComm((PetscObject)daf),&rank_f);
    689:   col_scale = size_f/size_c;
    690:   col_shift = Mx*My*Mz*(rank_f/size_c);

    692:   MatPreallocateInitialize(PetscObjectComm((PetscObject)daf),m_f*n_f*p_f,col_scale*m_c*n_c*p_c,dnz,onz);
    693:   for (l=l_start; l<l_start+p_f; l++) {
    694:     for (j=j_start; j<j_start+n_f; j++) {
    695:       for (i=i_start; i<i_start+m_f; i++) {
    696:         /* convert to local "natural" numbering and then to PETSc global numbering */
    697:         row = idx_f[(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

    699:         i_c = (i/ratioi);    /* coarse grid node to left of fine grid node */
    700:         j_c = (j/ratioj);    /* coarse grid node below fine grid node */
    701:         l_c = (l/ratiol);

    703:         if (l_c < l_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    704:     l_start %D l_c %D l_start_ghost_c %D",l_start,l_c,l_start_ghost_c);
    705:         if (j_c < j_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    706:     j_start %D j_c %D j_start_ghost_c %D",j_start,j_c,j_start_ghost_c);
    707:         if (i_c < i_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    708:     i_start %D i_c %D i_start_ghost_c %D",i_start,i_c,i_start_ghost_c);

    710:         /*
    711:            Only include those interpolation points that are truly
    712:            nonzero. Note this is very important for final grid lines
    713:            in x and y directions; since they have no right/top neighbors
    714:         */
    715:         nc = 0;
    716:         /* one left and below; or we are right on it */
    717:         col        = (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));
    718:         cols[nc++] = col_shift + idx_c[col];
    719:         MatPreallocateSet(row,nc,cols,dnz,onz);
    720:       }
    721:     }
    722:   }
    723:   MatCreate(PetscObjectComm((PetscObject)daf),&mat);
    724: #if defined(PETSC_HAVE_CUDA)
    725:   /*
    726:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
    727:      we don't want the original unconverted matrix copied to the GPU
    728:   */
    729:   if (dof > 1) {
    730:     MatBindToCPU(mat,PETSC_TRUE);
    731:   }
    732:   #endif
    733:   MatSetSizes(mat,m_f*n_f*p_f,col_scale*m_c*n_c*p_c,mx*my*mz,col_scale*Mx*My*Mz);
    734:   ConvertToAIJ(dac->mattype,&mattype);
    735:   MatSetType(mat,mattype);
    736:   MatSeqAIJSetPreallocation(mat,0,dnz);
    737:   MatMPIAIJSetPreallocation(mat,0,dnz,0,onz);
    738:   MatPreallocateFinalize(dnz,onz);

    740:   /* loop over local fine grid nodes setting interpolation for those*/
    741:   for (l=l_start; l<l_start+p_f; l++) {
    742:     for (j=j_start; j<j_start+n_f; j++) {
    743:       for (i=i_start; i<i_start+m_f; i++) {
    744:         /* convert to local "natural" numbering and then to PETSc global numbering */
    745:         row = idx_f[(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

    747:         i_c = (i/ratioi);    /* coarse grid node to left of fine grid node */
    748:         j_c = (j/ratioj);    /* coarse grid node below fine grid node */
    749:         l_c = (l/ratiol);
    750:         nc  = 0;
    751:         /* one left and below; or we are right on it */
    752:         col      = (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));
    753:         cols[nc] = col_shift + idx_c[col];
    754:         v[nc++]  = 1.0;

    756:         MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
    757:       }
    758:     }
    759:   }
    760:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    761:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);
    762:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
    763:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);
    764:   MatCreateMAIJ(mat,dof,A);
    765:   MatDestroy(&mat);
    766:   PetscLogFlops(13.0*m_f*n_f*p_f);
    767:   return(0);
    768: }

    770: PetscErrorCode DMCreateInterpolation_DA_3D_Q1(DM dac,DM daf,Mat *A)
    771: {
    772:   PetscErrorCode         ierr;
    773:   PetscInt               i,j,i_start,j_start,m_f,n_f,Mx,My,dof,l;
    774:   const PetscInt         *idx_c,*idx_f;
    775:   ISLocalToGlobalMapping ltog_f,ltog_c;
    776:   PetscInt               m_ghost,n_ghost,m_ghost_c,n_ghost_c,Mz,mz;
    777:   PetscInt               row,col,i_start_ghost,j_start_ghost,cols[8],mx,m_c,my,nc,ratioi,ratioj,ratiok;
    778:   PetscInt               i_c,j_c,i_start_c,j_start_c,n_c,i_start_ghost_c,j_start_ghost_c;
    779:   PetscInt               l_start,p_f,l_start_ghost,p_ghost,l_start_c,p_c;
    780:   PetscInt               l_start_ghost_c,p_ghost_c,l_c,*dnz,*onz;
    781:   PetscScalar            v[8],x,y,z;
    782:   Mat                    mat;
    783:   DMBoundaryType         bx,by,bz;
    784:   MatType                mattype;

    787:   DMDAGetInfo(dac,NULL,&Mx,&My,&Mz,NULL,NULL,NULL,NULL,NULL,&bx,&by,&bz,NULL);
    788:   DMDAGetInfo(daf,NULL,&mx,&my,&mz,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
    789:   if (mx == Mx) {
    790:     ratioi = 1;
    791:   } else if (bx == DM_BOUNDARY_PERIODIC) {
    792:     if (!Mx) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be positive",Mx);
    793:     ratioi = mx/Mx;
    794:     if (ratioi*Mx != mx) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mx/Mx  must be integer: mx %D Mx %D",mx,Mx);
    795:   } else {
    796:     if (Mx < 2) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of x coarse grid points %D must be greater than 1",Mx);
    797:     ratioi = (mx-1)/(Mx-1);
    798:     if (ratioi*(Mx-1) != mx-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %D Mx %D",mx,Mx);
    799:   }
    800:   if (my == My) {
    801:     ratioj = 1;
    802:   } else if (by == DM_BOUNDARY_PERIODIC) {
    803:     if (!My) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of y coarse grid points %D must be positive",My);
    804:     ratioj = my/My;
    805:     if (ratioj*My != my) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: my/My  must be integer: my %D My %D",my,My);
    806:   } else {
    807:     if (My < 2) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of y coarse grid points %D must be greater than 1",My);
    808:     ratioj = (my-1)/(My-1);
    809:     if (ratioj*(My-1) != my-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (my - 1)/(My - 1) must be integer: my %D My %D",my,My);
    810:   }
    811:   if (mz == Mz) {
    812:     ratiok = 1;
    813:   } else if (bz == DM_BOUNDARY_PERIODIC) {
    814:     if (!Mz) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of z coarse grid points %D must be positive",Mz);
    815:     ratiok = mz/Mz;
    816:     if (ratiok*Mz != mz) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mz/Mz  must be integer: mz %D Mz %D",mz,Mz);
    817:   } else {
    818:     if (Mz < 2) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Number of z coarse grid points %D must be greater than 1",Mz);
    819:     ratiok = (mz-1)/(Mz-1);
    820:     if (ratiok*(Mz-1) != mz-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mz - 1)/(Mz - 1) must be integer: mz %D Mz %D",mz,Mz);
    821:   }

    823:   DMDAGetCorners(daf,&i_start,&j_start,&l_start,&m_f,&n_f,&p_f);
    824:   DMDAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,&l_start_ghost,&m_ghost,&n_ghost,&p_ghost);
    825:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    826:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    828:   DMDAGetCorners(dac,&i_start_c,&j_start_c,&l_start_c,&m_c,&n_c,&p_c);
    829:   DMDAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,&l_start_ghost_c,&m_ghost_c,&n_ghost_c,&p_ghost_c);
    830:   DMGetLocalToGlobalMapping(dac,&ltog_c);
    831:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

    833:   /* create interpolation matrix, determining exact preallocation */
    834:   MatPreallocateInitialize(PetscObjectComm((PetscObject)dac),m_f*n_f*p_f,m_c*n_c*p_c,dnz,onz);
    835:   /* loop over local fine grid nodes counting interpolating points */
    836:   for (l=l_start; l<l_start+p_f; l++) {
    837:     for (j=j_start; j<j_start+n_f; j++) {
    838:       for (i=i_start; i<i_start+m_f; i++) {
    839:         /* convert to local "natural" numbering and then to PETSc global numbering */
    840:         row = idx_f[(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];
    841:         i_c = (i/ratioi);
    842:         j_c = (j/ratioj);
    843:         l_c = (l/ratiok);
    844:         if (l_c < l_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    845:                                             l_start %D l_c %D l_start_ghost_c %D",l_start,l_c,l_start_ghost_c);
    846:         if (j_c < j_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    847:                                             j_start %D j_c %D j_start_ghost_c %D",j_start,j_c,j_start_ghost_c);
    848:         if (i_c < i_start_ghost_c) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    849:                                             i_start %D i_c %D i_start_ghost_c %D",i_start,i_c,i_start_ghost_c);

    851:         /*
    852:          Only include those interpolation points that are truly
    853:          nonzero. Note this is very important for final grid lines
    854:          in x and y directions; since they have no right/top neighbors
    855:          */
    856:         nc         = 0;
    857:         col        = (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));
    858:         cols[nc++] = idx_c[col];
    859:         if (i_c*ratioi != i) {
    860:           cols[nc++] = idx_c[col+1];
    861:         }
    862:         if (j_c*ratioj != j) {
    863:           cols[nc++] = idx_c[col+m_ghost_c];
    864:         }
    865:         if (l_c*ratiok != l) {
    866:           cols[nc++] = idx_c[col+m_ghost_c*n_ghost_c];
    867:         }
    868:         if (j_c*ratioj != j && i_c*ratioi != i) {
    869:           cols[nc++] = idx_c[col+(m_ghost_c+1)];
    870:         }
    871:         if (j_c*ratioj != j && l_c*ratiok != l) {
    872:           cols[nc++] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c)];
    873:         }
    874:         if (i_c*ratioi != i && l_c*ratiok != l) {
    875:           cols[nc++] = idx_c[col+(m_ghost_c*n_ghost_c+1)];
    876:         }
    877:         if (i_c*ratioi != i && l_c*ratiok != l && j_c*ratioj != j) {
    878:           cols[nc++] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c+1)];
    879:         }
    880:         MatPreallocateSet(row,nc,cols,dnz,onz);
    881:       }
    882:     }
    883:   }
    884:   MatCreate(PetscObjectComm((PetscObject)dac),&mat);
    885: #if defined(PETSC_HAVE_CUDA)
    886:   /*
    887:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
    888:      we don't want the original unconverted matrix copied to the GPU
    889:   */
    890:   if (dof > 1) {
    891:     MatBindToCPU(mat,PETSC_TRUE);
    892:   }
    893:   #endif
    894:   MatSetSizes(mat,m_f*n_f*p_f,m_c*n_c*p_c,mx*my*mz,Mx*My*Mz);
    895:   ConvertToAIJ(dac->mattype,&mattype);
    896:   MatSetType(mat,mattype);
    897:   MatSeqAIJSetPreallocation(mat,0,dnz);
    898:   MatMPIAIJSetPreallocation(mat,0,dnz,0,onz);
    899:   MatPreallocateFinalize(dnz,onz);

    901:   /* loop over local fine grid nodes setting interpolation for those*/
    902:   if (!NEWVERSION) {

    904:     for (l=l_start; l<l_start+p_f; l++) {
    905:       for (j=j_start; j<j_start+n_f; j++) {
    906:         for (i=i_start; i<i_start+m_f; i++) {
    907:           /* convert to local "natural" numbering and then to PETSc global numbering */
    908:           row = idx_f[(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

    910:           i_c = (i/ratioi);
    911:           j_c = (j/ratioj);
    912:           l_c = (l/ratiok);

    914:           /*
    915:            Only include those interpolation points that are truly
    916:            nonzero. Note this is very important for final grid lines
    917:            in x and y directions; since they have no right/top neighbors
    918:            */
    919:           x = ((PetscReal)(i - i_c*ratioi))/((PetscReal)ratioi);
    920:           y = ((PetscReal)(j - j_c*ratioj))/((PetscReal)ratioj);
    921:           z = ((PetscReal)(l - l_c*ratiok))/((PetscReal)ratiok);

    923:           nc = 0;
    924:           /* one left and below; or we are right on it */
    925:           col = (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));

    927:           cols[nc] = idx_c[col];
    928:           v[nc++]  = .125*(1. - (2.0*x-1.))*(1. - (2.0*y-1.))*(1. - (2.0*z-1.));

    930:           if (i_c*ratioi != i) {
    931:             cols[nc] = idx_c[col+1];
    932:             v[nc++]  = .125*(1. + (2.0*x-1.))*(1. - (2.0*y-1.))*(1. - (2.0*z-1.));
    933:           }

    935:           if (j_c*ratioj != j) {
    936:             cols[nc] = idx_c[col+m_ghost_c];
    937:             v[nc++]  = .125*(1. - (2.0*x-1.))*(1. + (2.0*y-1.))*(1. - (2.0*z-1.));
    938:           }

    940:           if (l_c*ratiok != l) {
    941:             cols[nc] = idx_c[col+m_ghost_c*n_ghost_c];
    942:             v[nc++]  = .125*(1. - (2.0*x-1.))*(1. - (2.0*y-1.))*(1. + (2.0*z-1.));
    943:           }

    945:           if (j_c*ratioj != j && i_c*ratioi != i) {
    946:             cols[nc] = idx_c[col+(m_ghost_c+1)];
    947:             v[nc++]  = .125*(1. + (2.0*x-1.))*(1. + (2.0*y-1.))*(1. - (2.0*z-1.));
    948:           }

    950:           if (j_c*ratioj != j && l_c*ratiok != l) {
    951:             cols[nc] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c)];
    952:             v[nc++]  = .125*(1. - (2.0*x-1.))*(1. + (2.0*y-1.))*(1. + (2.0*z-1.));
    953:           }

    955:           if (i_c*ratioi != i && l_c*ratiok != l) {
    956:             cols[nc] = idx_c[col+(m_ghost_c*n_ghost_c+1)];
    957:             v[nc++]  = .125*(1. + (2.0*x-1.))*(1. - (2.0*y-1.))*(1. + (2.0*z-1.));
    958:           }

    960:           if (i_c*ratioi != i && l_c*ratiok != l && j_c*ratioj != j) {
    961:             cols[nc] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c+1)];
    962:             v[nc++]  = .125*(1. + (2.0*x-1.))*(1. + (2.0*y-1.))*(1. + (2.0*z-1.));
    963:           }
    964:           MatSetValues(mat,1,&row,nc,cols,v,INSERT_VALUES);
    965:         }
    966:       }
    967:     }

    969:   } else {
    970:     PetscScalar *xi,*eta,*zeta;
    971:     PetscInt    li,nxi,lj,neta,lk,nzeta,n;
    972:     PetscScalar Ni[8];

    974:     /* compute local coordinate arrays */
    975:     nxi   = ratioi + 1;
    976:     neta  = ratioj + 1;
    977:     nzeta = ratiok + 1;
    978:     PetscMalloc1(nxi,&xi);
    979:     PetscMalloc1(neta,&eta);
    980:     PetscMalloc1(nzeta,&zeta);
    981:     for (li=0; li<nxi; li++) xi[li] = -1.0 + (PetscScalar)li*(2.0/(PetscScalar)(nxi-1));
    982:     for (lj=0; lj<neta; lj++) eta[lj] = -1.0 + (PetscScalar)lj*(2.0/(PetscScalar)(neta-1));
    983:     for (lk=0; lk<nzeta; lk++) zeta[lk] = -1.0 + (PetscScalar)lk*(2.0/(PetscScalar)(nzeta-1));

    985:     for (l=l_start; l<l_start+p_f; l++) {
    986:       for (j=j_start; j<j_start+n_f; j++) {
    987:         for (i=i_start; i<i_start+m_f; i++) {
    988:           /* convert to local "natural" numbering and then to PETSc global numbering */
    989:           row = idx_f[(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))];

    991:           i_c = (i/ratioi);
    992:           j_c = (j/ratioj);
    993:           l_c = (l/ratiok);

    995:           /* remainders */
    996:           li = i - ratioi * (i/ratioi);
    997:           if (i==mx-1) li = nxi-1;
    998:           lj = j - ratioj * (j/ratioj);
    999:           if (j==my-1) lj = neta-1;
    1000:           lk = l - ratiok * (l/ratiok);
    1001:           if (l==mz-1) lk = nzeta-1;

    1003:           /* corners */
    1004:           col     = (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));
    1005:           cols[0] = idx_c[col];
    1006:           Ni[0]   = 1.0;
    1007:           if ((li==0) || (li==nxi-1)) {
    1008:             if ((lj==0) || (lj==neta-1)) {
    1009:               if ((lk==0) || (lk==nzeta-1)) {
    1010:                 MatSetValue(mat,row,cols[0],Ni[0],INSERT_VALUES);
    1011:                 continue;
    1012:               }
    1013:             }
    1014:           }

    1016:           /* edges + interior */
    1017:           /* remainders */
    1018:           if (i==mx-1) i_c--;
    1019:           if (j==my-1) j_c--;
    1020:           if (l==mz-1) l_c--;

    1022:           col     = (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));
    1023:           cols[0] = idx_c[col]; /* one left and below; or we are right on it */
    1024:           cols[1] = idx_c[col+1]; /* one right and below */
    1025:           cols[2] = idx_c[col+m_ghost_c];  /* one left and above */
    1026:           cols[3] = idx_c[col+(m_ghost_c+1)]; /* one right and above */

    1028:           cols[4] = idx_c[col+m_ghost_c*n_ghost_c]; /* one left and below and front; or we are right on it */
    1029:           cols[5] = idx_c[col+(m_ghost_c*n_ghost_c+1)]; /* one right and below, and front */
    1030:           cols[6] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c)]; /* one left and above and front*/
    1031:           cols[7] = idx_c[col+(m_ghost_c*n_ghost_c+m_ghost_c+1)]; /* one right and above and front */

    1033:           Ni[0] = 0.125*(1.0-xi[li])*(1.0-eta[lj])*(1.0-zeta[lk]);
    1034:           Ni[1] = 0.125*(1.0+xi[li])*(1.0-eta[lj])*(1.0-zeta[lk]);
    1035:           Ni[2] = 0.125*(1.0-xi[li])*(1.0+eta[lj])*(1.0-zeta[lk]);
    1036:           Ni[3] = 0.125*(1.0+xi[li])*(1.0+eta[lj])*(1.0-zeta[lk]);

    1038:           Ni[4] = 0.125*(1.0-xi[li])*(1.0-eta[lj])*(1.0+zeta[lk]);
    1039:           Ni[5] = 0.125*(1.0+xi[li])*(1.0-eta[lj])*(1.0+zeta[lk]);
    1040:           Ni[6] = 0.125*(1.0-xi[li])*(1.0+eta[lj])*(1.0+zeta[lk]);
    1041:           Ni[7] = 0.125*(1.0+xi[li])*(1.0+eta[lj])*(1.0+zeta[lk]);

    1043:           for (n=0; n<8; n++) {
    1044:             if (PetscAbsScalar(Ni[n])<1.0e-32) cols[n]=-1;
    1045:           }
    1046:           MatSetValues(mat,1,&row,8,cols,Ni,INSERT_VALUES);

    1048:         }
    1049:       }
    1050:     }
    1051:     PetscFree(xi);
    1052:     PetscFree(eta);
    1053:     PetscFree(zeta);
    1054:   }
    1055:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    1056:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);
    1057:   MatAssemblyBegin(mat,MAT_FINAL_ASSEMBLY);
    1058:   MatAssemblyEnd(mat,MAT_FINAL_ASSEMBLY);

    1060:   MatCreateMAIJ(mat,dof,A);
    1061:   MatDestroy(&mat);
    1062:   return(0);
    1063: }

    1065: PetscErrorCode  DMCreateInterpolation_DA(DM dac,DM daf,Mat *A,Vec *scale)
    1066: {
    1067:   PetscErrorCode   ierr;
    1068:   PetscInt         dimc,Mc,Nc,Pc,mc,nc,pc,dofc,sc,dimf,Mf,Nf,Pf,mf,nf,pf,doff,sf;
    1069:   DMBoundaryType   bxc,byc,bzc,bxf,byf,bzf;
    1070:   DMDAStencilType  stc,stf;
    1071:   DM_DA            *ddc = (DM_DA*)dac->data;

    1079:   DMDAGetInfo(dac,&dimc,&Mc,&Nc,&Pc,&mc,&nc,&pc,&dofc,&sc,&bxc,&byc,&bzc,&stc);
    1080:   DMDAGetInfo(daf,&dimf,&Mf,&Nf,&Pf,&mf,&nf,&pf,&doff,&sf,&bxf,&byf,&bzf,&stf);
    1081:   if (dimc != dimf) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Dimensions of DMDA do not match %D %D",dimc,dimf);
    1082:   if (dofc != doff) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"DOF of DMDA do not match %D %D",dofc,doff);
    1083:   if (sc != sf) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Stencil width of DMDA do not match %D %D",sc,sf);
    1084:   if (bxc != bxf || byc != byf || bzc != bzf) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Boundary type different in two DMDAs");
    1085:   if (stc != stf) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Stencil type different in two DMDAs");
    1086:   if (Mc < 2 && Mf > 1) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Coarse grid requires at least 2 points in x direction");
    1087:   if (dimc > 1 && Nc < 2 && Nf > 1) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Coarse grid requires at least 2 points in y direction");
    1088:   if (dimc > 2 && Pc < 2 && Pf > 1) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Coarse grid requires at least 2 points in z direction");

    1090:   if (ddc->interptype == DMDA_Q1) {
    1091:     if (dimc == 1) {
    1092:       DMCreateInterpolation_DA_1D_Q1(dac,daf,A);
    1093:     } else if (dimc == 2) {
    1094:       DMCreateInterpolation_DA_2D_Q1(dac,daf,A);
    1095:     } else if (dimc == 3) {
    1096:       DMCreateInterpolation_DA_3D_Q1(dac,daf,A);
    1097:     } else SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_SUP,"No support for this DMDA dimension %D for interpolation type %d",dimc,(int)ddc->interptype);
    1098:   } else if (ddc->interptype == DMDA_Q0) {
    1099:     if (dimc == 1) {
    1100:       DMCreateInterpolation_DA_1D_Q0(dac,daf,A);
    1101:     } else if (dimc == 2) {
    1102:       DMCreateInterpolation_DA_2D_Q0(dac,daf,A);
    1103:     } else if (dimc == 3) {
    1104:       DMCreateInterpolation_DA_3D_Q0(dac,daf,A);
    1105:     } else SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_SUP,"No support for this DMDA dimension %D for interpolation type %d",dimc,(int)ddc->interptype);
    1106:   }
    1107:   if (scale) {
    1108:     DMCreateInterpolationScale((DM)dac,(DM)daf,*A,scale);
    1109:   }
    1110:   return(0);
    1111: }

    1113: PetscErrorCode DMCreateInjection_DA_1D(DM dac,DM daf,VecScatter *inject)
    1114: {
    1115:   PetscErrorCode         ierr;
    1116:   PetscInt               i,i_start,m_f,Mx,dof;
    1117:   const PetscInt         *idx_f;
    1118:   ISLocalToGlobalMapping ltog_f;
    1119:   PetscInt               m_ghost,m_ghost_c;
    1120:   PetscInt               row,i_start_ghost,mx,m_c,nc,ratioi;
    1121:   PetscInt               i_start_c,i_start_ghost_c;
    1122:   PetscInt               *cols;
    1123:   DMBoundaryType         bx;
    1124:   Vec                    vecf,vecc;
    1125:   IS                     isf;

    1128:   DMDAGetInfo(dac,NULL,&Mx,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&bx,NULL,NULL,NULL);
    1129:   DMDAGetInfo(daf,NULL,&mx,NULL,NULL,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
    1130:   if (bx == DM_BOUNDARY_PERIODIC) {
    1131:     ratioi = mx/Mx;
    1132:     if (ratioi*Mx != mx) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mx/Mx  must be integer: mx %D Mx %D",mx,Mx);
    1133:   } else {
    1134:     ratioi = (mx-1)/(Mx-1);
    1135:     if (ratioi*(Mx-1) != mx-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %D Mx %D",mx,Mx);
    1136:   }

    1138:   DMDAGetCorners(daf,&i_start,NULL,NULL,&m_f,NULL,NULL);
    1139:   DMDAGetGhostCorners(daf,&i_start_ghost,NULL,NULL,&m_ghost,NULL,NULL);
    1140:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    1141:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    1143:   DMDAGetCorners(dac,&i_start_c,NULL,NULL,&m_c,NULL,NULL);
    1144:   DMDAGetGhostCorners(dac,&i_start_ghost_c,NULL,NULL,&m_ghost_c,NULL,NULL);

    1146:   /* loop over local fine grid nodes setting interpolation for those*/
    1147:   nc   = 0;
    1148:   PetscMalloc1(m_f,&cols);

    1150:   for (i=i_start_c; i<i_start_c+m_c; i++) {
    1151:     PetscInt i_f = i*ratioi;

    1153:     if (i_f < i_start_ghost || i_f >= i_start_ghost+m_ghost) SETERRQ4(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\ni_c %D i_f %D fine ghost range [%D,%D]",i,i_f,i_start_ghost,i_start_ghost+m_ghost);

    1155:     row        = idx_f[(i_f-i_start_ghost)];
    1156:     cols[nc++] = row;
    1157:   }

    1159:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    1160:   ISCreateBlock(PetscObjectComm((PetscObject)daf),dof,nc,cols,PETSC_OWN_POINTER,&isf);
    1161:   DMGetGlobalVector(dac,&vecc);
    1162:   DMGetGlobalVector(daf,&vecf);
    1163:   VecScatterCreate(vecf,isf,vecc,NULL,inject);
    1164:   DMRestoreGlobalVector(dac,&vecc);
    1165:   DMRestoreGlobalVector(daf,&vecf);
    1166:   ISDestroy(&isf);
    1167:   return(0);
    1168: }

    1170: PetscErrorCode DMCreateInjection_DA_2D(DM dac,DM daf,VecScatter *inject)
    1171: {
    1172:   PetscErrorCode         ierr;
    1173:   PetscInt               i,j,i_start,j_start,m_f,n_f,Mx,My,dof;
    1174:   const PetscInt         *idx_c,*idx_f;
    1175:   ISLocalToGlobalMapping ltog_f,ltog_c;
    1176:   PetscInt               m_ghost,n_ghost,m_ghost_c,n_ghost_c;
    1177:   PetscInt               row,i_start_ghost,j_start_ghost,mx,m_c,my,nc,ratioi,ratioj;
    1178:   PetscInt               i_start_c,j_start_c,n_c,i_start_ghost_c,j_start_ghost_c;
    1179:   PetscInt               *cols;
    1180:   DMBoundaryType         bx,by;
    1181:   Vec                    vecf,vecc;
    1182:   IS                     isf;

    1185:   DMDAGetInfo(dac,NULL,&Mx,&My,NULL,NULL,NULL,NULL,NULL,NULL,&bx,&by,NULL,NULL);
    1186:   DMDAGetInfo(daf,NULL,&mx,&my,NULL,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);
    1187:   if (bx == DM_BOUNDARY_PERIODIC) {
    1188:     ratioi = mx/Mx;
    1189:     if (ratioi*Mx != mx) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mx/Mx  must be integer: mx %D Mx %D",mx,Mx);
    1190:   } else {
    1191:     ratioi = (mx-1)/(Mx-1);
    1192:     if (ratioi*(Mx-1) != mx-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %D Mx %D",mx,Mx);
    1193:   }
    1194:   if (by == DM_BOUNDARY_PERIODIC) {
    1195:     ratioj = my/My;
    1196:     if (ratioj*My != my) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: my/My  must be integer: my %D My %D",my,My);
    1197:   } else {
    1198:     ratioj = (my-1)/(My-1);
    1199:     if (ratioj*(My-1) != my-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (my - 1)/(My - 1) must be integer: my %D My %D",my,My);
    1200:   }

    1202:   DMDAGetCorners(daf,&i_start,&j_start,NULL,&m_f,&n_f,NULL);
    1203:   DMDAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,NULL,&m_ghost,&n_ghost,NULL);
    1204:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    1205:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    1207:   DMDAGetCorners(dac,&i_start_c,&j_start_c,NULL,&m_c,&n_c,NULL);
    1208:   DMDAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,NULL,&m_ghost_c,&n_ghost_c,NULL);
    1209:   DMGetLocalToGlobalMapping(dac,&ltog_c);
    1210:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

    1212:   /* loop over local fine grid nodes setting interpolation for those*/
    1213:   nc   = 0;
    1214:   PetscMalloc1(n_f*m_f,&cols);
    1215:   for (j=j_start_c; j<j_start_c+n_c; j++) {
    1216:     for (i=i_start_c; i<i_start_c+m_c; i++) {
    1217:       PetscInt i_f = i*ratioi,j_f = j*ratioj;
    1218:       if (j_f < j_start_ghost || j_f >= j_start_ghost+n_ghost) SETERRQ4(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    1219:     j_c %D j_f %D fine ghost range [%D,%D]",j,j_f,j_start_ghost,j_start_ghost+n_ghost);
    1220:       if (i_f < i_start_ghost || i_f >= i_start_ghost+m_ghost) SETERRQ4(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA\n\
    1221:     i_c %D i_f %D fine ghost range [%D,%D]",i,i_f,i_start_ghost,i_start_ghost+m_ghost);
    1222:       row        = idx_f[(m_ghost*(j_f-j_start_ghost) + (i_f-i_start_ghost))];
    1223:       cols[nc++] = row;
    1224:     }
    1225:   }
    1226:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    1227:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);

    1229:   ISCreateBlock(PetscObjectComm((PetscObject)daf),dof,nc,cols,PETSC_OWN_POINTER,&isf);
    1230:   DMGetGlobalVector(dac,&vecc);
    1231:   DMGetGlobalVector(daf,&vecf);
    1232:   VecScatterCreate(vecf,isf,vecc,NULL,inject);
    1233:   DMRestoreGlobalVector(dac,&vecc);
    1234:   DMRestoreGlobalVector(daf,&vecf);
    1235:   ISDestroy(&isf);
    1236:   return(0);
    1237: }

    1239: PetscErrorCode DMCreateInjection_DA_3D(DM dac,DM daf,VecScatter *inject)
    1240: {
    1241:   PetscErrorCode         ierr;
    1242:   PetscInt               i,j,k,i_start,j_start,k_start,m_f,n_f,p_f,Mx,My,Mz;
    1243:   PetscInt               m_ghost,n_ghost,p_ghost,m_ghost_c,n_ghost_c,p_ghost_c;
    1244:   PetscInt               i_start_ghost,j_start_ghost,k_start_ghost;
    1245:   PetscInt               mx,my,mz,ratioi,ratioj,ratiok;
    1246:   PetscInt               i_start_c,j_start_c,k_start_c;
    1247:   PetscInt               m_c,n_c,p_c;
    1248:   PetscInt               i_start_ghost_c,j_start_ghost_c,k_start_ghost_c;
    1249:   PetscInt               row,nc,dof;
    1250:   const PetscInt         *idx_c,*idx_f;
    1251:   ISLocalToGlobalMapping ltog_f,ltog_c;
    1252:   PetscInt               *cols;
    1253:   DMBoundaryType         bx,by,bz;
    1254:   Vec                    vecf,vecc;
    1255:   IS                     isf;

    1258:   DMDAGetInfo(dac,NULL,&Mx,&My,&Mz,NULL,NULL,NULL,NULL,NULL,&bx,&by,&bz,NULL);
    1259:   DMDAGetInfo(daf,NULL,&mx,&my,&mz,NULL,NULL,NULL,&dof,NULL,NULL,NULL,NULL,NULL);

    1261:   if (bx == DM_BOUNDARY_PERIODIC) {
    1262:     ratioi = mx/Mx;
    1263:     if (ratioi*Mx != mx) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mx/Mx  must be integer: mx %D Mx %D",mx,Mx);
    1264:   } else {
    1265:     ratioi = (mx-1)/(Mx-1);
    1266:     if (ratioi*(Mx-1) != mx-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %D Mx %D",mx,Mx);
    1267:   }
    1268:   if (by == DM_BOUNDARY_PERIODIC) {
    1269:     ratioj = my/My;
    1270:     if (ratioj*My != my) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: my/My  must be integer: my %D My %D",my,My);
    1271:   } else {
    1272:     ratioj = (my-1)/(My-1);
    1273:     if (ratioj*(My-1) != my-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (my - 1)/(My - 1) must be integer: my %D My %D",my,My);
    1274:   }
    1275:   if (bz == DM_BOUNDARY_PERIODIC) {
    1276:     ratiok = mz/Mz;
    1277:     if (ratiok*Mz != mz) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: mz/Mz  must be integer: mz %D My %D",mz,Mz);
    1278:   } else {
    1279:     ratiok = (mz-1)/(Mz-1);
    1280:     if (ratiok*(Mz-1) != mz-1) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Ratio between levels: (mz - 1)/(Mz - 1) must be integer: mz %D Mz %D",mz,Mz);
    1281:   }

    1283:   DMDAGetCorners(daf,&i_start,&j_start,&k_start,&m_f,&n_f,&p_f);
    1284:   DMDAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,&k_start_ghost,&m_ghost,&n_ghost,&p_ghost);
    1285:   DMGetLocalToGlobalMapping(daf,&ltog_f);
    1286:   ISLocalToGlobalMappingGetBlockIndices(ltog_f,&idx_f);

    1288:   DMDAGetCorners(dac,&i_start_c,&j_start_c,&k_start_c,&m_c,&n_c,&p_c);
    1289:   DMDAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,&k_start_ghost_c,&m_ghost_c,&n_ghost_c,&p_ghost_c);
    1290:   DMGetLocalToGlobalMapping(dac,&ltog_c);
    1291:   ISLocalToGlobalMappingGetBlockIndices(ltog_c,&idx_c);

    1293:   /* loop over local fine grid nodes setting interpolation for those*/
    1294:   nc   = 0;
    1295:   PetscMalloc1(n_f*m_f*p_f,&cols);
    1296:   for (k=k_start_c; k<k_start_c+p_c; k++) {
    1297:     for (j=j_start_c; j<j_start_c+n_c; j++) {
    1298:       for (i=i_start_c; i<i_start_c+m_c; i++) {
    1299:         PetscInt i_f = i*ratioi,j_f = j*ratioj,k_f = k*ratiok;
    1300:         if (k_f < k_start_ghost || k_f >= k_start_ghost+p_ghost) SETERRQ4(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA  "
    1301:                                                                           "k_c %D k_f %D fine ghost range [%D,%D]",k,k_f,k_start_ghost,k_start_ghost+p_ghost);
    1302:         if (j_f < j_start_ghost || j_f >= j_start_ghost+n_ghost) SETERRQ4(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA  "
    1303:                                                                           "j_c %D j_f %D fine ghost range [%D,%D]",j,j_f,j_start_ghost,j_start_ghost+n_ghost);
    1304:         if (i_f < i_start_ghost || i_f >= i_start_ghost+m_ghost) SETERRQ4(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Processor's coarse DMDA must lie over fine DMDA  "
    1305:                                                                           "i_c %D i_f %D fine ghost range [%D,%D]",i,i_f,i_start_ghost,i_start_ghost+m_ghost);
    1306:         row        = idx_f[(m_ghost*n_ghost*(k_f-k_start_ghost) + m_ghost*(j_f-j_start_ghost) + (i_f-i_start_ghost))];
    1307:         cols[nc++] = row;
    1308:       }
    1309:     }
    1310:   }
    1311:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_f,&idx_f);
    1312:   ISLocalToGlobalMappingRestoreBlockIndices(ltog_c,&idx_c);

    1314:   ISCreateBlock(PetscObjectComm((PetscObject)daf),dof,nc,cols,PETSC_OWN_POINTER,&isf);
    1315:   DMGetGlobalVector(dac,&vecc);
    1316:   DMGetGlobalVector(daf,&vecf);
    1317:   VecScatterCreate(vecf,isf,vecc,NULL,inject);
    1318:   DMRestoreGlobalVector(dac,&vecc);
    1319:   DMRestoreGlobalVector(daf,&vecf);
    1320:   ISDestroy(&isf);
    1321:   return(0);
    1322: }

    1324: PetscErrorCode  DMCreateInjection_DA(DM dac,DM daf,Mat *mat)
    1325: {
    1326:   PetscErrorCode  ierr;
    1327:   PetscInt        dimc,Mc,Nc,Pc,mc,nc,pc,dofc,sc,dimf,Mf,Nf,Pf,mf,nf,pf,doff,sf;
    1328:   DMBoundaryType  bxc,byc,bzc,bxf,byf,bzf;
    1329:   DMDAStencilType stc,stf;
    1330:   VecScatter      inject = NULL;

    1337:   DMDAGetInfo(dac,&dimc,&Mc,&Nc,&Pc,&mc,&nc,&pc,&dofc,&sc,&bxc,&byc,&bzc,&stc);
    1338:   DMDAGetInfo(daf,&dimf,&Mf,&Nf,&Pf,&mf,&nf,&pf,&doff,&sf,&bxf,&byf,&bzf,&stf);
    1339:   if (dimc != dimf) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Dimensions of DMDA do not match %D %D",dimc,dimf);
    1340:   if (dofc != doff) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"DOF of DMDA do not match %D %D",dofc,doff);
    1341:   if (sc != sf) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Stencil width of DMDA do not match %D %D",sc,sf);
    1342:   if (bxc != bxf || byc != byf || bzc != bzf) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Boundary type different in two DMDAs");
    1343:   if (stc != stf) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Stencil type different in two DMDAs");
    1344:   if (Mc < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Coarse grid requires at least 2 points in x direction");
    1345:   if (dimc > 1 && Nc < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Coarse grid requires at least 2 points in y direction");
    1346:   if (dimc > 2 && Pc < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Coarse grid requires at least 2 points in z direction");

    1348:   if (dimc == 1) {
    1349:     DMCreateInjection_DA_1D(dac,daf,&inject);
    1350:   } else if (dimc == 2) {
    1351:     DMCreateInjection_DA_2D(dac,daf,&inject);
    1352:   } else if (dimc == 3) {
    1353:     DMCreateInjection_DA_3D(dac,daf,&inject);
    1354:   }
    1355:   MatCreateScatter(PetscObjectComm((PetscObject)inject), inject, mat);
    1356:   VecScatterDestroy(&inject);
    1357:   return(0);
    1358: }

    1360: /*@
    1361:    DMCreateAggregates - Deprecated, see DMDACreateAggregates.

    1363:    Level: intermediate
    1364: @*/
    1365: PetscErrorCode DMCreateAggregates(DM dac,DM daf,Mat *mat)
    1366: {
    1367:   return DMDACreateAggregates(dac,daf,mat);
    1368: }

    1370: /*@
    1371:    DMDACreateAggregates - Gets the aggregates that map between
    1372:    grids associated with two DMDAs.

    1374:    Collective on dmc

    1376:    Input Parameters:
    1377: +  dmc - the coarse grid DMDA
    1378: -  dmf - the fine grid DMDA

    1380:    Output Parameters:
    1381: .  rest - the restriction matrix (transpose of the projection matrix)

    1383:    Level: intermediate

    1385:    Note: This routine is not used by PETSc.
    1386:    It is not clear what its use case is and it may be removed in a future release.
    1387:    Users should contact petsc-maint@mcs.anl.gov if they plan to use it.

    1389: .seealso: DMRefine(), DMCreateInjection(), DMCreateInterpolation()
    1390: @*/
    1391: PetscErrorCode DMDACreateAggregates(DM dac,DM daf,Mat *rest)
    1392: {
    1393:   PetscErrorCode         ierr;
    1394:   PetscInt               dimc,Mc,Nc,Pc,mc,nc,pc,dofc,sc;
    1395:   PetscInt               dimf,Mf,Nf,Pf,mf,nf,pf,doff,sf;
    1396:   DMBoundaryType         bxc,byc,bzc,bxf,byf,bzf;
    1397:   DMDAStencilType        stc,stf;
    1398:   PetscInt               i,j,l;
    1399:   PetscInt               i_start,j_start,l_start, m_f,n_f,p_f;
    1400:   PetscInt               i_start_ghost,j_start_ghost,l_start_ghost,m_ghost,n_ghost,p_ghost;
    1401:   const PetscInt         *idx_f;
    1402:   PetscInt               i_c,j_c,l_c;
    1403:   PetscInt               i_start_c,j_start_c,l_start_c, m_c,n_c,p_c;
    1404:   PetscInt               i_start_ghost_c,j_start_ghost_c,l_start_ghost_c,m_ghost_c,n_ghost_c,p_ghost_c;
    1405:   const PetscInt         *idx_c;
    1406:   PetscInt               d;
    1407:   PetscInt               a;
    1408:   PetscInt               max_agg_size;
    1409:   PetscInt               *fine_nodes;
    1410:   PetscScalar            *one_vec;
    1411:   PetscInt               fn_idx;
    1412:   ISLocalToGlobalMapping ltogmf,ltogmc;

    1419:   DMDAGetInfo(dac,&dimc,&Mc,&Nc,&Pc,&mc,&nc,&pc,&dofc,&sc,&bxc,&byc,&bzc,&stc);
    1420:   DMDAGetInfo(daf,&dimf,&Mf,&Nf,&Pf,&mf,&nf,&pf,&doff,&sf,&bxf,&byf,&bzf,&stf);
    1421:   if (dimc != dimf) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Dimensions of DMDA do not match %D %D",dimc,dimf);
    1422:   if (dofc != doff) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"DOF of DMDA do not match %D %D",dofc,doff);
    1423:   if (sc != sf) SETERRQ2(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Stencil width of DMDA do not match %D %D",sc,sf);
    1424:   if (bxc != bxf || byc != byf || bzc != bzf) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Boundary type different in two DMDAs");
    1425:   if (stc != stf) SETERRQ(PetscObjectComm((PetscObject)daf),PETSC_ERR_ARG_INCOMP,"Stencil type different in two DMDAs");

    1427:   if (Mf < Mc) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Coarse grid has more points than fine grid, Mc %D, Mf %D", Mc, Mf);
    1428:   if (Nf < Nc) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Coarse grid has more points than fine grid, Nc %D, Nf %D", Nc, Nf);
    1429:   if (Pf < Pc) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Coarse grid has more points than fine grid, Pc %D, Pf %D", Pc, Pf);

    1431:   if (Pc < 0) Pc = 1;
    1432:   if (Pf < 0) Pf = 1;
    1433:   if (Nc < 0) Nc = 1;
    1434:   if (Nf < 0) Nf = 1;

    1436:   DMDAGetCorners(daf,&i_start,&j_start,&l_start,&m_f,&n_f,&p_f);
    1437:   DMDAGetGhostCorners(daf,&i_start_ghost,&j_start_ghost,&l_start_ghost,&m_ghost,&n_ghost,&p_ghost);

    1439:   DMGetLocalToGlobalMapping(daf,&ltogmf);
    1440:   ISLocalToGlobalMappingGetIndices(ltogmf,&idx_f);

    1442:   DMDAGetCorners(dac,&i_start_c,&j_start_c,&l_start_c,&m_c,&n_c,&p_c);
    1443:   DMDAGetGhostCorners(dac,&i_start_ghost_c,&j_start_ghost_c,&l_start_ghost_c,&m_ghost_c,&n_ghost_c,&p_ghost_c);

    1445:   DMGetLocalToGlobalMapping(dac,&ltogmc);
    1446:   ISLocalToGlobalMappingGetIndices(ltogmc,&idx_c);

    1448:   /*
    1449:      Basic idea is as follows. Here's a 2D example, suppose r_x, r_y are the ratios
    1450:      for dimension 1 and 2 respectively.
    1451:      Let (i,j) be a coarse grid node. All the fine grid nodes between r_x*i and r_x*(i+1)
    1452:      and r_y*j and r_y*(j+1) will be grouped into the same coarse grid agregate.
    1453:      Each specific dof on the fine grid is mapped to one dof on the coarse grid.
    1454:   */

    1456:   max_agg_size = (Mf/Mc+1)*(Nf/Nc+1)*(Pf/Pc+1);

    1458:   /* create the matrix that will contain the restriction operator */
    1459:   MatCreateAIJ(PetscObjectComm((PetscObject)daf), m_c*n_c*p_c*dofc, m_f*n_f*p_f*doff, Mc*Nc*Pc*dofc, Mf*Nf*Pf*doff,
    1460:                       max_agg_size, NULL, max_agg_size, NULL, rest);

    1462:   /* store nodes in the fine grid here */
    1463:   PetscMalloc2(max_agg_size, &one_vec,max_agg_size, &fine_nodes);
    1464:   for (i=0; i<max_agg_size; i++) one_vec[i] = 1.0;

    1466:   /* loop over all coarse nodes */
    1467:   for (l_c=l_start_c; l_c<l_start_c+p_c; l_c++) {
    1468:     for (j_c=j_start_c; j_c<j_start_c+n_c; j_c++) {
    1469:       for (i_c=i_start_c; i_c<i_start_c+m_c; i_c++) {
    1470:         for (d=0; d<dofc; d++) {
    1471:           /* convert to local "natural" numbering and then to PETSc global numbering */
    1472:           a = idx_c[dofc*(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))] + d;

    1474:           fn_idx = 0;
    1475:           /* Corresponding fine points are all points (i_f, j_f, l_f) such that
    1476:              i_c*Mf/Mc <= i_f < (i_c+1)*Mf/Mc
    1477:              (same for other dimensions)
    1478:           */
    1479:           for (l=l_c*Pf/Pc; l<PetscMin((l_c+1)*Pf/Pc,Pf); l++) {
    1480:             for (j=j_c*Nf/Nc; j<PetscMin((j_c+1)*Nf/Nc,Nf); j++) {
    1481:               for (i=i_c*Mf/Mc; i<PetscMin((i_c+1)*Mf/Mc,Mf); i++) {
    1482:                 fine_nodes[fn_idx] = idx_f[doff*(m_ghost*n_ghost*(l-l_start_ghost) + m_ghost*(j-j_start_ghost) + (i-i_start_ghost))] + d;
    1483:                 fn_idx++;
    1484:               }
    1485:             }
    1486:           }
    1487:           /* add all these points to one aggregate */
    1488:           MatSetValues(*rest, 1, &a, fn_idx, fine_nodes, one_vec, INSERT_VALUES);
    1489:         }
    1490:       }
    1491:     }
    1492:   }
    1493:   ISLocalToGlobalMappingRestoreIndices(ltogmf,&idx_f);
    1494:   ISLocalToGlobalMappingRestoreIndices(ltogmc,&idx_c);
    1495:   PetscFree2(one_vec,fine_nodes);
    1496:   MatAssemblyBegin(*rest, MAT_FINAL_ASSEMBLY);
    1497:   MatAssemblyEnd(*rest, MAT_FINAL_ASSEMBLY);
    1498:   return(0);
    1499: }