Actual source code: dainterp.c

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

  5: /*
  6:    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
  7:    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
  8:    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
  9:    consider it cleaner, but old version is turned on since it handles periodic case.
 10: */
 11: #define NEWVERSION 0

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

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

 27:   PetscFunctionBegin;
 28:   *outtype = MATAIJ;
 29:   for (i = 0; i < 3; i++) {
 30:     PetscCall(PetscStrbeginswith(intype, types[i], &flg));
 31:     if (flg) {
 32:       *outtype = intype;
 33:       PetscFunctionReturn(PETSC_SUCCESS);
 34:     }
 35:   }
 36:   PetscFunctionReturn(PETSC_SUCCESS);
 37: }

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

 52:   PetscFunctionBegin;
 53:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &bx, NULL, NULL, NULL));
 54:   PetscCall(DMDAGetInfo(daf, NULL, &mx, NULL, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
 55:   if (bx == DM_BOUNDARY_PERIODIC) {
 56:     ratio = mx / Mx;
 57:     PetscCheck(ratio * Mx == mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mx/Mx  must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
 58:   } else {
 59:     ratio = (mx - 1) / (Mx - 1);
 60:     PetscCheck(ratio * (Mx - 1) == mx - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
 61:   }

 63:   PetscCall(DMDAGetCorners(daf, &i_start, NULL, NULL, &m_f, NULL, NULL));
 64:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, NULL, NULL, &m_ghost, NULL, NULL));
 65:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
 66:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

 68:   PetscCall(DMDAGetCorners(dac, &i_start_c, NULL, NULL, &m_c, NULL, NULL));
 69:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, NULL, NULL, &m_ghost_c, NULL, NULL));
 70:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
 71:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

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

 88:   /* loop over local fine grid nodes setting interpolation for those*/
 89:   if (!NEWVERSION) {
 90:     for (i = i_start; i < i_start + m_f; i++) {
 91:       /* convert to local "natural" numbering and then to PETSc global numbering */
 92:       row = idx_f[i - i_start_ghost];

 94:       i_c = (i / ratio); /* coarse grid node to left of fine grid node */
 95:       PetscCheck(i_c >= i_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_start %" PetscInt_FMT " i_c %" PetscInt_FMT " i_start_ghost_c %" PetscInt_FMT, i_start, i_c, i_start_ghost_c);

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

116:   } else {
117:     PetscScalar *xi;
118:     PetscInt     li, nxi, n;
119:     PetscScalar  Ni[2];

121:     /* compute local coordinate arrays */
122:     nxi = ratio + 1;
123:     PetscCall(PetscMalloc1(nxi, &xi));
124:     for (li = 0; li < nxi; li++) xi[li] = -1.0 + (PetscScalar)li * (2.0 / (PetscScalar)(nxi - 1));

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

130:       i_c = (i / ratio); /* coarse grid node to left of fine grid node */
131:       PetscCheck(i_c >= i_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_start %" PetscInt_FMT " i_c %" PetscInt_FMT " i_start_ghost_c %" PetscInt_FMT, i_start, i_c, i_start_ghost_c); /* remainders */
132:       li = i - ratio * (i / ratio);
133:       if (i == mx - 1) li = nxi - 1;

135:       /* corners */
136:       col     = (i_c - i_start_ghost_c);
137:       cols[0] = idx_c[col];
138:       Ni[0]   = 1.0;
139:       if ((li == 0) || (li == nxi - 1)) {
140:         PetscCall(MatSetValue(mat, row, cols[0], Ni[0], INSERT_VALUES));
141:         continue;
142:       }

144:       /* edges + interior */
145:       /* remainders */
146:       if (i == mx - 1) i_c--;

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

152:       Ni[0] = 0.5 * (1.0 - xi[li]);
153:       Ni[1] = 0.5 * (1.0 + xi[li]);
154:       for (n = 0; n < 2; n++) {
155:         if (PetscAbsScalar(Ni[n]) < 1.0e-32) cols[n] = -1;
156:       }
157:       PetscCall(MatSetValues(mat, 1, &row, 2, cols, Ni, INSERT_VALUES));
158:     }
159:     PetscCall(PetscFree(xi));
160:   }
161:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
162:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));
163:   PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
164:   PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
165:   PetscCall(MatCreateMAIJ(mat, dof, A));
166:   PetscCall(MatDestroy(&mat));
167:   PetscFunctionReturn(PETSC_SUCCESS);
168: }

170: static PetscErrorCode DMCreateInterpolation_DA_1D_Q0(DM dac, DM daf, Mat *A)
171: {
172:   PetscInt               i, i_start, m_f, Mx;
173:   const PetscInt        *idx_f, *idx_c;
174:   ISLocalToGlobalMapping ltog_f, ltog_c;
175:   PetscInt               m_ghost, m_ghost_c;
176:   PetscInt               row, col, i_start_ghost, mx, m_c, nc, ratio;
177:   PetscInt               i_c, i_start_c, i_start_ghost_c, cols[2], dof;
178:   PetscScalar            v[2], x;
179:   Mat                    mat;
180:   DMBoundaryType         bx;
181:   MatType                mattype;

183:   PetscFunctionBegin;
184:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &bx, NULL, NULL, NULL));
185:   PetscCall(DMDAGetInfo(daf, NULL, &mx, NULL, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
186:   if (bx == DM_BOUNDARY_PERIODIC) {
187:     PetscCheck(Mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be positive", Mx);
188:     ratio = mx / Mx;
189:     PetscCheck(ratio * Mx == mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mx/Mx  must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
190:   } else {
191:     PetscCheck(Mx >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be greater than 1", Mx);
192:     ratio = (mx - 1) / (Mx - 1);
193:     PetscCheck(ratio * (Mx - 1) == mx - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
194:   }

196:   PetscCall(DMDAGetCorners(daf, &i_start, NULL, NULL, &m_f, NULL, NULL));
197:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, NULL, NULL, &m_ghost, NULL, NULL));
198:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
199:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

201:   PetscCall(DMDAGetCorners(dac, &i_start_c, NULL, NULL, &m_c, NULL, NULL));
202:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, NULL, NULL, &m_ghost_c, NULL, NULL));
203:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
204:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

206:   /* create interpolation matrix */
207:   PetscCall(MatCreate(PetscObjectComm((PetscObject)dac), &mat));
208: #if defined(PETSC_HAVE_CUDA)
209:   /*
210:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
211:      we don't want the original unconverted matrix copied to the GPU
212:    */
213:   if (dof > 1) PetscCall(MatBindToCPU(mat, PETSC_TRUE));
214: #endif
215:   PetscCall(MatSetSizes(mat, m_f, m_c, mx, Mx));
216:   PetscCall(ConvertToAIJ(dac->mattype, &mattype));
217:   PetscCall(MatSetType(mat, mattype));
218:   PetscCall(MatSeqAIJSetPreallocation(mat, 2, NULL));
219:   PetscCall(MatMPIAIJSetPreallocation(mat, 2, NULL, 0, NULL));

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

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

228:     /*
229:          Only include those interpolation points that are truly
230:          nonzero. Note this is very important for final grid lines
231:          in x direction; since they have no right neighbor
232:     */
233:     x  = ((PetscReal)(i - i_c * ratio)) / ((PetscReal)ratio);
234:     nc = 0;
235:     /* one left and below; or we are right on it */
236:     col      = (i_c - i_start_ghost_c);
237:     cols[nc] = idx_c[col];
238:     v[nc++]  = -x + 1.0;
239:     /* one right? */
240:     if (i_c * ratio != i) {
241:       cols[nc] = idx_c[col + 1];
242:       v[nc++]  = x;
243:     }
244:     PetscCall(MatSetValues(mat, 1, &row, nc, cols, v, INSERT_VALUES));
245:   }
246:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
247:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));
248:   PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
249:   PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
250:   PetscCall(MatCreateMAIJ(mat, dof, A));
251:   PetscCall(MatDestroy(&mat));
252:   PetscCall(PetscLogFlops(5.0 * m_f));
253:   PetscFunctionReturn(PETSC_SUCCESS);
254: }

