xref: /petsc/src/dm/impls/sliced/sliced.c (revision 6d5076fa512d0d8e4e12a3685978afdbfee2b1a4)
1 #include <petscdmsliced.h> /*I      "petscdmsliced.h" I*/
2 #include <petscmat.h>
3 #include <petsc/private/dmimpl.h>
4 
5 /* CSR storage of the nonzero structure of a bs*bs matrix */
6 typedef struct {
7   PetscInt bs, nz, *i, *j;
8 } DMSlicedBlockFills;
9 
10 typedef struct {
11   PetscInt            bs, n, N, Nghosts, *ghosts;
12   PetscInt            d_nz, o_nz, *d_nnz, *o_nnz;
13   DMSlicedBlockFills *dfill, *ofill;
14 } DM_Sliced;
15 
16 PetscErrorCode DMCreateMatrix_Sliced(DM dm, Mat *J)
17 {
18   PetscInt              *globals, *sd_nnz, *so_nnz, rstart, bs, i;
19   ISLocalToGlobalMapping lmap;
20   void (*aij)(void) = NULL;
21   DM_Sliced *slice  = (DM_Sliced *)dm->data;
22 
23   PetscFunctionBegin;
24   bs = slice->bs;
25   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
26   PetscCall(MatSetSizes(*J, slice->n * bs, slice->n * bs, PETSC_DETERMINE, PETSC_DETERMINE));
27   PetscCall(MatSetBlockSize(*J, bs));
28   PetscCall(MatSetType(*J, dm->mattype));
29   PetscCall(MatSeqBAIJSetPreallocation(*J, bs, slice->d_nz, slice->d_nnz));
30   PetscCall(MatMPIBAIJSetPreallocation(*J, bs, slice->d_nz, slice->d_nnz, slice->o_nz, slice->o_nnz));
31   /* In general, we have to do extra work to preallocate for scalar (AIJ) matrices so we check whether it will do any
32   * good before going on with it. */
33   PetscCall(PetscObjectQueryFunction((PetscObject)*J, "MatMPIAIJSetPreallocation_C", &aij));
34   if (!aij) PetscCall(PetscObjectQueryFunction((PetscObject)*J, "MatSeqAIJSetPreallocation_C", &aij));
35   if (aij) {
36     if (bs == 1) {
37       PetscCall(MatSeqAIJSetPreallocation(*J, slice->d_nz, slice->d_nnz));
38       PetscCall(MatMPIAIJSetPreallocation(*J, slice->d_nz, slice->d_nnz, slice->o_nz, slice->o_nnz));
39     } else if (!slice->d_nnz) {
40       PetscCall(MatSeqAIJSetPreallocation(*J, slice->d_nz * bs, NULL));
41       PetscCall(MatMPIAIJSetPreallocation(*J, slice->d_nz * bs, NULL, slice->o_nz * bs, NULL));
42     } else {
43       /* The user has provided preallocation per block-row, convert it to per scalar-row respecting DMSlicedSetBlockFills() if applicable */
44       PetscCall(PetscMalloc2(slice->n * bs, &sd_nnz, (!!slice->o_nnz) * slice->n * bs, &so_nnz));
45       for (i = 0; i < slice->n * bs; i++) {
46         sd_nnz[i] = (slice->d_nnz[i / bs] - 1) * (slice->ofill ? slice->ofill->i[i % bs + 1] - slice->ofill->i[i % bs] : bs) + (slice->dfill ? slice->dfill->i[i % bs + 1] - slice->dfill->i[i % bs] : bs);
47         if (so_nnz) so_nnz[i] = slice->o_nnz[i / bs] * (slice->ofill ? slice->ofill->i[i % bs + 1] - slice->ofill->i[i % bs] : bs);
48       }
49       PetscCall(MatSeqAIJSetPreallocation(*J, slice->d_nz * bs, sd_nnz));
50       PetscCall(MatMPIAIJSetPreallocation(*J, slice->d_nz * bs, sd_nnz, slice->o_nz * bs, so_nnz));
51       PetscCall(PetscFree2(sd_nnz, so_nnz));
52     }
53   }
54 
55   /* Set up the local to global map.  For the scalar map, we have to translate to entry-wise indexing instead of block-wise. */
56   PetscCall(PetscMalloc1(slice->n + slice->Nghosts, &globals));
57   PetscCall(MatGetOwnershipRange(*J, &rstart, NULL));
58   for (i = 0; i < slice->n; i++) globals[i] = rstart / bs + i;
59 
60   for (i = 0; i < slice->Nghosts; i++) globals[slice->n + i] = slice->ghosts[i];
61   PetscCall(ISLocalToGlobalMappingCreate(PETSC_COMM_SELF, bs, slice->n + slice->Nghosts, globals, PETSC_OWN_POINTER, &lmap));
62   PetscCall(MatSetLocalToGlobalMapping(*J, lmap, lmap));
63   PetscCall(ISLocalToGlobalMappingDestroy(&lmap));
64   PetscCall(MatSetDM(*J, dm));
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 /*@C
69     DMSlicedSetGhosts - Sets the global indices of other processes elements that will
70       be ghosts on this process
71 
72     Not Collective
73 
74     Input Parameters:
75 +    slice - the `DMSLICED` object
76 .    bs - block size
77 .    nlocal - number of local (owned, non-ghost) blocks
78 .    Nghosts - number of ghost blocks on this process
79 -    ghosts - global indices of each ghost block
80 
81     Level: advanced
82 
83 .seealso `DMDestroy()`, `DMCreateGlobalVector()`
84 @*/
85 PetscErrorCode DMSlicedSetGhosts(DM dm, PetscInt bs, PetscInt nlocal, PetscInt Nghosts, const PetscInt ghosts[])
86 {
87   DM_Sliced *slice = (DM_Sliced *)dm->data;
88 
89   PetscFunctionBegin;
90   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
91   PetscCall(PetscFree(slice->ghosts));
92   PetscCall(PetscMalloc1(Nghosts, &slice->ghosts));
93   PetscCall(PetscArraycpy(slice->ghosts, ghosts, Nghosts));
94   slice->bs      = bs;
95   slice->n       = nlocal;
96   slice->Nghosts = Nghosts;
97   PetscFunctionReturn(PETSC_SUCCESS);
98 }
99 
100 /*@C
101     DMSlicedSetPreallocation - sets the matrix memory preallocation for matrices computed by DMSliced
102 
103     Not Collective
104 
105     Input Parameters:
106 +    slice - the `DM` object
107 .    d_nz  - number of block nonzeros per block row in diagonal portion of local
108            submatrix  (same for all local rows)
109 .    d_nnz - array containing the number of block nonzeros in the various block rows
110            of the in diagonal portion of the local (possibly different for each block
111            row) or NULL.
112 .    o_nz  - number of block nonzeros per block row in the off-diagonal portion of local
113            submatrix (same for all local rows).
114 -    o_nnz - array containing the number of nonzeros in the various block rows of the
115            off-diagonal portion of the local submatrix (possibly different for
116            each block row) or `NULL`.
117 
118     Level: advanced
119 
120     Note:
121     See `MatMPIBAIJSetPreallocation()` for more details on preallocation.  If a scalar matrix (AIJ) is
122     obtained with `DMSlicedGetMatrix()`, the correct preallocation will be set, respecting `DMSlicedSetBlockFills()`.
123 
124 .seealso `DMDestroy()`, `DMCreateGlobalVector()`, `MatMPIAIJSetPreallocation()`,
125          `MatMPIBAIJSetPreallocation()`, `DMSlicedGetMatrix()`, `DMSlicedSetBlockFills()`
126 @*/
127 PetscErrorCode DMSlicedSetPreallocation(DM dm, PetscInt d_nz, const PetscInt d_nnz[], PetscInt o_nz, const PetscInt o_nnz[])
128 {
129   DM_Sliced *slice = (DM_Sliced *)dm->data;
130 
131   PetscFunctionBegin;
132   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
133   slice->d_nz  = d_nz;
134   slice->d_nnz = (PetscInt *)d_nnz;
135   slice->o_nz  = o_nz;
136   slice->o_nnz = (PetscInt *)o_nnz;
137   PetscFunctionReturn(PETSC_SUCCESS);
138 }
139 
140 static PetscErrorCode DMSlicedSetBlockFills_Private(PetscInt bs, const PetscInt *fill, DMSlicedBlockFills **inf)
141 {
142   PetscInt            i, j, nz, *fi, *fj;
143   DMSlicedBlockFills *f;
144 
145   PetscFunctionBegin;
146   PetscValidPointer(inf, 3);
147   if (*inf) PetscCall(PetscFree3(*inf, (*inf)->i, (*inf)->j));
148   if (!fill) PetscFunctionReturn(PETSC_SUCCESS);
149   for (i = 0, nz = 0; i < bs * bs; i++)
150     if (fill[i]) nz++;
151   PetscCall(PetscMalloc3(1, &f, bs + 1, &fi, nz, &fj));
152   f->bs = bs;
153   f->nz = nz;
154   f->i  = fi;
155   f->j  = fj;
156   for (i = 0, nz = 0; i < bs; i++) {
157     fi[i] = nz;
158     for (j = 0; j < bs; j++)
159       if (fill[i * bs + j]) fj[nz++] = j;
160   }
161   fi[i] = nz;
162   *inf  = f;
163   PetscFunctionReturn(PETSC_SUCCESS);
164 }
165 
166 /*@C
167     DMSlicedSetBlockFills - Sets the fill pattern in each block for a multi-component problem
168     of the matrix returned by `DMSlicedGetMatrix()`.
169 
170     Logically Collective
171 
172     Input Parameters:
173 +   sliced - the `DM` object
174 .   dfill - the fill pattern in the diagonal block (may be `NULL`, means use dense block)
175 -   ofill - the fill pattern in the off-diagonal blocks
176 
177     Level: advanced
178 
179     Note:
180     This only makes sense for multicomponent problems using scalar matrix formats (AIJ).
181     See `DMDASetBlockFills()` for example usage.
182 
183 .seealso `DMSlicedGetMatrix()`, `DMDASetBlockFills()`
184 @*/
185 PetscErrorCode DMSlicedSetBlockFills(DM dm, const PetscInt *dfill, const PetscInt *ofill)
186 {
187   DM_Sliced *slice = (DM_Sliced *)dm->data;
188 
189   PetscFunctionBegin;
190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
191   PetscCall(DMSlicedSetBlockFills_Private(slice->bs, dfill, &slice->dfill));
192   PetscCall(DMSlicedSetBlockFills_Private(slice->bs, ofill, &slice->ofill));
193   PetscFunctionReturn(PETSC_SUCCESS);
194 }
195 
196 static PetscErrorCode DMDestroy_Sliced(DM dm)
197 {
198   DM_Sliced *slice = (DM_Sliced *)dm->data;
199 
200   PetscFunctionBegin;
201   PetscCall(PetscFree(slice->ghosts));
202   if (slice->dfill) PetscCall(PetscFree3(slice->dfill, slice->dfill->i, slice->dfill->j));
203   if (slice->ofill) PetscCall(PetscFree3(slice->ofill, slice->ofill->i, slice->ofill->j));
204   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
205   PetscCall(PetscFree(slice));
206   PetscFunctionReturn(PETSC_SUCCESS);
207 }
208 
209 static PetscErrorCode DMCreateGlobalVector_Sliced(DM dm, Vec *gvec)
210 {
211   DM_Sliced *slice = (DM_Sliced *)dm->data;
212 
213   PetscFunctionBegin;
214   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
215   PetscValidPointer(gvec, 2);
216   *gvec = NULL;
217   PetscCall(VecCreateGhostBlock(PetscObjectComm((PetscObject)dm), slice->bs, slice->n * slice->bs, PETSC_DETERMINE, slice->Nghosts, slice->ghosts, gvec));
218   PetscCall(VecSetDM(*gvec, dm));
219   PetscFunctionReturn(PETSC_SUCCESS);
220 }
221 
222 static PetscErrorCode DMGlobalToLocalBegin_Sliced(DM da, Vec g, InsertMode mode, Vec l)
223 {
224   PetscBool flg;
225 
226   PetscFunctionBegin;
227   PetscCall(VecGhostIsLocalForm(g, l, &flg));
228   PetscCheck(flg, PetscObjectComm((PetscObject)da), PETSC_ERR_ARG_WRONG, "Local vector is not local form of global vector");
229   PetscCall(VecGhostUpdateEnd(g, mode, SCATTER_FORWARD));
230   PetscCall(VecGhostUpdateBegin(g, mode, SCATTER_FORWARD));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode DMGlobalToLocalEnd_Sliced(DM da, Vec g, InsertMode mode, Vec l)
235 {
236   PetscBool flg;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGhostIsLocalForm(g, l, &flg));
240   PetscCheck(flg, PetscObjectComm((PetscObject)da), PETSC_ERR_ARG_WRONG, "Local vector is not local form of global vector");
241   PetscCall(VecGhostUpdateEnd(g, mode, SCATTER_FORWARD));
242   PetscFunctionReturn(PETSC_SUCCESS);
243 }
244 
245 /*MC
246    DMSLICED = "sliced" - A DM object that is used to manage data for a general graph. Uses VecCreateGhost() ghosted vectors for storing the fields
247 
248    See DMCreateSliced() for details.
249 
250   Level: intermediate
251 
252 .seealso: `DMType`, `DMCOMPOSITE`, `DMCreateSliced()`, `DMCreate()`
253 M*/
254 
255 PETSC_EXTERN PetscErrorCode DMCreate_Sliced(DM p)
256 {
257   DM_Sliced *slice;
258 
259   PetscFunctionBegin;
260   PetscCall(PetscNew(&slice));
261   p->data = slice;
262 
263   p->ops->createglobalvector = DMCreateGlobalVector_Sliced;
264   p->ops->creatematrix       = DMCreateMatrix_Sliced;
265   p->ops->globaltolocalbegin = DMGlobalToLocalBegin_Sliced;
266   p->ops->globaltolocalend   = DMGlobalToLocalEnd_Sliced;
267   p->ops->destroy            = DMDestroy_Sliced;
268   PetscFunctionReturn(PETSC_SUCCESS);
269 }
270 
271 /*@C
272     DMSlicedCreate - Creates a `DM` object, used to manage data for a unstructured problem
273 
274     Collective
275 
276     Input Parameters:
277 +   comm - the processors that will share the global vector
278 .   bs - the block size
279 .   nlocal - number of vector entries on this process
280 .   Nghosts - number of ghost points needed on this process
281 .   ghosts - global indices of all ghost points for this process
282 .   d_nnz - matrix preallocation information representing coupling within this process
283 -   o_nnz - matrix preallocation information representing coupling between this process and other processes
284 
285     Output Parameters:
286 .   slice - the slice object
287 
288     Level: advanced
289 
290     Notes:
291         This `DM` does not support `DMCreateLocalVector()`, `DMGlobalToLocalBegin()`, and `DMGlobalToLocalEnd()` instead one directly uses
292         `VecGhostGetLocalForm()` and `VecGhostRestoreLocalForm()` to access the local representation and `VecGhostUpdateBegin()` and `VecGhostUpdateEnd()` to update
293         the ghost points.
294 
295         One can use `DMGlobalToLocalBegin()`, and `DMGlobalToLocalEnd()` instead of `VecGhostUpdateBegin()` and `VecGhostUpdateEnd()`.
296 
297 .seealso `DMDestroy()`, `DMCreateGlobalVector()`, `DMSetType()`, `DMSLICED`, `DMSlicedSetGhosts()`, `DMSlicedSetPreallocation()`, `VecGhostUpdateBegin()`, `VecGhostUpdateEnd()`,
298          `VecGhostGetLocalForm()`, `VecGhostRestoreLocalForm()`
299 
300 @*/
301 PetscErrorCode DMSlicedCreate(MPI_Comm comm, PetscInt bs, PetscInt nlocal, PetscInt Nghosts, const PetscInt ghosts[], const PetscInt d_nnz[], const PetscInt o_nnz[], DM *dm)
302 {
303   PetscFunctionBegin;
304   PetscValidPointer(dm, 8);
305   PetscCall(DMCreate(comm, dm));
306   PetscCall(DMSetType(*dm, DMSLICED));
307   PetscCall(DMSlicedSetGhosts(*dm, bs, nlocal, Nghosts, ghosts));
308   if (d_nnz) PetscCall(DMSlicedSetPreallocation(*dm, 0, d_nnz, 0, o_nnz));
309   PetscFunctionReturn(PETSC_SUCCESS);
310 }
311