256: static PetscErrorCode DMCreateInterpolation_DA_2D_Q1(DM dac, DM daf, Mat *A)
257: {
258:   PetscInt               i, j, i_start, j_start, m_f, n_f, Mx, My, dof;
259:   const PetscInt        *idx_c, *idx_f;
260:   ISLocalToGlobalMapping ltog_f, ltog_c;
261:   PetscInt               m_ghost, n_ghost, m_ghost_c, n_ghost_c, *dnz, *onz;
262:   PetscInt               row, col, i_start_ghost, j_start_ghost, cols[4], mx, m_c, my, nc, ratioi, ratioj;
263:   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;
264:   PetscMPIInt            size_c, size_f, rank_f;
265:   PetscScalar            v[4], x, y;
266:   Mat                    mat;
267:   DMBoundaryType         bx, by;
268:   MatType                mattype;

270:   PetscFunctionBegin;
271:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, &My, NULL, NULL, NULL, NULL, NULL, NULL, &bx, &by, NULL, NULL));
272:   PetscCall(DMDAGetInfo(daf, NULL, &mx, &my, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
273:   if (bx == DM_BOUNDARY_PERIODIC) {
274:     PetscCheck(Mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be positive", Mx);
275:     ratioi = mx / Mx;
276:     PetscCheck(ratioi * Mx == mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mx/Mx  must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
277:   } else {
278:     PetscCheck(Mx >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be greater than 1", Mx);
279:     ratioi = (mx - 1) / (Mx - 1);
280:     PetscCheck(ratioi * (Mx - 1) == mx - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
281:   }
282:   if (by == DM_BOUNDARY_PERIODIC) {
283:     PetscCheck(My, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of y coarse grid points %" PetscInt_FMT " must be positive", My);
284:     ratioj = my / My;
285:     PetscCheck(ratioj * My == my, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: my/My  must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
286:   } else {
287:     PetscCheck(My >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of y coarse grid points %" PetscInt_FMT " must be greater than 1", My);
288:     ratioj = (my - 1) / (My - 1);
289:     PetscCheck(ratioj * (My - 1) == my - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (my - 1)/(My - 1) must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
290:   }

292:   PetscCall(DMDAGetCorners(daf, &i_start, &j_start, NULL, &m_f, &n_f, NULL));
293:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, &j_start_ghost, NULL, &m_ghost, &n_ghost, NULL));
294:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
295:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

297:   PetscCall(DMDAGetCorners(dac, &i_start_c, &j_start_c, NULL, &m_c, &n_c, NULL));
298:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, &j_start_ghost_c, NULL, &m_ghost_c, &n_ghost_c, NULL));
299:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
300:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

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

310:    In the standard case when size_f == size_c col_scale == 1 and col_shift == 0
311:    */
312:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dac), &size_c));
313:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)daf), &size_f));
314:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)daf), &rank_f));
315:   col_scale = size_f / size_c;
316:   col_shift = Mx * My * (rank_f / size_c);

318:   MatPreallocateBegin(PetscObjectComm((PetscObject)daf), m_f * n_f, col_scale * m_c * n_c, dnz, onz);
319:   for (j = j_start; j < j_start + n_f; j++) {
320:     for (i = i_start; i < i_start + m_f; i++) {
321:       /* convert to local "natural" numbering and then to PETSc global numbering */
322:       row = idx_f[(m_ghost * (j - j_start_ghost) + (i - i_start_ghost))];

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

327:       PetscCheck(j_c >= j_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, j_start %" PetscInt_FMT " j_c %" PetscInt_FMT " j_start_ghost_c %" PetscInt_FMT, j_start, j_c, j_start_ghost_c);
328:       PetscCheck(i_c >= i_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_start %" PetscInt_FMT " i_c %" PetscInt_FMT " i_start_ghost_c %" PetscInt_FMT, i_start, i_c, i_start_ghost_c);

330:       /*
331:        Only include those interpolation points that are truly
332:        nonzero. Note this is very important for final grid lines
333:        in x and y directions; since they have no right/top neighbors
334:        */
335:       nc = 0;
336:       /* one left and below; or we are right on it */
337:       col        = (m_ghost_c * (j_c - j_start_ghost_c) + (i_c - i_start_ghost_c));
338:       cols[nc++] = col_shift + idx_c[col];
339:       /* one right and below */
340:       if (i_c * ratioi != i) cols[nc++] = col_shift + idx_c[col + 1];
341:       /* one left and above */
342:       if (j_c * ratioj != j) cols[nc++] = col_shift + idx_c[col + m_ghost_c];
343:       /* one right and above */
344:       if (i_c * ratioi != i && j_c * ratioj != j) cols[nc++] = col_shift + idx_c[col + (m_ghost_c + 1)];
345:       PetscCall(MatPreallocateSet(row, nc, cols, dnz, onz));
346:     }
347:   }
348:   PetscCall(MatCreate(PetscObjectComm((PetscObject)daf), &mat));
349: #if defined(PETSC_HAVE_CUDA)
350:   /*
351:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
352:      we don't want the original unconverted matrix copied to the GPU
353:   */
354:   if (dof > 1) PetscCall(MatBindToCPU(mat, PETSC_TRUE));
355: #endif
356:   PetscCall(MatSetSizes(mat, m_f * n_f, col_scale * m_c * n_c, mx * my, col_scale * Mx * My));
357:   PetscCall(ConvertToAIJ(dac->mattype, &mattype));
358:   PetscCall(MatSetType(mat, mattype));
359:   PetscCall(MatSeqAIJSetPreallocation(mat, 0, dnz));
360:   PetscCall(MatMPIAIJSetPreallocation(mat, 0, dnz, 0, onz));
361:   MatPreallocateEnd(dnz, onz);

363:   /* loop over local fine grid nodes setting interpolation for those*/
364:   if (!NEWVERSION) {
365:     for (j = j_start; j < j_start + n_f; j++) {
366:       for (i = i_start; i < i_start + m_f; i++) {
367:         /* convert to local "natural" numbering and then to PETSc global numbering */
368:         row = idx_f[(m_ghost * (j - j_start_ghost) + (i - i_start_ghost))];

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

373:         /*
374:          Only include those interpolation points that are truly
375:          nonzero. Note this is very important for final grid lines
376:          in x and y directions; since they have no right/top neighbors
377:          */
378:         x = ((PetscReal)(i - i_c * ratioi)) / ((PetscReal)ratioi);
379:         y = ((PetscReal)(j - j_c * ratioj)) / ((PetscReal)ratioj);

381:         nc = 0;
382:         /* one left and below; or we are right on it */
383:         col      = (m_ghost_c * (j_c - j_start_ghost_c) + (i_c - i_start_ghost_c));
384:         cols[nc] = col_shift + idx_c[col];
385:         v[nc++]  = x * y - x - y + 1.0;
386:         /* one right and below */
387:         if (i_c * ratioi != i) {
388:           cols[nc] = col_shift + idx_c[col + 1];
389:           v[nc++]  = -x * y + x;
390:         }
391:         /* one left and above */
392:         if (j_c * ratioj != j) {
393:           cols[nc] = col_shift + idx_c[col + m_ghost_c];
394:           v[nc++]  = -x * y + y;
395:         }
396:         /* one right and above */
397:         if (j_c * ratioj != j && i_c * ratioi != i) {
398:           cols[nc] = col_shift + idx_c[col + (m_ghost_c + 1)];
399:           v[nc++]  = x * y;
400:         }
401:         PetscCall(MatSetValues(mat, 1, &row, nc, cols, v, INSERT_VALUES));
402:       }
403:     }

405:   } else {
406:     PetscScalar  Ni[4];
407:     PetscScalar *xi, *eta;
408:     PetscInt     li, nxi, lj, neta;

410:     /* compute local coordinate arrays */
411:     nxi  = ratioi + 1;
412:     neta = ratioj + 1;
413:     PetscCall(PetscMalloc1(nxi, &xi));
414:     PetscCall(PetscMalloc1(neta, &eta));
415:     for (li = 0; li < nxi; li++) xi[li] = -1.0 + (PetscScalar)li * (2.0 / (PetscScalar)(nxi - 1));
416:     for (lj = 0; lj < neta; lj++) eta[lj] = -1.0 + (PetscScalar)lj * (2.0 / (PetscScalar)(neta - 1));

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

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

427:         /* remainders */
428:         li = i - ratioi * (i / ratioi);
429:         if (i == mx - 1) li = nxi - 1;
430:         lj = j - ratioj * (j / ratioj);
431:         if (j == my - 1) lj = neta - 1;

433:         /* corners */
434:         col     = (m_ghost_c * (j_c - j_start_ghost_c) + (i_c - i_start_ghost_c));
435:         cols[0] = col_shift + idx_c[col]; /* left, below */
436:         Ni[0]   = 1.0;
437:         if ((li == 0) || (li == nxi - 1)) {
438:           if ((lj == 0) || (lj == neta - 1)) {
439:             PetscCall(MatSetValue(mat, row, cols[0], Ni[0], INSERT_VALUES));
440:             continue;
441:           }
442:         }

444:         /* edges + interior */
445:         /* remainders */
446:         if (i == mx - 1) i_c--;
447:         if (j == my - 1) j_c--;

449:         col     = (m_ghost_c * (j_c - j_start_ghost_c) + (i_c - i_start_ghost_c));
450:         cols[0] = col_shift + idx_c[col];                   /* left, below */
451:         cols[1] = col_shift + idx_c[col + 1];               /* right, below */
452:         cols[2] = col_shift + idx_c[col + m_ghost_c];       /* left, above */
453:         cols[3] = col_shift + idx_c[col + (m_ghost_c + 1)]; /* right, above */

455:         Ni[0] = 0.25 * (1.0 - xi[li]) * (1.0 - eta[lj]);
456:         Ni[1] = 0.25 * (1.0 + xi[li]) * (1.0 - eta[lj]);
457:         Ni[2] = 0.25 * (1.0 - xi[li]) * (1.0 + eta[lj]);
458:         Ni[3] = 0.25 * (1.0 + xi[li]) * (1.0 + eta[lj]);

460:         nc = 0;
461:         if (PetscAbsScalar(Ni[0]) < 1.0e-32) cols[0] = -1;
462:         if (PetscAbsScalar(Ni[1]) < 1.0e-32) cols[1] = -1;
463:         if (PetscAbsScalar(Ni[2]) < 1.0e-32) cols[2] = -1;
464:         if (PetscAbsScalar(Ni[3]) < 1.0e-32) cols[3] = -1;

466:         PetscCall(MatSetValues(mat, 1, &row, 4, cols, Ni, INSERT_VALUES));
467:       }
468:     }
469:     PetscCall(PetscFree(xi));
470:     PetscCall(PetscFree(eta));
471:   }
472:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
473:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));
474:   PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
475:   PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
476:   PetscCall(MatCreateMAIJ(mat, dof, A));
477:   PetscCall(MatDestroy(&mat));
478:   PetscFunctionReturn(PETSC_SUCCESS);
479: }

481: /*
482:        Contributed by Andrei Draganescu <aidraga@sandia.gov>
483: */
484: static PetscErrorCode DMCreateInterpolation_DA_2D_Q0(DM dac, DM daf, Mat *A)
485: {
486:   PetscInt               i, j, i_start, j_start, m_f, n_f, Mx, My, dof;
487:   const PetscInt        *idx_c, *idx_f;
488:   ISLocalToGlobalMapping ltog_f, ltog_c;
489:   PetscInt               m_ghost, n_ghost, m_ghost_c, n_ghost_c, *dnz, *onz;
490:   PetscInt               row, col, i_start_ghost, j_start_ghost, cols[4], mx, m_c, my, nc, ratioi, ratioj;
491:   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;
492:   PetscMPIInt            size_c, size_f, rank_f;
493:   PetscScalar            v[4];
494:   Mat                    mat;
495:   DMBoundaryType         bx, by;
496:   MatType                mattype;

498:   PetscFunctionBegin;
499:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, &My, NULL, NULL, NULL, NULL, NULL, NULL, &bx, &by, NULL, NULL));
500:   PetscCall(DMDAGetInfo(daf, NULL, &mx, &my, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
501:   PetscCheck(Mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be positive", Mx);
502:   PetscCheck(My, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of y coarse grid points %" PetscInt_FMT " must be positive", My);
503:   ratioi = mx / Mx;
504:   ratioj = my / My;
505:   PetscCheck(ratioi * Mx == mx, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Fine grid points must be multiple of coarse grid points in x");
506:   PetscCheck(ratioj * My == my, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Fine grid points must be multiple of coarse grid points in y");
507:   PetscCheck(ratioi == 2, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Coarsening factor in x must be 2");
508:   PetscCheck(ratioj == 2, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Coarsening factor in y must be 2");

510:   PetscCall(DMDAGetCorners(daf, &i_start, &j_start, NULL, &m_f, &n_f, NULL));
511:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, &j_start_ghost, NULL, &m_ghost, &n_ghost, NULL));
512:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
513:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

515:   PetscCall(DMDAGetCorners(dac, &i_start_c, &j_start_c, NULL, &m_c, &n_c, NULL));
516:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, &j_start_ghost_c, NULL, &m_ghost_c, &n_ghost_c, NULL));
517:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
518:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

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

528:      In the standard case when size_f == size_c col_scale == 1 and col_shift == 0
529:   */
530:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dac), &size_c));
531:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)daf), &size_f));
532:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)daf), &rank_f));
533:   col_scale = size_f / size_c;
534:   col_shift = Mx * My * (rank_f / size_c);

536:   MatPreallocateBegin(PetscObjectComm((PetscObject)daf), m_f * n_f, col_scale * m_c * n_c, dnz, onz);
537:   for (j = j_start; j < j_start + n_f; j++) {
538:     for (i = i_start; i < i_start + m_f; i++) {
539:       /* convert to local "natural" numbering and then to PETSc global numbering */
540:       row = idx_f[(m_ghost * (j - j_start_ghost) + (i - i_start_ghost))];

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

545:       PetscCheck(j_c >= j_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, j_start %" PetscInt_FMT " j_c %" PetscInt_FMT " j_start_ghost_c %" PetscInt_FMT, j_start, j_c, j_start_ghost_c);
546:       PetscCheck(i_c >= i_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_start %" PetscInt_FMT " i_c %" PetscInt_FMT " i_start_ghost_c %" PetscInt_FMT, i_start, i_c, i_start_ghost_c);

548:       /*
549:          Only include those interpolation points that are truly
550:          nonzero. Note this is very important for final grid lines
551:          in x and y directions; since they have no right/top neighbors
552:       */
553:       nc = 0;
554:       /* one left and below; or we are right on it */
555:       col        = (m_ghost_c * (j_c - j_start_ghost_c) + (i_c - i_start_ghost_c));
556:       cols[nc++] = col_shift + idx_c[col];
557:       PetscCall(MatPreallocateSet(row, nc, cols, dnz, onz));
558:     }
559:   }
560:   PetscCall(MatCreate(PetscObjectComm((PetscObject)daf), &mat));
561: #if defined(PETSC_HAVE_CUDA)
562:   /*
563:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
564:      we don't want the original unconverted matrix copied to the GPU
565:   */
566:   if (dof > 1) PetscCall(MatBindToCPU(mat, PETSC_TRUE));
567: #endif
568:   PetscCall(MatSetSizes(mat, m_f * n_f, col_scale * m_c * n_c, mx * my, col_scale * Mx * My));
569:   PetscCall(ConvertToAIJ(dac->mattype, &mattype));
570:   PetscCall(MatSetType(mat, mattype));
571:   PetscCall(MatSeqAIJSetPreallocation(mat, 0, dnz));
572:   PetscCall(MatMPIAIJSetPreallocation(mat, 0, dnz, 0, onz));
573:   MatPreallocateEnd(dnz, onz);

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

581:       i_c = (i / ratioi); /* coarse grid node to left of fine grid node */
582:       j_c = (j / ratioj); /* coarse grid node below fine grid node */
583:       nc  = 0;
584:       /* one left and below; or we are right on it */
585:       col      = (m_ghost_c * (j_c - j_start_ghost_c) + (i_c - i_start_ghost_c));
586:       cols[nc] = col_shift + idx_c[col];
587:       v[nc++]  = 1.0;

589:       PetscCall(MatSetValues(mat, 1, &row, nc, cols, v, INSERT_VALUES));
590:     }
591:   }
592:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
593:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));
594:   PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
595:   PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
596:   PetscCall(MatCreateMAIJ(mat, dof, A));
597:   PetscCall(MatDestroy(&mat));
598:   PetscCall(PetscLogFlops(13.0 * m_f * n_f));
599:   PetscFunctionReturn(PETSC_SUCCESS);
600: }

602: /*
603:        Contributed by Jianming Yang <jianming-yang@uiowa.edu>
604: */
605: static PetscErrorCode DMCreateInterpolation_DA_3D_Q0(DM dac, DM daf, Mat *A)
606: {
607:   PetscInt               i, j, l, i_start, j_start, l_start, m_f, n_f, p_f, Mx, My, Mz, dof;
608:   const PetscInt        *idx_c, *idx_f;
609:   ISLocalToGlobalMapping ltog_f, ltog_c;
610:   PetscInt               m_ghost, n_ghost, p_ghost, m_ghost_c, n_ghost_c, p_ghost_c, nc, *dnz, *onz;
611:   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;
612:   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;
613:   PetscMPIInt            size_c, size_f, rank_f;
614:   PetscScalar            v[8];
615:   Mat                    mat;
616:   DMBoundaryType         bx, by, bz;
617:   MatType                mattype;

619:   PetscFunctionBegin;
620:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, &My, &Mz, NULL, NULL, NULL, NULL, NULL, &bx, &by, &bz, NULL));
621:   PetscCheck(Mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be positive", Mx);
622:   PetscCheck(My, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of y coarse grid points %" PetscInt_FMT " must be positive", My);
623:   PetscCheck(Mz, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of z coarse grid points %" PetscInt_FMT " must be positive", Mz);
624:   PetscCall(DMDAGetInfo(daf, NULL, &mx, &my, &mz, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
625:   ratioi = mx / Mx;
626:   ratioj = my / My;
627:   ratiol = mz / Mz;
628:   PetscCheck(ratioi * Mx == mx, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Fine grid points must be multiple of coarse grid points in x");
629:   PetscCheck(ratioj * My == my, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Fine grid points must be multiple of coarse grid points in y");
630:   PetscCheck(ratiol * Mz == mz, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Fine grid points must be multiple of coarse grid points in z");
631:   PetscCheck(ratioi == 2 || ratioi == 1, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Coarsening factor in x must be 1 or 2");
632:   PetscCheck(ratioj == 2 || ratioj == 1, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Coarsening factor in y must be 1 or 2");
633:   PetscCheck(ratiol == 2 || ratiol == 1, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_WRONG, "Coarsening factor in z must be 1 or 2");

635:   PetscCall(DMDAGetCorners(daf, &i_start, &j_start, &l_start, &m_f, &n_f, &p_f));
636:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, &j_start_ghost, &l_start_ghost, &m_ghost, &n_ghost, &p_ghost));
637:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
638:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

640:   PetscCall(DMDAGetCorners(dac, &i_start_c, &j_start_c, &l_start_c, &m_c, &n_c, &p_c));
641:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, &j_start_ghost_c, &l_start_ghost_c, &m_ghost_c, &n_ghost_c, &p_ghost_c));
642:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
643:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

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

653:      In the standard case when size_f == size_c col_scale == 1 and col_shift == 0
654:   */
655:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dac), &size_c));
656:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)daf), &size_f));
657:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)daf), &rank_f));
658:   col_scale = size_f / size_c;
659:   col_shift = Mx * My * Mz * (rank_f / size_c);

661:   MatPreallocateBegin(PetscObjectComm((PetscObject)daf), m_f * n_f * p_f, col_scale * m_c * n_c * p_c, dnz, onz);
662:   for (l = l_start; l < l_start + p_f; l++) {
663:     for (j = j_start; j < j_start + n_f; j++) {
664:       for (i = i_start; i < i_start + m_f; i++) {
665:         /* convert to local "natural" numbering and then to PETSc global numbering */
666:         row = idx_f[(m_ghost * n_ghost * (l - l_start_ghost) + m_ghost * (j - j_start_ghost) + (i - i_start_ghost))];

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

672:         PetscCheck(l_c >= l_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, l_start %" PetscInt_FMT " l_c %" PetscInt_FMT " l_start_ghost_c %" PetscInt_FMT, l_start, l_c, l_start_ghost_c);
673:         PetscCheck(j_c >= j_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, j_start %" PetscInt_FMT " j_c %" PetscInt_FMT " j_start_ghost_c %" PetscInt_FMT, j_start, j_c, j_start_ghost_c);
674:         PetscCheck(i_c >= i_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_start %" PetscInt_FMT " i_c %" PetscInt_FMT " i_start_ghost_c %" PetscInt_FMT, i_start, i_c, i_start_ghost_c);

676:         /*
677:            Only include those interpolation points that are truly
678:            nonzero. Note this is very important for final grid lines
679:            in x and y directions; since they have no right/top neighbors
680:         */
681:         nc = 0;
682:         /* one left and below; or we are right on it */
683:         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));
684:         cols[nc++] = col_shift + idx_c[col];
685:         PetscCall(MatPreallocateSet(row, nc, cols, dnz, onz));
686:       }
687:     }
688:   }
689:   PetscCall(MatCreate(PetscObjectComm((PetscObject)daf), &mat));
690: #if defined(PETSC_HAVE_CUDA)
691:   /*
692:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
693:      we don't want the original unconverted matrix copied to the GPU
694:   */
695:   if (dof > 1) PetscCall(MatBindToCPU(mat, PETSC_TRUE));
696: #endif
697:   PetscCall(MatSetSizes(mat, m_f * n_f * p_f, col_scale * m_c * n_c * p_c, mx * my * mz, col_scale * Mx * My * Mz));
698:   PetscCall(ConvertToAIJ(dac->mattype, &mattype));
699:   PetscCall(MatSetType(mat, mattype));
700:   PetscCall(MatSeqAIJSetPreallocation(mat, 0, dnz));
701:   PetscCall(MatMPIAIJSetPreallocation(mat, 0, dnz, 0, onz));
702:   MatPreallocateEnd(dnz, onz);

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

711:         i_c = (i / ratioi); /* coarse grid node to left of fine grid node */
712:         j_c = (j / ratioj); /* coarse grid node below fine grid node */
713:         l_c = (l / ratiol);
714:         nc  = 0;
715:         /* one left and below; or we are right on it */
716:         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));
717:         cols[nc] = col_shift + idx_c[col];
718:         v[nc++]  = 1.0;

720:         PetscCall(MatSetValues(mat, 1, &row, nc, cols, v, INSERT_VALUES));
721:       }
722:     }
723:   }
724:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
725:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));
726:   PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
727:   PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
728:   PetscCall(MatCreateMAIJ(mat, dof, A));
729:   PetscCall(MatDestroy(&mat));
730:   PetscCall(PetscLogFlops(13.0 * m_f * n_f * p_f));
731:   PetscFunctionReturn(PETSC_SUCCESS);
732: }

734: static PetscErrorCode DMCreateInterpolation_DA_3D_Q1(DM dac, DM daf, Mat *A)
735: {
736:   PetscInt               i, j, i_start, j_start, m_f, n_f, Mx, My, dof, l;
737:   const PetscInt        *idx_c, *idx_f;
738:   ISLocalToGlobalMapping ltog_f, ltog_c;
739:   PetscInt               m_ghost, n_ghost, m_ghost_c, n_ghost_c, Mz, mz;
740:   PetscInt               row, col, i_start_ghost, j_start_ghost, cols[8], mx, m_c, my, nc, ratioi, ratioj, ratiok;
741:   PetscInt               i_c, j_c, i_start_c, j_start_c, n_c, i_start_ghost_c, j_start_ghost_c;
742:   PetscInt               l_start, p_f, l_start_ghost, p_ghost, l_start_c, p_c;
743:   PetscInt               l_start_ghost_c, p_ghost_c, l_c, *dnz, *onz;
744:   PetscScalar            v[8], x, y, z;
745:   Mat                    mat;
746:   DMBoundaryType         bx, by, bz;
747:   MatType                mattype;

749:   PetscFunctionBegin;
750:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, &My, &Mz, NULL, NULL, NULL, NULL, NULL, &bx, &by, &bz, NULL));
751:   PetscCall(DMDAGetInfo(daf, NULL, &mx, &my, &mz, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
752:   if (mx == Mx) {
753:     ratioi = 1;
754:   } else if (bx == DM_BOUNDARY_PERIODIC) {
755:     PetscCheck(Mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be positive", Mx);
756:     ratioi = mx / Mx;
757:     PetscCheck(ratioi * Mx == mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mx/Mx  must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
758:   } else {
759:     PetscCheck(Mx >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of x coarse grid points %" PetscInt_FMT " must be greater than 1", Mx);
760:     ratioi = (mx - 1) / (Mx - 1);
761:     PetscCheck(ratioi * (Mx - 1) == mx - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
762:   }
763:   if (my == My) {
764:     ratioj = 1;
765:   } else if (by == DM_BOUNDARY_PERIODIC) {
766:     PetscCheck(My, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of y coarse grid points %" PetscInt_FMT " must be positive", My);
767:     ratioj = my / My;
768:     PetscCheck(ratioj * My == my, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: my/My  must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
769:   } else {
770:     PetscCheck(My >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of y coarse grid points %" PetscInt_FMT " must be greater than 1", My);
771:     ratioj = (my - 1) / (My - 1);
772:     PetscCheck(ratioj * (My - 1) == my - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (my - 1)/(My - 1) must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
773:   }
774:   if (mz == Mz) {
775:     ratiok = 1;
776:   } else if (bz == DM_BOUNDARY_PERIODIC) {
777:     PetscCheck(Mz, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of z coarse grid points %" PetscInt_FMT " must be positive", Mz);
778:     ratiok = mz / Mz;
779:     PetscCheck(ratiok * Mz == mz, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mz/Mz  must be integer: mz %" PetscInt_FMT " Mz %" PetscInt_FMT, mz, Mz);
780:   } else {
781:     PetscCheck(Mz >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number of z coarse grid points %" PetscInt_FMT " must be greater than 1", Mz);
782:     ratiok = (mz - 1) / (Mz - 1);
783:     PetscCheck(ratiok * (Mz - 1) == mz - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mz - 1)/(Mz - 1) must be integer: mz %" PetscInt_FMT " Mz %" PetscInt_FMT, mz, Mz);
784:   }

786:   PetscCall(DMDAGetCorners(daf, &i_start, &j_start, &l_start, &m_f, &n_f, &p_f));
787:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, &j_start_ghost, &l_start_ghost, &m_ghost, &n_ghost, &p_ghost));
788:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
789:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

791:   PetscCall(DMDAGetCorners(dac, &i_start_c, &j_start_c, &l_start_c, &m_c, &n_c, &p_c));
792:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, &j_start_ghost_c, &l_start_ghost_c, &m_ghost_c, &n_ghost_c, &p_ghost_c));
793:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
794:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

796:   /* create interpolation matrix, determining exact preallocation */
797:   MatPreallocateBegin(PetscObjectComm((PetscObject)dac), m_f * n_f * p_f, m_c * n_c * p_c, dnz, onz);
798:   /* loop over local fine grid nodes counting interpolating points */
799:   for (l = l_start; l < l_start + p_f; l++) {
800:     for (j = j_start; j < j_start + n_f; j++) {
801:       for (i = i_start; i < i_start + m_f; i++) {
802:         /* convert to local "natural" numbering and then to PETSc global numbering */
803:         row = idx_f[(m_ghost * n_ghost * (l - l_start_ghost) + m_ghost * (j - j_start_ghost) + (i - i_start_ghost))];
804:         i_c = (i / ratioi);
805:         j_c = (j / ratioj);
806:         l_c = (l / ratiok);
807:         PetscCheck(l_c >= l_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, l_start %" PetscInt_FMT " l_c %" PetscInt_FMT " l_start_ghost_c %" PetscInt_FMT, l_start, l_c, l_start_ghost_c);
808:         PetscCheck(j_c >= j_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, j_start %" PetscInt_FMT " j_c %" PetscInt_FMT " j_start_ghost_c %" PetscInt_FMT, j_start, j_c, j_start_ghost_c);
809:         PetscCheck(i_c >= i_start_ghost_c, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_start %" PetscInt_FMT " i_c %" PetscInt_FMT " i_start_ghost_c %" PetscInt_FMT, i_start, i_c, i_start_ghost_c);

811:         /*
812:          Only include those interpolation points that are truly
813:          nonzero. Note this is very important for final grid lines
814:          in x and y directions; since they have no right/top neighbors
815:          */
816:         nc         = 0;
817:         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));
818:         cols[nc++] = idx_c[col];
819:         if (i_c * ratioi != i) cols[nc++] = idx_c[col + 1];
820:         if (j_c * ratioj != j) cols[nc++] = idx_c[col + m_ghost_c];
821:         if (l_c * ratiok != l) cols[nc++] = idx_c[col + m_ghost_c * n_ghost_c];
822:         if (j_c * ratioj != j && i_c * ratioi != i) cols[nc++] = idx_c[col + (m_ghost_c + 1)];
823:         if (j_c * ratioj != j && l_c * ratiok != l) cols[nc++] = idx_c[col + (m_ghost_c * n_ghost_c + m_ghost_c)];
824:         if (i_c * ratioi != i && l_c * ratiok != l) cols[nc++] = idx_c[col + (m_ghost_c * n_ghost_c + 1)];
825:         if (i_c * ratioi != i && l_c * ratiok != l && j_c * ratioj != j) cols[nc++] = idx_c[col + (m_ghost_c * n_ghost_c + m_ghost_c + 1)];
826:         PetscCall(MatPreallocateSet(row, nc, cols, dnz, onz));
827:       }
828:     }
829:   }
830:   PetscCall(MatCreate(PetscObjectComm((PetscObject)dac), &mat));
831: #if defined(PETSC_HAVE_CUDA)
832:   /*
833:      Temporary hack: Since the MAIJ matrix must be converted to AIJ before being used by the GPU
834:      we don't want the original unconverted matrix copied to the GPU
835:   */
836:   if (dof > 1) PetscCall(MatBindToCPU(mat, PETSC_TRUE));
837: #endif
838:   PetscCall(MatSetSizes(mat, m_f * n_f * p_f, m_c * n_c * p_c, mx * my * mz, Mx * My * Mz));
839:   PetscCall(ConvertToAIJ(dac->mattype, &mattype));
840:   PetscCall(MatSetType(mat, mattype));
841:   PetscCall(MatSeqAIJSetPreallocation(mat, 0, dnz));
842:   PetscCall(MatMPIAIJSetPreallocation(mat, 0, dnz, 0, onz));
843:   MatPreallocateEnd(dnz, onz);

845:   /* loop over local fine grid nodes setting interpolation for those*/
846:   if (!NEWVERSION) {
847:     for (l = l_start; l < l_start + p_f; l++) {
848:       for (j = j_start; j < j_start + n_f; j++) {
849:         for (i = i_start; i < i_start + m_f; i++) {
850:           /* convert to local "natural" numbering and then to PETSc global numbering */
851:           row = idx_f[(m_ghost * n_ghost * (l - l_start_ghost) + m_ghost * (j - j_start_ghost) + (i - i_start_ghost))];

853:           i_c = (i / ratioi);
854:           j_c = (j / ratioj);
855:           l_c = (l / ratiok);

857:           /*
858:            Only include those interpolation points that are truly
859:            nonzero. Note this is very important for final grid lines
860:            in x and y directions; since they have no right/top neighbors
861:            */
862:           x = ((PetscReal)(i - i_c * ratioi)) / ((PetscReal)ratioi);
863:           y = ((PetscReal)(j - j_c * ratioj)) / ((PetscReal)ratioj);
864:           z = ((PetscReal)(l - l_c * ratiok)) / ((PetscReal)ratiok);

866:           nc = 0;
867:           /* one left and below; or we are right on it */
868:           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));

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

873:           if (i_c * ratioi != i) {
874:             cols[nc] = idx_c[col + 1];
875:             v[nc++]  = .125 * (1. + (2.0 * x - 1.)) * (1. - (2.0 * y - 1.)) * (1. - (2.0 * z - 1.));
876:           }

878:           if (j_c * ratioj != j) {
879:             cols[nc] = idx_c[col + m_ghost_c];
880:             v[nc++]  = .125 * (1. - (2.0 * x - 1.)) * (1. + (2.0 * y - 1.)) * (1. - (2.0 * z - 1.));
881:           }

883:           if (l_c * ratiok != l) {
884:             cols[nc] = idx_c[col + m_ghost_c * n_ghost_c];
885:             v[nc++]  = .125 * (1. - (2.0 * x - 1.)) * (1. - (2.0 * y - 1.)) * (1. + (2.0 * z - 1.));
886:           }

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

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

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

903:           if (i_c * ratioi != i && l_c * ratiok != l && j_c * ratioj != j) {
904:             cols[nc] = idx_c[col + (m_ghost_c * n_ghost_c + m_ghost_c + 1)];
905:             v[nc++]  = .125 * (1. + (2.0 * x - 1.)) * (1. + (2.0 * y - 1.)) * (1. + (2.0 * z - 1.));
906:           }
907:           PetscCall(MatSetValues(mat, 1, &row, nc, cols, v, INSERT_VALUES));
908:         }
909:       }
910:     }

912:   } else {
913:     PetscScalar *xi, *eta, *zeta;
914:     PetscInt     li, nxi, lj, neta, lk, nzeta, n;
915:     PetscScalar  Ni[8];

917:     /* compute local coordinate arrays */
918:     nxi   = ratioi + 1;
919:     neta  = ratioj + 1;
920:     nzeta = ratiok + 1;
921:     PetscCall(PetscMalloc1(nxi, &xi));
922:     PetscCall(PetscMalloc1(neta, &eta));
923:     PetscCall(PetscMalloc1(nzeta, &zeta));
924:     for (li = 0; li < nxi; li++) xi[li] = -1.0 + (PetscScalar)li * (2.0 / (PetscScalar)(nxi - 1));
925:     for (lj = 0; lj < neta; lj++) eta[lj] = -1.0 + (PetscScalar)lj * (2.0 / (PetscScalar)(neta - 1));
926:     for (lk = 0; lk < nzeta; lk++) zeta[lk] = -1.0 + (PetscScalar)lk * (2.0 / (PetscScalar)(nzeta - 1));

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

934:           i_c = (i / ratioi);
935:           j_c = (j / ratioj);
936:           l_c = (l / ratiok);

938:           /* remainders */
939:           li = i - ratioi * (i / ratioi);
940:           if (i == mx - 1) li = nxi - 1;
941:           lj = j - ratioj * (j / ratioj);
942:           if (j == my - 1) lj = neta - 1;
943:           lk = l - ratiok * (l / ratiok);
944:           if (l == mz - 1) lk = nzeta - 1;

946:           /* corners */
947:           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));
948:           cols[0] = idx_c[col];
949:           Ni[0]   = 1.0;
950:           if ((li == 0) || (li == nxi - 1)) {
951:             if ((lj == 0) || (lj == neta - 1)) {
952:               if ((lk == 0) || (lk == nzeta - 1)) {
953:                 PetscCall(MatSetValue(mat, row, cols[0], Ni[0], INSERT_VALUES));
954:                 continue;
955:               }
956:             }
957:           }

959:           /* edges + interior */
960:           /* remainders */
961:           if (i == mx - 1) i_c--;
962:           if (j == my - 1) j_c--;
963:           if (l == mz - 1) l_c--;

965:           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));
966:           cols[0] = idx_c[col];                   /* one left and below; or we are right on it */
967:           cols[1] = idx_c[col + 1];               /* one right and below */
968:           cols[2] = idx_c[col + m_ghost_c];       /* one left and above */
969:           cols[3] = idx_c[col + (m_ghost_c + 1)]; /* one right and above */

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

976:           Ni[0] = 0.125 * (1.0 - xi[li]) * (1.0 - eta[lj]) * (1.0 - zeta[lk]);
977:           Ni[1] = 0.125 * (1.0 + xi[li]) * (1.0 - eta[lj]) * (1.0 - zeta[lk]);
978:           Ni[2] = 0.125 * (1.0 - xi[li]) * (1.0 + eta[lj]) * (1.0 - zeta[lk]);
979:           Ni[3] = 0.125 * (1.0 + xi[li]) * (1.0 + eta[lj]) * (1.0 - zeta[lk]);

981:           Ni[4] = 0.125 * (1.0 - xi[li]) * (1.0 - eta[lj]) * (1.0 + zeta[lk]);
982:           Ni[5] = 0.125 * (1.0 + xi[li]) * (1.0 - eta[lj]) * (1.0 + zeta[lk]);
983:           Ni[6] = 0.125 * (1.0 - xi[li]) * (1.0 + eta[lj]) * (1.0 + zeta[lk]);
984:           Ni[7] = 0.125 * (1.0 + xi[li]) * (1.0 + eta[lj]) * (1.0 + zeta[lk]);

986:           for (n = 0; n < 8; n++) {
987:             if (PetscAbsScalar(Ni[n]) < 1.0e-32) cols[n] = -1;
988:           }
989:           PetscCall(MatSetValues(mat, 1, &row, 8, cols, Ni, INSERT_VALUES));
990:         }
991:       }
992:     }
993:     PetscCall(PetscFree(xi));
994:     PetscCall(PetscFree(eta));
995:     PetscCall(PetscFree(zeta));
996:   }
997:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
998:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));
999:   PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
1000:   PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));

1002:   PetscCall(MatCreateMAIJ(mat, dof, A));
1003:   PetscCall(MatDestroy(&mat));
1004:   PetscFunctionReturn(PETSC_SUCCESS);
1005: }

1007: PetscErrorCode DMCreateInterpolation_DA(DM dac, DM daf, Mat *A, Vec *scale)
1008: {
1009:   PetscInt        dimc, Mc, Nc, Pc, mc, nc, pc, dofc, sc, dimf, Mf, Nf, Pf, mf, nf, pf, doff, sf;
1010:   DMBoundaryType  bxc, byc, bzc, bxf, byf, bzf;
1011:   DMDAStencilType stc, stf;
1012:   DM_DA          *ddc = (DM_DA *)dac->data;

1014:   PetscFunctionBegin;
1017:   PetscAssertPointer(A, 3);
1018:   if (scale) PetscAssertPointer(scale, 4);

1020:   PetscCall(DMDAGetInfo(dac, &dimc, &Mc, &Nc, &Pc, &mc, &nc, &pc, &dofc, &sc, &bxc, &byc, &bzc, &stc));
1021:   PetscCall(DMDAGetInfo(daf, &dimf, &Mf, &Nf, &Pf, &mf, &nf, &pf, &doff, &sf, &bxf, &byf, &bzf, &stf));
1022:   PetscCheck(dimc == dimf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Dimensions of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, dimc, dimf);
1023:   PetscCheck(dofc == doff, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "DOF of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, dofc, doff);
1024:   PetscCheck(sc == sf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Stencil width of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, sc, sf);
1025:   PetscCheck(bxc == bxf && byc == byf && bzc == bzf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Boundary type different in two DMDAs");
1026:   PetscCheck(stc == stf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Stencil type different in two DMDAs");
1027:   PetscCheck(Mc >= 2 || Mf <= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coarse grid requires at least 2 points in x direction");
1028:   PetscCheck(dimc <= 1 || Nc >= 2 || Nf <= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coarse grid requires at least 2 points in y direction");
1029:   PetscCheck(dimc <= 2 || Pc >= 2 || Pf <= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coarse grid requires at least 2 points in z direction");

1031:   if (ddc->interptype == DMDA_Q1) {
1032:     if (dimc == 1) {
1033:       PetscCall(DMCreateInterpolation_DA_1D_Q1(dac, daf, A));
1034:     } else if (dimc == 2) {
1035:       PetscCall(DMCreateInterpolation_DA_2D_Q1(dac, daf, A));
1036:     } else if (dimc == 3) {
1037:       PetscCall(DMCreateInterpolation_DA_3D_Q1(dac, daf, A));
1038:     } else SETERRQ(PetscObjectComm((PetscObject)daf), PETSC_ERR_SUP, "No support for this DMDA dimension %" PetscInt_FMT " for interpolation type %d", dimc, (int)ddc->interptype);
1039:   } else if (ddc->interptype == DMDA_Q0) {
1040:     if (dimc == 1) {
1041:       PetscCall(DMCreateInterpolation_DA_1D_Q0(dac, daf, A));
1042:     } else if (dimc == 2) {
1043:       PetscCall(DMCreateInterpolation_DA_2D_Q0(dac, daf, A));
1044:     } else if (dimc == 3) {
1045:       PetscCall(DMCreateInterpolation_DA_3D_Q0(dac, daf, A));
1046:     } else SETERRQ(PetscObjectComm((PetscObject)daf), PETSC_ERR_SUP, "No support for this DMDA dimension %" PetscInt_FMT " for interpolation type %d", dimc, (int)ddc->interptype);
1047:   }
1048:   if (scale) PetscCall(DMCreateInterpolationScale(dac, daf, *A, scale));
1049:   PetscFunctionReturn(PETSC_SUCCESS);
1050: }

1052: static PetscErrorCode DMCreateInjection_DA_1D(DM dac, DM daf, VecScatter *inject)
1053: {
1054:   PetscInt               i, i_start, m_f, Mx, dof;
1055:   const PetscInt        *idx_f;
1056:   ISLocalToGlobalMapping ltog_f;
1057:   PetscInt               m_ghost, m_ghost_c;
1058:   PetscInt               row, i_start_ghost, mx, m_c, nc, ratioi;
1059:   PetscInt               i_start_c, i_start_ghost_c;
1060:   PetscInt              *cols;
1061:   DMBoundaryType         bx;
1062:   Vec                    vecf, vecc;
1063:   IS                     isf;

1065:   PetscFunctionBegin;
1066:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &bx, NULL, NULL, NULL));
1067:   PetscCall(DMDAGetInfo(daf, NULL, &mx, NULL, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
1068:   if (bx == DM_BOUNDARY_PERIODIC) {
1069:     ratioi = mx / Mx;
1070:     PetscCheck(ratioi * Mx == mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mx/Mx  must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
1071:   } else {
1072:     ratioi = (mx - 1) / (Mx - 1);
1073:     PetscCheck(ratioi * (Mx - 1) == mx - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
1074:   }

1076:   PetscCall(DMDAGetCorners(daf, &i_start, NULL, NULL, &m_f, NULL, NULL));
1077:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, NULL, NULL, &m_ghost, NULL, NULL));
1078:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
1079:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

1081:   PetscCall(DMDAGetCorners(dac, &i_start_c, NULL, NULL, &m_c, NULL, NULL));
1082:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, NULL, NULL, &m_ghost_c, NULL, NULL));

1084:   /* loop over local fine grid nodes setting interpolation for those*/
1085:   nc = 0;
1086:   PetscCall(PetscMalloc1(m_f, &cols));

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

1091:     PetscCheck(i_f >= i_start_ghost && i_f < i_start_ghost + m_ghost, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_c %" PetscInt_FMT " i_f %" PetscInt_FMT " fine ghost range [%" PetscInt_FMT ",%" PetscInt_FMT "]", i, i_f, i_start_ghost, i_start_ghost + m_ghost);

1093:     row        = idx_f[i_f - i_start_ghost];
1094:     cols[nc++] = row;
1095:   }

1097:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
1098:   PetscCall(ISCreateBlock(PetscObjectComm((PetscObject)daf), dof, nc, cols, PETSC_OWN_POINTER, &isf));
1099:   PetscCall(DMGetGlobalVector(dac, &vecc));
1100:   PetscCall(DMGetGlobalVector(daf, &vecf));
1101:   PetscCall(VecScatterCreate(vecf, isf, vecc, NULL, inject));
1102:   PetscCall(DMRestoreGlobalVector(dac, &vecc));
1103:   PetscCall(DMRestoreGlobalVector(daf, &vecf));
1104:   PetscCall(ISDestroy(&isf));
1105:   PetscFunctionReturn(PETSC_SUCCESS);
1106: }

1108: static PetscErrorCode DMCreateInjection_DA_2D(DM dac, DM daf, VecScatter *inject)
1109: {
1110:   PetscInt               i, j, i_start, j_start, m_f, n_f, Mx, My, dof;
1111:   const PetscInt        *idx_c, *idx_f;
1112:   ISLocalToGlobalMapping ltog_f, ltog_c;
1113:   PetscInt               m_ghost, n_ghost, m_ghost_c, n_ghost_c;
1114:   PetscInt               row, i_start_ghost, j_start_ghost, mx, m_c, my, nc, ratioi, ratioj;
1115:   PetscInt               i_start_c, j_start_c, n_c, i_start_ghost_c, j_start_ghost_c;
1116:   PetscInt              *cols;
1117:   DMBoundaryType         bx, by;
1118:   Vec                    vecf, vecc;
1119:   IS                     isf;

1121:   PetscFunctionBegin;
1122:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, &My, NULL, NULL, NULL, NULL, NULL, NULL, &bx, &by, NULL, NULL));
1123:   PetscCall(DMDAGetInfo(daf, NULL, &mx, &my, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
1124:   if (bx == DM_BOUNDARY_PERIODIC) {
1125:     ratioi = mx / Mx;
1126:     PetscCheck(ratioi * Mx == mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mx/Mx  must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
1127:   } else {
1128:     ratioi = (mx - 1) / (Mx - 1);
1129:     PetscCheck(ratioi * (Mx - 1) == mx - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
1130:   }
1131:   if (by == DM_BOUNDARY_PERIODIC) {
1132:     ratioj = my / My;
1133:     PetscCheck(ratioj * My == my, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: my/My  must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
1134:   } else {
1135:     ratioj = (my - 1) / (My - 1);
1136:     PetscCheck(ratioj * (My - 1) == my - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (my - 1)/(My - 1) must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
1137:   }

1139:   PetscCall(DMDAGetCorners(daf, &i_start, &j_start, NULL, &m_f, &n_f, NULL));
1140:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, &j_start_ghost, NULL, &m_ghost, &n_ghost, NULL));
1141:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
1142:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

1144:   PetscCall(DMDAGetCorners(dac, &i_start_c, &j_start_c, NULL, &m_c, &n_c, NULL));
1145:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, &j_start_ghost_c, NULL, &m_ghost_c, &n_ghost_c, NULL));
1146:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
1147:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

1149:   /* loop over local fine grid nodes setting interpolation for those*/
1150:   nc = 0;
1151:   PetscCall(PetscMalloc1(n_f * m_f, &cols));
1152:   for (j = j_start_c; j < j_start_c + n_c; j++) {
1153:     for (i = i_start_c; i < i_start_c + m_c; i++) {
1154:       PetscInt i_f = i * ratioi, j_f = j * ratioj;
1155:       PetscCheck(j_f >= j_start_ghost && j_f < j_start_ghost + n_ghost, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, j_c %" PetscInt_FMT " j_f %" PetscInt_FMT " fine ghost range [%" PetscInt_FMT ",%" PetscInt_FMT "]", j, j_f, j_start_ghost, j_start_ghost + n_ghost);
1156:       PetscCheck(i_f >= i_start_ghost && i_f < i_start_ghost + m_ghost, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Processor's coarse DMDA must lie over fine DMDA, i_c %" PetscInt_FMT " i_f %" PetscInt_FMT " fine ghost range [%" PetscInt_FMT ",%" PetscInt_FMT "]", i, i_f, i_start_ghost, i_start_ghost + m_ghost);
1157:       row        = idx_f[(m_ghost * (j_f - j_start_ghost) + (i_f - i_start_ghost))];
1158:       cols[nc++] = row;
1159:     }
1160:   }
1161:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
1162:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));

1164:   PetscCall(ISCreateBlock(PetscObjectComm((PetscObject)daf), dof, nc, cols, PETSC_OWN_POINTER, &isf));
1165:   PetscCall(DMGetGlobalVector(dac, &vecc));
1166:   PetscCall(DMGetGlobalVector(daf, &vecf));
1167:   PetscCall(VecScatterCreate(vecf, isf, vecc, NULL, inject));
1168:   PetscCall(DMRestoreGlobalVector(dac, &vecc));
1169:   PetscCall(DMRestoreGlobalVector(daf, &vecf));
1170:   PetscCall(ISDestroy(&isf));
1171:   PetscFunctionReturn(PETSC_SUCCESS);
1172: }

1174: static PetscErrorCode DMCreateInjection_DA_3D(DM dac, DM daf, VecScatter *inject)
1175: {
1176:   PetscInt               i, j, k, i_start, j_start, k_start, m_f, n_f, p_f, Mx, My, Mz;
1177:   PetscInt               m_ghost, n_ghost, p_ghost, m_ghost_c, n_ghost_c, p_ghost_c;
1178:   PetscInt               i_start_ghost, j_start_ghost, k_start_ghost;
1179:   PetscInt               mx, my, mz, ratioi, ratioj, ratiok;
1180:   PetscInt               i_start_c, j_start_c, k_start_c;
1181:   PetscInt               m_c, n_c, p_c;
1182:   PetscInt               i_start_ghost_c, j_start_ghost_c, k_start_ghost_c;
1183:   PetscInt               row, nc, dof;
1184:   const PetscInt        *idx_c, *idx_f;
1185:   ISLocalToGlobalMapping ltog_f, ltog_c;
1186:   PetscInt              *cols;
1187:   DMBoundaryType         bx, by, bz;
1188:   Vec                    vecf, vecc;
1189:   IS                     isf;

1191:   PetscFunctionBegin;
1192:   PetscCall(DMDAGetInfo(dac, NULL, &Mx, &My, &Mz, NULL, NULL, NULL, NULL, NULL, &bx, &by, &bz, NULL));
1193:   PetscCall(DMDAGetInfo(daf, NULL, &mx, &my, &mz, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));

1195:   if (bx == DM_BOUNDARY_PERIODIC) {
1196:     ratioi = mx / Mx;
1197:     PetscCheck(ratioi * Mx == mx, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mx/Mx  must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
1198:   } else {
1199:     ratioi = (mx - 1) / (Mx - 1);
1200:     PetscCheck(ratioi * (Mx - 1) == mx - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mx - 1)/(Mx - 1) must be integer: mx %" PetscInt_FMT " Mx %" PetscInt_FMT, mx, Mx);
1201:   }
1202:   if (by == DM_BOUNDARY_PERIODIC) {
1203:     ratioj = my / My;
1204:     PetscCheck(ratioj * My == my, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: my/My  must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
1205:   } else {
1206:     ratioj = (my - 1) / (My - 1);
1207:     PetscCheck(ratioj * (My - 1) == my - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (my - 1)/(My - 1) must be integer: my %" PetscInt_FMT " My %" PetscInt_FMT, my, My);
1208:   }
1209:   if (bz == DM_BOUNDARY_PERIODIC) {
1210:     ratiok = mz / Mz;
1211:     PetscCheck(ratiok * Mz == mz, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: mz/Mz  must be integer: mz %" PetscInt_FMT " My %" PetscInt_FMT, mz, Mz);
1212:   } else {
1213:     ratiok = (mz - 1) / (Mz - 1);
1214:     PetscCheck(ratiok * (Mz - 1) == mz - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Ratio between levels: (mz - 1)/(Mz - 1) must be integer: mz %" PetscInt_FMT " Mz %" PetscInt_FMT, mz, Mz);
1215:   }

1217:   PetscCall(DMDAGetCorners(daf, &i_start, &j_start, &k_start, &m_f, &n_f, &p_f));
1218:   PetscCall(DMDAGetGhostCorners(daf, &i_start_ghost, &j_start_ghost, &k_start_ghost, &m_ghost, &n_ghost, &p_ghost));
1219:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltog_f));
1220:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_f, &idx_f));

1222:   PetscCall(DMDAGetCorners(dac, &i_start_c, &j_start_c, &k_start_c, &m_c, &n_c, &p_c));
1223:   PetscCall(DMDAGetGhostCorners(dac, &i_start_ghost_c, &j_start_ghost_c, &k_start_ghost_c, &m_ghost_c, &n_ghost_c, &p_ghost_c));
1224:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltog_c));
1225:   PetscCall(ISLocalToGlobalMappingGetBlockIndices(ltog_c, &idx_c));

1227:   /* loop over local fine grid nodes setting interpolation for those*/
1228:   nc = 0;
1229:   PetscCall(PetscMalloc1(n_f * m_f * p_f, &cols));
1230:   for (k = k_start_c; k < k_start_c + p_c; k++) {
1231:     for (j = j_start_c; j < j_start_c + n_c; j++) {
1232:       for (i = i_start_c; i < i_start_c + m_c; i++) {
1233:         PetscInt i_f = i * ratioi, j_f = j * ratioj, k_f = k * ratiok;
1234:         PetscCheck(k_f >= k_start_ghost && k_f < k_start_ghost + p_ghost, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
1235:                    "Processor's coarse DMDA must lie over fine DMDA  "
1236:                    "k_c %" PetscInt_FMT " k_f %" PetscInt_FMT " fine ghost range [%" PetscInt_FMT ",%" PetscInt_FMT "]",
1237:                    k, k_f, k_start_ghost, k_start_ghost + p_ghost);
1238:         PetscCheck(j_f >= j_start_ghost && j_f < j_start_ghost + n_ghost, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
1239:                    "Processor's coarse DMDA must lie over fine DMDA  "
1240:                    "j_c %" PetscInt_FMT " j_f %" PetscInt_FMT " fine ghost range [%" PetscInt_FMT ",%" PetscInt_FMT "]",
1241:                    j, j_f, j_start_ghost, j_start_ghost + n_ghost);
1242:         PetscCheck(i_f >= i_start_ghost && i_f < i_start_ghost + m_ghost, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
1243:                    "Processor's coarse DMDA must lie over fine DMDA  "
1244:                    "i_c %" PetscInt_FMT " i_f %" PetscInt_FMT " fine ghost range [%" PetscInt_FMT ",%" PetscInt_FMT "]",
1245:                    i, i_f, i_start_ghost, i_start_ghost + m_ghost);
1246:         row        = idx_f[(m_ghost * n_ghost * (k_f - k_start_ghost) + m_ghost * (j_f - j_start_ghost) + (i_f - i_start_ghost))];
1247:         cols[nc++] = row;
1248:       }
1249:     }
1250:   }
1251:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_f, &idx_f));
1252:   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(ltog_c, &idx_c));

1254:   PetscCall(ISCreateBlock(PetscObjectComm((PetscObject)daf), dof, nc, cols, PETSC_OWN_POINTER, &isf));
1255:   PetscCall(DMGetGlobalVector(dac, &vecc));
1256:   PetscCall(DMGetGlobalVector(daf, &vecf));
1257:   PetscCall(VecScatterCreate(vecf, isf, vecc, NULL, inject));
1258:   PetscCall(DMRestoreGlobalVector(dac, &vecc));
1259:   PetscCall(DMRestoreGlobalVector(daf, &vecf));
1260:   PetscCall(ISDestroy(&isf));
1261:   PetscFunctionReturn(PETSC_SUCCESS);
1262: }

1264: PetscErrorCode DMCreateInjection_DA(DM dac, DM daf, Mat *mat)
1265: {
1266:   PetscInt        dimc, Mc, Nc, Pc, mc, nc, pc, dofc, sc, dimf, Mf, Nf, Pf, mf, nf, pf, doff, sf;
1267:   DMBoundaryType  bxc, byc, bzc, bxf, byf, bzf;
1268:   DMDAStencilType stc, stf;
1269:   VecScatter      inject = NULL;

1271:   PetscFunctionBegin;
1274:   PetscAssertPointer(mat, 3);

1276:   PetscCall(DMDAGetInfo(dac, &dimc, &Mc, &Nc, &Pc, &mc, &nc, &pc, &dofc, &sc, &bxc, &byc, &bzc, &stc));
1277:   PetscCall(DMDAGetInfo(daf, &dimf, &Mf, &Nf, &Pf, &mf, &nf, &pf, &doff, &sf, &bxf, &byf, &bzf, &stf));
1278:   PetscCheck(dimc == dimf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Dimensions of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, dimc, dimf);
1279:   PetscCheck(dofc == doff, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "DOF of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, dofc, doff);
1280:   PetscCheck(sc == sf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Stencil width of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, sc, sf);
1281:   PetscCheck(bxc == bxf && byc == byf && bzc == bzf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Boundary type different in two DMDAs");
1282:   PetscCheck(stc == stf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Stencil type different in two DMDAs");
1283:   PetscCheck(Mc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coarse grid requires at least 2 points in x direction");
1284:   PetscCheck(dimc <= 1 || Nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coarse grid requires at least 2 points in y direction");
1285:   PetscCheck(dimc <= 2 || Pc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coarse grid requires at least 2 points in z direction");

1287:   if (dimc == 1) {
1288:     PetscCall(DMCreateInjection_DA_1D(dac, daf, &inject));
1289:   } else if (dimc == 2) {
1290:     PetscCall(DMCreateInjection_DA_2D(dac, daf, &inject));
1291:   } else if (dimc == 3) {
1292:     PetscCall(DMCreateInjection_DA_3D(dac, daf, &inject));
1293:   }
1294:   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)inject), inject, mat));
1295:   PetscCall(VecScatterDestroy(&inject));
1296:   PetscFunctionReturn(PETSC_SUCCESS);
1297: }

1299: // PetscClangLinter pragma ignore: -fdoc-*
1300: /*@
1301:   DMCreateAggregates - Deprecated, see DMDACreateAggregates.

1303:   Level: intermediate
1304: @*/
1305: PetscErrorCode DMCreateAggregates(DM dac, DM daf, Mat *mat)
1306: {
1307:   return DMDACreateAggregates(dac, daf, mat);
1308: }

1310: /*@
1311:   DMDACreateAggregates - Gets the aggregates that map between
1312:   grids associated with two `DMDA`

1314:   Collective

1316:   Input Parameters:
1317: + dac - the coarse grid `DMDA`
1318: - daf - the fine grid `DMDA`

1320:   Output Parameter:
1321: . rest - the restriction matrix (transpose of the projection matrix)

1323:   Level: intermediate

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

1330: .seealso: [](sec_struct), `DMRefine()`, `DMCreateInjection()`, `DMCreateInterpolation()`
1331: @*/
1332: PetscErrorCode DMDACreateAggregates(DM dac, DM daf, Mat *rest)
1333: {
1334:   PetscInt               dimc, Mc, Nc, Pc, mc, nc, pc, dofc, sc;
1335:   PetscInt               dimf, Mf, Nf, Pf, mf, nf, pf, doff, sf;
1336:   DMBoundaryType         bxc, byc, bzc, bxf, byf, bzf;
1337:   DMDAStencilType        stc, stf;
1338:   PetscInt               i, j, l;
1339:   PetscInt               i_start, j_start, l_start, m_f, n_f, p_f;
1340:   PetscInt               i_start_ghost, j_start_ghost, l_start_ghost, m_ghost, n_ghost, p_ghost;
1341:   const PetscInt        *idx_f;
1342:   PetscInt               i_c, j_c, l_c;
1343:   PetscInt               i_start_c, j_start_c, l_start_c, m_c, n_c, p_c;
1344:   PetscInt               i_start_ghost_c, j_start_ghost_c, l_start_ghost_c, m_ghost_c, n_ghost_c, p_ghost_c;
1345:   const PetscInt        *idx_c;
1346:   PetscInt               d;
1347:   PetscInt               a;
1348:   PetscInt               max_agg_size;
1349:   PetscInt              *fine_nodes;
1350:   PetscScalar           *one_vec;
1351:   PetscInt               fn_idx;
1352:   ISLocalToGlobalMapping ltogmf, ltogmc;

1354:   PetscFunctionBegin;
1357:   PetscAssertPointer(rest, 3);

1359:   PetscCall(DMDAGetInfo(dac, &dimc, &Mc, &Nc, &Pc, &mc, &nc, &pc, &dofc, &sc, &bxc, &byc, &bzc, &stc));
1360:   PetscCall(DMDAGetInfo(daf, &dimf, &Mf, &Nf, &Pf, &mf, &nf, &pf, &doff, &sf, &bxf, &byf, &bzf, &stf));
1361:   PetscCheck(dimc == dimf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Dimensions of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, dimc, dimf);
1362:   PetscCheck(dofc == doff, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "DOF of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, dofc, doff);
1363:   PetscCheck(sc == sf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Stencil width of DMDA do not match %" PetscInt_FMT " %" PetscInt_FMT, sc, sf);
1364:   PetscCheck(bxc == bxf && byc == byf && bzc == bzf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Boundary type different in two DMDAs");
1365:   PetscCheck(stc == stf, PetscObjectComm((PetscObject)daf), PETSC_ERR_ARG_INCOMP, "Stencil type different in two DMDAs");

1367:   PetscCheck(Mf >= Mc, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Coarse grid has more points than fine grid, Mc %" PetscInt_FMT ", Mf %" PetscInt_FMT, Mc, Mf);
1368:   PetscCheck(Nf >= Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Coarse grid has more points than fine grid, Nc %" PetscInt_FMT ", Nf %" PetscInt_FMT, Nc, Nf);
1369:   PetscCheck(Pf >= Pc, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Coarse grid has more points than fine grid, Pc %" PetscInt_FMT ", Pf %" PetscInt_FMT, Pc, Pf);

1371:   if (Pc < 0) Pc = 1;
1372:   if (Pf < 0) Pf = 1;
1373:   if (Nc < 0) Nc = 1;
1374:   if (Nf < 0) Nf = 1;

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

1379:   PetscCall(DMGetLocalToGlobalMapping(daf, &ltogmf));
1380:   PetscCall(ISLocalToGlobalMappingGetIndices(ltogmf, &idx_f));

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

1385:   PetscCall(DMGetLocalToGlobalMapping(dac, &ltogmc));
1386:   PetscCall(ISLocalToGlobalMappingGetIndices(ltogmc, &idx_c));

1388:   /*
1389:      Basic idea is as follows. Here's a 2D example, suppose r_x, r_y are the ratios
1390:      for dimension 1 and 2 respectively.
1391:      Let (i,j) be a coarse grid node. All the fine grid nodes between r_x*i and r_x*(i+1)
1392:      and r_y*j and r_y*(j+1) will be grouped into the same coarse grid aggregate.
1393:      Each specific dof on the fine grid is mapped to one dof on the coarse grid.
1394:   */

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

1398:   /* create the matrix that will contain the restriction operator */
1399:   PetscCall(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, max_agg_size, NULL, max_agg_size, NULL, rest));

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

1405:   /* loop over all coarse nodes */
1406:   for (l_c = l_start_c; l_c < l_start_c + p_c; l_c++) {
1407:     for (j_c = j_start_c; j_c < j_start_c + n_c; j_c++) {
1408:       for (i_c = i_start_c; i_c < i_start_c + m_c; i_c++) {
1409:         for (d = 0; d < dofc; d++) {
1410:           /* convert to local "natural" numbering and then to PETSc global numbering */
1411:           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;

1413:           fn_idx = 0;
1414:           /* Corresponding fine points are all points (i_f, j_f, l_f) such that
1415:              i_c*Mf/Mc <= i_f < (i_c+1)*Mf/Mc
1416:              (same for other dimensions)
1417:           */
1418:           for (l = l_c * Pf / Pc; l < PetscMin((l_c + 1) * Pf / Pc, Pf); l++) {
1419:             for (j = j_c * Nf / Nc; j < PetscMin((j_c + 1) * Nf / Nc, Nf); j++) {
1420:               for (i = i_c * Mf / Mc; i < PetscMin((i_c + 1) * Mf / Mc, Mf); i++) {
1421:                 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;
1422:                 fn_idx++;
1423:               }
1424:             }
1425:           }
1426:           /* add all these points to one aggregate */
1427:           PetscCall(MatSetValues(*rest, 1, &a, fn_idx, fine_nodes, one_vec, INSERT_VALUES));
1428:         }
1429:       }
1430:     }
1431:   }
1432:   PetscCall(ISLocalToGlobalMappingRestoreIndices(ltogmf, &idx_f));
1433:   PetscCall(ISLocalToGlobalMappingRestoreIndices(ltogmc, &idx_c));
1434:   PetscCall(PetscFree2(one_vec, fine_nodes));
1435:   PetscCall(MatAssemblyBegin(*rest, MAT_FINAL_ASSEMBLY));
1436:   PetscCall(MatAssemblyEnd(*rest, MAT_FINAL_ASSEMBLY));
1437:   PetscFunctionReturn(PETSC_SUCCESS);
1438: }