1 #include <../src/mat/impls/aij/mpi/mpiaij.h> /*I "petscmat.h" I*/ 2 #include <../src/mat/impls/sell/mpi/mpisell.h> /*I "petscmat.h" I*/ 3 #include <petsc/private/vecimpl.h> 4 #include <petsc/private/isimpl.h> 5 #include <petscblaslapack.h> 6 #include <petscsf.h> 7 8 /*MC 9 MATSELL - MATSELL = "sell" - A matrix type to be used for sparse matrices. 10 11 This matrix type is identical to `MATSEQSELL` when constructed with a single process communicator, 12 and `MATMPISELL` otherwise. As a result, for single process communicators, 13 `MatSeqSELLSetPreallocation()` is supported, and similarly `MatMPISELLSetPreallocation()` is supported 14 for communicators controlling multiple processes. It is recommended that you call both of 15 the above preallocation routines for simplicity. 16 17 Options Database Keys: 18 . -mat_type sell - sets the matrix type to `MATSELL` during a call to `MatSetFromOptions()` 19 20 Level: beginner 21 22 .seealso: `Mat`, `MATAIJ`, `MATBAIJ`, `MATSBAIJ`, `MatCreateSELL()`, `MatCreateSeqSELL()`, `MATSEQSELL`, `MATMPISELL` 23 M*/ 24 25 PetscErrorCode MatDiagonalSet_MPISELL(Mat Y, Vec D, InsertMode is) 26 { 27 Mat_MPISELL *sell = (Mat_MPISELL *)Y->data; 28 29 PetscFunctionBegin; 30 if (Y->assembled && Y->rmap->rstart == Y->cmap->rstart && Y->rmap->rend == Y->cmap->rend) { 31 PetscCall(MatDiagonalSet(sell->A, D, is)); 32 } else { 33 PetscCall(MatDiagonalSet_Default(Y, D, is)); 34 } 35 PetscFunctionReturn(PETSC_SUCCESS); 36 } 37 38 /* 39 Local utility routine that creates a mapping from the global column 40 number to the local number in the off-diagonal part of the local 41 storage of the matrix. When PETSC_USE_CTABLE is used this is scalable at 42 a slightly higher hash table cost; without it it is not scalable (each processor 43 has an order N integer array but is fast to access. 44 */ 45 PetscErrorCode MatCreateColmap_MPISELL_Private(Mat mat) 46 { 47 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 48 PetscInt n = sell->B->cmap->n, i; 49 50 PetscFunctionBegin; 51 PetscCheck(sell->garray, PETSC_COMM_SELF, PETSC_ERR_PLIB, "MPISELL Matrix was assembled but is missing garray"); 52 #if defined(PETSC_USE_CTABLE) 53 PetscCall(PetscHMapICreateWithSize(n, &sell->colmap)); 54 for (i = 0; i < n; i++) PetscCall(PetscHMapISet(sell->colmap, sell->garray[i] + 1, i + 1)); 55 #else 56 PetscCall(PetscCalloc1(mat->cmap->N + 1, &sell->colmap)); 57 for (i = 0; i < n; i++) sell->colmap[sell->garray[i]] = i + 1; 58 #endif 59 PetscFunctionReturn(PETSC_SUCCESS); 60 } 61 62 #define MatSetValues_SeqSELL_A_Private(row, col, value, addv, orow, ocol) \ 63 { \ 64 if (col <= lastcol1) low1 = 0; \ 65 else high1 = nrow1; \ 66 lastcol1 = col; \ 67 while (high1 - low1 > 5) { \ 68 t = (low1 + high1) / 2; \ 69 if (*(cp1 + 8 * t) > col) high1 = t; \ 70 else low1 = t; \ 71 } \ 72 for (_i = low1; _i < high1; _i++) { \ 73 if (*(cp1 + 8 * _i) > col) break; \ 74 if (*(cp1 + 8 * _i) == col) { \ 75 if (addv == ADD_VALUES) *(vp1 + 8 * _i) += value; \ 76 else *(vp1 + 8 * _i) = value; \ 77 goto a_noinsert; \ 78 } \ 79 } \ 80 if (value == 0.0 && ignorezeroentries) { \ 81 low1 = 0; \ 82 high1 = nrow1; \ 83 goto a_noinsert; \ 84 } \ 85 if (nonew == 1) { \ 86 low1 = 0; \ 87 high1 = nrow1; \ 88 goto a_noinsert; \ 89 } \ 90 PetscCheck(nonew != -1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Inserting a new nonzero at global row/column (%" PetscInt_FMT ", %" PetscInt_FMT ") into matrix", orow, ocol); \ 91 MatSeqXSELLReallocateSELL(A, am, 1, nrow1, a->sliidx, row / 8, row, col, a->colidx, a->val, cp1, vp1, nonew, MatScalar); \ 92 /* shift up all the later entries in this row */ \ 93 for (ii = nrow1 - 1; ii >= _i; ii--) { \ 94 *(cp1 + 8 * (ii + 1)) = *(cp1 + 8 * ii); \ 95 *(vp1 + 8 * (ii + 1)) = *(vp1 + 8 * ii); \ 96 } \ 97 *(cp1 + 8 * _i) = col; \ 98 *(vp1 + 8 * _i) = value; \ 99 a->nz++; \ 100 nrow1++; \ 101 A->nonzerostate++; \ 102 a_noinsert:; \ 103 a->rlen[row] = nrow1; \ 104 } 105 106 #define MatSetValues_SeqSELL_B_Private(row, col, value, addv, orow, ocol) \ 107 { \ 108 if (col <= lastcol2) low2 = 0; \ 109 else high2 = nrow2; \ 110 lastcol2 = col; \ 111 while (high2 - low2 > 5) { \ 112 t = (low2 + high2) / 2; \ 113 if (*(cp2 + 8 * t) > col) high2 = t; \ 114 else low2 = t; \ 115 } \ 116 for (_i = low2; _i < high2; _i++) { \ 117 if (*(cp2 + 8 * _i) > col) break; \ 118 if (*(cp2 + 8 * _i) == col) { \ 119 if (addv == ADD_VALUES) *(vp2 + 8 * _i) += value; \ 120 else *(vp2 + 8 * _i) = value; \ 121 goto b_noinsert; \ 122 } \ 123 } \ 124 if (value == 0.0 && ignorezeroentries) { \ 125 low2 = 0; \ 126 high2 = nrow2; \ 127 goto b_noinsert; \ 128 } \ 129 if (nonew == 1) { \ 130 low2 = 0; \ 131 high2 = nrow2; \ 132 goto b_noinsert; \ 133 } \ 134 PetscCheck(nonew != -1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Inserting a new nonzero at global row/column (%" PetscInt_FMT ", %" PetscInt_FMT ") into matrix", orow, ocol); \ 135 MatSeqXSELLReallocateSELL(B, bm, 1, nrow2, b->sliidx, row / 8, row, col, b->colidx, b->val, cp2, vp2, nonew, MatScalar); \ 136 /* shift up all the later entries in this row */ \ 137 for (ii = nrow2 - 1; ii >= _i; ii--) { \ 138 *(cp2 + 8 * (ii + 1)) = *(cp2 + 8 * ii); \ 139 *(vp2 + 8 * (ii + 1)) = *(vp2 + 8 * ii); \ 140 } \ 141 *(cp2 + 8 * _i) = col; \ 142 *(vp2 + 8 * _i) = value; \ 143 b->nz++; \ 144 nrow2++; \ 145 B->nonzerostate++; \ 146 b_noinsert:; \ 147 b->rlen[row] = nrow2; \ 148 } 149 150 PetscErrorCode MatSetValues_MPISELL(Mat mat, PetscInt m, const PetscInt im[], PetscInt n, const PetscInt in[], const PetscScalar v[], InsertMode addv) 151 { 152 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 153 PetscScalar value; 154 PetscInt i, j, rstart = mat->rmap->rstart, rend = mat->rmap->rend, shift1, shift2; 155 PetscInt cstart = mat->cmap->rstart, cend = mat->cmap->rend, row, col; 156 PetscBool roworiented = sell->roworiented; 157 158 /* Some Variables required in the macro */ 159 Mat A = sell->A; 160 Mat_SeqSELL *a = (Mat_SeqSELL *)A->data; 161 PetscBool ignorezeroentries = a->ignorezeroentries, found; 162 Mat B = sell->B; 163 Mat_SeqSELL *b = (Mat_SeqSELL *)B->data; 164 PetscInt *cp1, *cp2, ii, _i, nrow1, nrow2, low1, high1, low2, high2, t, lastcol1, lastcol2; 165 MatScalar *vp1, *vp2; 166 167 PetscFunctionBegin; 168 for (i = 0; i < m; i++) { 169 if (im[i] < 0) continue; 170 PetscCheck(im[i] < mat->rmap->N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Row too large: row %" PetscInt_FMT " max %" PetscInt_FMT, im[i], mat->rmap->N - 1); 171 if (im[i] >= rstart && im[i] < rend) { 172 row = im[i] - rstart; 173 lastcol1 = -1; 174 shift1 = a->sliidx[row >> 3] + (row & 0x07); /* starting index of the row */ 175 cp1 = a->colidx + shift1; 176 vp1 = a->val + shift1; 177 nrow1 = a->rlen[row]; 178 low1 = 0; 179 high1 = nrow1; 180 lastcol2 = -1; 181 shift2 = b->sliidx[row >> 3] + (row & 0x07); /* starting index of the row */ 182 cp2 = b->colidx + shift2; 183 vp2 = b->val + shift2; 184 nrow2 = b->rlen[row]; 185 low2 = 0; 186 high2 = nrow2; 187 188 for (j = 0; j < n; j++) { 189 if (roworiented) value = v[i * n + j]; 190 else value = v[i + j * m]; 191 if (ignorezeroentries && value == 0.0 && (addv == ADD_VALUES)) continue; 192 if (in[j] >= cstart && in[j] < cend) { 193 col = in[j] - cstart; 194 MatSetValue_SeqSELL_Private(A, row, col, value, addv, im[i], in[j], cp1, vp1, lastcol1, low1, high1); /* set one value */ 195 } else if (in[j] < 0) { 196 continue; 197 } else { 198 PetscCheck(in[j] < mat->cmap->N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Column too large: col %" PetscInt_FMT " max %" PetscInt_FMT, in[j], mat->cmap->N - 1); 199 if (mat->was_assembled) { 200 if (!sell->colmap) PetscCall(MatCreateColmap_MPISELL_Private(mat)); 201 #if defined(PETSC_USE_CTABLE) 202 PetscCall(PetscHMapIGetWithDefault(sell->colmap, in[j] + 1, 0, &col)); 203 col--; 204 #else 205 col = sell->colmap[in[j]] - 1; 206 #endif 207 if (col < 0 && !((Mat_SeqSELL *)(sell->B->data))->nonew) { 208 PetscCall(MatDisAssemble_MPISELL(mat)); 209 col = in[j]; 210 /* Reinitialize the variables required by MatSetValues_SeqSELL_B_Private() */ 211 B = sell->B; 212 b = (Mat_SeqSELL *)B->data; 213 shift2 = b->sliidx[row >> 3] + (row & 0x07); /* starting index of the row */ 214 cp2 = b->colidx + shift2; 215 vp2 = b->val + shift2; 216 nrow2 = b->rlen[row]; 217 low2 = 0; 218 high2 = nrow2; 219 } else { 220 PetscCheck(col >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Inserting a new nonzero at global row/column (%" PetscInt_FMT ", %" PetscInt_FMT ") into matrix", im[i], in[j]); 221 } 222 } else col = in[j]; 223 MatSetValue_SeqSELL_Private(B, row, col, value, addv, im[i], in[j], cp2, vp2, lastcol2, low2, high2); /* set one value */ 224 } 225 } 226 } else { 227 PetscCheck(!mat->nooffprocentries, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Setting off process row %" PetscInt_FMT " even though MatSetOption(,MAT_NO_OFF_PROC_ENTRIES,PETSC_TRUE) was set", im[i]); 228 if (!sell->donotstash) { 229 mat->assembled = PETSC_FALSE; 230 if (roworiented) { 231 PetscCall(MatStashValuesRow_Private(&mat->stash, im[i], n, in, v + i * n, (PetscBool)(ignorezeroentries && (addv == ADD_VALUES)))); 232 } else { 233 PetscCall(MatStashValuesCol_Private(&mat->stash, im[i], n, in, v + i, m, (PetscBool)(ignorezeroentries && (addv == ADD_VALUES)))); 234 } 235 } 236 } 237 } 238 PetscFunctionReturn(PETSC_SUCCESS); 239 } 240 241 PetscErrorCode MatGetValues_MPISELL(Mat mat, PetscInt m, const PetscInt idxm[], PetscInt n, const PetscInt idxn[], PetscScalar v[]) 242 { 243 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 244 PetscInt i, j, rstart = mat->rmap->rstart, rend = mat->rmap->rend; 245 PetscInt cstart = mat->cmap->rstart, cend = mat->cmap->rend, row, col; 246 247 PetscFunctionBegin; 248 for (i = 0; i < m; i++) { 249 if (idxm[i] < 0) continue; /* negative row */ 250 PetscCheck(idxm[i] < mat->rmap->N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Row too large: row %" PetscInt_FMT " max %" PetscInt_FMT, idxm[i], mat->rmap->N - 1); 251 if (idxm[i] >= rstart && idxm[i] < rend) { 252 row = idxm[i] - rstart; 253 for (j = 0; j < n; j++) { 254 if (idxn[j] < 0) continue; /* negative column */ 255 PetscCheck(idxn[j] < mat->cmap->N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Column too large: col %" PetscInt_FMT " max %" PetscInt_FMT, idxn[j], mat->cmap->N - 1); 256 if (idxn[j] >= cstart && idxn[j] < cend) { 257 col = idxn[j] - cstart; 258 PetscCall(MatGetValues(sell->A, 1, &row, 1, &col, v + i * n + j)); 259 } else { 260 if (!sell->colmap) PetscCall(MatCreateColmap_MPISELL_Private(mat)); 261 #if defined(PETSC_USE_CTABLE) 262 PetscCall(PetscHMapIGetWithDefault(sell->colmap, idxn[j] + 1, 0, &col)); 263 col--; 264 #else 265 col = sell->colmap[idxn[j]] - 1; 266 #endif 267 if ((col < 0) || (sell->garray[col] != idxn[j])) *(v + i * n + j) = 0.0; 268 else PetscCall(MatGetValues(sell->B, 1, &row, 1, &col, v + i * n + j)); 269 } 270 } 271 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Only local values currently supported"); 272 } 273 PetscFunctionReturn(PETSC_SUCCESS); 274 } 275 276 extern PetscErrorCode MatMultDiagonalBlock_MPISELL(Mat, Vec, Vec); 277 278 PetscErrorCode MatAssemblyBegin_MPISELL(Mat mat, MatAssemblyType mode) 279 { 280 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 281 PetscInt nstash, reallocs; 282 283 PetscFunctionBegin; 284 if (sell->donotstash || mat->nooffprocentries) PetscFunctionReturn(PETSC_SUCCESS); 285 286 PetscCall(MatStashScatterBegin_Private(mat, &mat->stash, mat->rmap->range)); 287 PetscCall(MatStashGetInfo_Private(&mat->stash, &nstash, &reallocs)); 288 PetscCall(PetscInfo(sell->A, "Stash has %" PetscInt_FMT " entries, uses %" PetscInt_FMT " mallocs.\n", nstash, reallocs)); 289 PetscFunctionReturn(PETSC_SUCCESS); 290 } 291 292 PetscErrorCode MatAssemblyEnd_MPISELL(Mat mat, MatAssemblyType mode) 293 { 294 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 295 PetscMPIInt n; 296 PetscInt i, flg; 297 PetscInt *row, *col; 298 PetscScalar *val; 299 PetscBool other_disassembled; 300 /* do not use 'b = (Mat_SeqSELL*)sell->B->data' as B can be reset in disassembly */ 301 PetscFunctionBegin; 302 if (!sell->donotstash && !mat->nooffprocentries) { 303 while (1) { 304 PetscCall(MatStashScatterGetMesg_Private(&mat->stash, &n, &row, &col, &val, &flg)); 305 if (!flg) break; 306 307 for (i = 0; i < n; i++) { /* assemble one by one */ 308 PetscCall(MatSetValues_MPISELL(mat, 1, row + i, 1, col + i, val + i, mat->insertmode)); 309 } 310 } 311 PetscCall(MatStashScatterEnd_Private(&mat->stash)); 312 } 313 PetscCall(MatAssemblyBegin(sell->A, mode)); 314 PetscCall(MatAssemblyEnd(sell->A, mode)); 315 316 /* 317 determine if any processor has disassembled, if so we must 318 also disassemble ourselves, in order that we may reassemble. 319 */ 320 /* 321 if nonzero structure of submatrix B cannot change then we know that 322 no processor disassembled thus we can skip this stuff 323 */ 324 if (!((Mat_SeqSELL *)sell->B->data)->nonew) { 325 PetscCall(MPIU_Allreduce(&mat->was_assembled, &other_disassembled, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)mat))); 326 PetscCheck(!mat->was_assembled || other_disassembled, PETSC_COMM_SELF, PETSC_ERR_SUP, "MatDisAssemble not implemented yet"); 327 } 328 if (!mat->was_assembled && mode == MAT_FINAL_ASSEMBLY) PetscCall(MatSetUpMultiply_MPISELL(mat)); 329 /* 330 PetscCall(MatSetOption(sell->B,MAT_USE_INODES,PETSC_FALSE)); 331 */ 332 PetscCall(MatAssemblyBegin(sell->B, mode)); 333 PetscCall(MatAssemblyEnd(sell->B, mode)); 334 PetscCall(PetscFree2(sell->rowvalues, sell->rowindices)); 335 sell->rowvalues = NULL; 336 PetscCall(VecDestroy(&sell->diag)); 337 338 /* if no new nonzero locations are allowed in matrix then only set the matrix state the first time through */ 339 if ((!mat->was_assembled && mode == MAT_FINAL_ASSEMBLY) || !((Mat_SeqSELL *)(sell->A->data))->nonew) { 340 PetscObjectState state = sell->A->nonzerostate + sell->B->nonzerostate; 341 PetscCall(MPIU_Allreduce(&state, &mat->nonzerostate, 1, MPIU_INT64, MPI_SUM, PetscObjectComm((PetscObject)mat))); 342 } 343 PetscFunctionReturn(PETSC_SUCCESS); 344 } 345 346 PetscErrorCode MatZeroEntries_MPISELL(Mat A) 347 { 348 Mat_MPISELL *l = (Mat_MPISELL *)A->data; 349 350 PetscFunctionBegin; 351 PetscCall(MatZeroEntries(l->A)); 352 PetscCall(MatZeroEntries(l->B)); 353 PetscFunctionReturn(PETSC_SUCCESS); 354 } 355 356 PetscErrorCode MatMult_MPISELL(Mat A, Vec xx, Vec yy) 357 { 358 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 359 PetscInt nt; 360 361 PetscFunctionBegin; 362 PetscCall(VecGetLocalSize(xx, &nt)); 363 PetscCheck(nt == A->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Incompatible partition of A (%" PetscInt_FMT ") and xx (%" PetscInt_FMT ")", A->cmap->n, nt); 364 PetscCall(VecScatterBegin(a->Mvctx, xx, a->lvec, INSERT_VALUES, SCATTER_FORWARD)); 365 PetscCall((*a->A->ops->mult)(a->A, xx, yy)); 366 PetscCall(VecScatterEnd(a->Mvctx, xx, a->lvec, INSERT_VALUES, SCATTER_FORWARD)); 367 PetscCall((*a->B->ops->multadd)(a->B, a->lvec, yy, yy)); 368 PetscFunctionReturn(PETSC_SUCCESS); 369 } 370 371 PetscErrorCode MatMultDiagonalBlock_MPISELL(Mat A, Vec bb, Vec xx) 372 { 373 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 374 375 PetscFunctionBegin; 376 PetscCall(MatMultDiagonalBlock(a->A, bb, xx)); 377 PetscFunctionReturn(PETSC_SUCCESS); 378 } 379 380 PetscErrorCode MatMultAdd_MPISELL(Mat A, Vec xx, Vec yy, Vec zz) 381 { 382 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 383 384 PetscFunctionBegin; 385 PetscCall(VecScatterBegin(a->Mvctx, xx, a->lvec, INSERT_VALUES, SCATTER_FORWARD)); 386 PetscCall((*a->A->ops->multadd)(a->A, xx, yy, zz)); 387 PetscCall(VecScatterEnd(a->Mvctx, xx, a->lvec, INSERT_VALUES, SCATTER_FORWARD)); 388 PetscCall((*a->B->ops->multadd)(a->B, a->lvec, zz, zz)); 389 PetscFunctionReturn(PETSC_SUCCESS); 390 } 391 392 PetscErrorCode MatMultTranspose_MPISELL(Mat A, Vec xx, Vec yy) 393 { 394 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 395 396 PetscFunctionBegin; 397 /* do nondiagonal part */ 398 PetscCall((*a->B->ops->multtranspose)(a->B, xx, a->lvec)); 399 /* do local part */ 400 PetscCall((*a->A->ops->multtranspose)(a->A, xx, yy)); 401 /* add partial results together */ 402 PetscCall(VecScatterBegin(a->Mvctx, a->lvec, yy, ADD_VALUES, SCATTER_REVERSE)); 403 PetscCall(VecScatterEnd(a->Mvctx, a->lvec, yy, ADD_VALUES, SCATTER_REVERSE)); 404 PetscFunctionReturn(PETSC_SUCCESS); 405 } 406 407 PetscErrorCode MatIsTranspose_MPISELL(Mat Amat, Mat Bmat, PetscReal tol, PetscBool *f) 408 { 409 MPI_Comm comm; 410 Mat_MPISELL *Asell = (Mat_MPISELL *)Amat->data, *Bsell; 411 Mat Adia = Asell->A, Bdia, Aoff, Boff, *Aoffs, *Boffs; 412 IS Me, Notme; 413 PetscInt M, N, first, last, *notme, i; 414 PetscMPIInt size; 415 416 PetscFunctionBegin; 417 /* Easy test: symmetric diagonal block */ 418 Bsell = (Mat_MPISELL *)Bmat->data; 419 Bdia = Bsell->A; 420 PetscCall(MatIsTranspose(Adia, Bdia, tol, f)); 421 if (!*f) PetscFunctionReturn(PETSC_SUCCESS); 422 PetscCall(PetscObjectGetComm((PetscObject)Amat, &comm)); 423 PetscCallMPI(MPI_Comm_size(comm, &size)); 424 if (size == 1) PetscFunctionReturn(PETSC_SUCCESS); 425 426 /* Hard test: off-diagonal block. This takes a MatCreateSubMatrix. */ 427 PetscCall(MatGetSize(Amat, &M, &N)); 428 PetscCall(MatGetOwnershipRange(Amat, &first, &last)); 429 PetscCall(PetscMalloc1(N - last + first, ¬me)); 430 for (i = 0; i < first; i++) notme[i] = i; 431 for (i = last; i < M; i++) notme[i - last + first] = i; 432 PetscCall(ISCreateGeneral(MPI_COMM_SELF, N - last + first, notme, PETSC_COPY_VALUES, &Notme)); 433 PetscCall(ISCreateStride(MPI_COMM_SELF, last - first, first, 1, &Me)); 434 PetscCall(MatCreateSubMatrices(Amat, 1, &Me, &Notme, MAT_INITIAL_MATRIX, &Aoffs)); 435 Aoff = Aoffs[0]; 436 PetscCall(MatCreateSubMatrices(Bmat, 1, &Notme, &Me, MAT_INITIAL_MATRIX, &Boffs)); 437 Boff = Boffs[0]; 438 PetscCall(MatIsTranspose(Aoff, Boff, tol, f)); 439 PetscCall(MatDestroyMatrices(1, &Aoffs)); 440 PetscCall(MatDestroyMatrices(1, &Boffs)); 441 PetscCall(ISDestroy(&Me)); 442 PetscCall(ISDestroy(&Notme)); 443 PetscCall(PetscFree(notme)); 444 PetscFunctionReturn(PETSC_SUCCESS); 445 } 446 447 PetscErrorCode MatMultTransposeAdd_MPISELL(Mat A, Vec xx, Vec yy, Vec zz) 448 { 449 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 450 451 PetscFunctionBegin; 452 /* do nondiagonal part */ 453 PetscCall((*a->B->ops->multtranspose)(a->B, xx, a->lvec)); 454 /* do local part */ 455 PetscCall((*a->A->ops->multtransposeadd)(a->A, xx, yy, zz)); 456 /* add partial results together */ 457 PetscCall(VecScatterBegin(a->Mvctx, a->lvec, zz, ADD_VALUES, SCATTER_REVERSE)); 458 PetscCall(VecScatterEnd(a->Mvctx, a->lvec, zz, ADD_VALUES, SCATTER_REVERSE)); 459 PetscFunctionReturn(PETSC_SUCCESS); 460 } 461 462 /* 463 This only works correctly for square matrices where the subblock A->A is the 464 diagonal block 465 */ 466 PetscErrorCode MatGetDiagonal_MPISELL(Mat A, Vec v) 467 { 468 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 469 470 PetscFunctionBegin; 471 PetscCheck(A->rmap->N == A->cmap->N, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Supports only square matrix where A->A is diag block"); 472 PetscCheck(A->rmap->rstart == A->cmap->rstart && A->rmap->rend == A->cmap->rend, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "row partition must equal col partition"); 473 PetscCall(MatGetDiagonal(a->A, v)); 474 PetscFunctionReturn(PETSC_SUCCESS); 475 } 476 477 PetscErrorCode MatScale_MPISELL(Mat A, PetscScalar aa) 478 { 479 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 480 481 PetscFunctionBegin; 482 PetscCall(MatScale(a->A, aa)); 483 PetscCall(MatScale(a->B, aa)); 484 PetscFunctionReturn(PETSC_SUCCESS); 485 } 486 487 PetscErrorCode MatDestroy_MPISELL(Mat mat) 488 { 489 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 490 491 PetscFunctionBegin; 492 #if defined(PETSC_USE_LOG) 493 PetscCall(PetscLogObjectState((PetscObject)mat, "Rows=%" PetscInt_FMT ", Cols=%" PetscInt_FMT, mat->rmap->N, mat->cmap->N)); 494 #endif 495 PetscCall(MatStashDestroy_Private(&mat->stash)); 496 PetscCall(VecDestroy(&sell->diag)); 497 PetscCall(MatDestroy(&sell->A)); 498 PetscCall(MatDestroy(&sell->B)); 499 #if defined(PETSC_USE_CTABLE) 500 PetscCall(PetscHMapIDestroy(&sell->colmap)); 501 #else 502 PetscCall(PetscFree(sell->colmap)); 503 #endif 504 PetscCall(PetscFree(sell->garray)); 505 PetscCall(VecDestroy(&sell->lvec)); 506 PetscCall(VecScatterDestroy(&sell->Mvctx)); 507 PetscCall(PetscFree2(sell->rowvalues, sell->rowindices)); 508 PetscCall(PetscFree(sell->ld)); 509 PetscCall(PetscFree(mat->data)); 510 511 PetscCall(PetscObjectChangeTypeName((PetscObject)mat, NULL)); 512 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatStoreValues_C", NULL)); 513 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatRetrieveValues_C", NULL)); 514 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatIsTranspose_C", NULL)); 515 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatMPISELLSetPreallocation_C", NULL)); 516 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatConvert_mpisell_mpiaij_C", NULL)); 517 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatDiagonalScaleLocal_C", NULL)); 518 PetscFunctionReturn(PETSC_SUCCESS); 519 } 520 521 #include <petscdraw.h> 522 PetscErrorCode MatView_MPISELL_ASCIIorDraworSocket(Mat mat, PetscViewer viewer) 523 { 524 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 525 PetscMPIInt rank = sell->rank, size = sell->size; 526 PetscBool isdraw, iascii, isbinary; 527 PetscViewer sviewer; 528 PetscViewerFormat format; 529 530 PetscFunctionBegin; 531 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw)); 532 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 533 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERBINARY, &isbinary)); 534 if (iascii) { 535 PetscCall(PetscViewerGetFormat(viewer, &format)); 536 if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) { 537 MatInfo info; 538 PetscInt *inodes; 539 540 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)mat), &rank)); 541 PetscCall(MatGetInfo(mat, MAT_LOCAL, &info)); 542 PetscCall(MatInodeGetInodeSizes(sell->A, NULL, &inodes, NULL)); 543 PetscCall(PetscViewerASCIIPushSynchronized(viewer)); 544 if (!inodes) { 545 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Local rows %" PetscInt_FMT " nz %" PetscInt_FMT " nz alloced %" PetscInt_FMT " mem %" PetscInt_FMT ", not using I-node routines\n", rank, mat->rmap->n, (PetscInt)info.nz_used, 546 (PetscInt)info.nz_allocated, (PetscInt)info.memory)); 547 } else { 548 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Local rows %" PetscInt_FMT " nz %" PetscInt_FMT " nz alloced %" PetscInt_FMT " mem %" PetscInt_FMT ", using I-node routines\n", rank, mat->rmap->n, (PetscInt)info.nz_used, 549 (PetscInt)info.nz_allocated, (PetscInt)info.memory)); 550 } 551 PetscCall(MatGetInfo(sell->A, MAT_LOCAL, &info)); 552 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] on-diagonal part: nz %" PetscInt_FMT " \n", rank, (PetscInt)info.nz_used)); 553 PetscCall(MatGetInfo(sell->B, MAT_LOCAL, &info)); 554 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] off-diagonal part: nz %" PetscInt_FMT " \n", rank, (PetscInt)info.nz_used)); 555 PetscCall(PetscViewerFlush(viewer)); 556 PetscCall(PetscViewerASCIIPopSynchronized(viewer)); 557 PetscCall(PetscViewerASCIIPrintf(viewer, "Information on VecScatter used in matrix-vector product: \n")); 558 PetscCall(VecScatterView(sell->Mvctx, viewer)); 559 PetscFunctionReturn(PETSC_SUCCESS); 560 } else if (format == PETSC_VIEWER_ASCII_INFO) { 561 PetscInt inodecount, inodelimit, *inodes; 562 PetscCall(MatInodeGetInodeSizes(sell->A, &inodecount, &inodes, &inodelimit)); 563 if (inodes) { 564 PetscCall(PetscViewerASCIIPrintf(viewer, "using I-node (on process 0) routines: found %" PetscInt_FMT " nodes, limit used is %" PetscInt_FMT "\n", inodecount, inodelimit)); 565 } else { 566 PetscCall(PetscViewerASCIIPrintf(viewer, "not using I-node (on process 0) routines\n")); 567 } 568 PetscFunctionReturn(PETSC_SUCCESS); 569 } else if (format == PETSC_VIEWER_ASCII_FACTOR_INFO) { 570 PetscFunctionReturn(PETSC_SUCCESS); 571 } 572 } else if (isbinary) { 573 if (size == 1) { 574 PetscCall(PetscObjectSetName((PetscObject)sell->A, ((PetscObject)mat)->name)); 575 PetscCall(MatView(sell->A, viewer)); 576 } else { 577 /* PetscCall(MatView_MPISELL_Binary(mat,viewer)); */ 578 } 579 PetscFunctionReturn(PETSC_SUCCESS); 580 } else if (isdraw) { 581 PetscDraw draw; 582 PetscBool isnull; 583 PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw)); 584 PetscCall(PetscDrawIsNull(draw, &isnull)); 585 if (isnull) PetscFunctionReturn(PETSC_SUCCESS); 586 } 587 588 { 589 /* assemble the entire matrix onto first processor. */ 590 Mat A; 591 Mat_SeqSELL *Aloc; 592 PetscInt M = mat->rmap->N, N = mat->cmap->N, *acolidx, row, col, i, j; 593 MatScalar *aval; 594 PetscBool isnonzero; 595 596 PetscCall(MatCreate(PetscObjectComm((PetscObject)mat), &A)); 597 if (rank == 0) { 598 PetscCall(MatSetSizes(A, M, N, M, N)); 599 } else { 600 PetscCall(MatSetSizes(A, 0, 0, M, N)); 601 } 602 /* This is just a temporary matrix, so explicitly using MATMPISELL is probably best */ 603 PetscCall(MatSetType(A, MATMPISELL)); 604 PetscCall(MatMPISELLSetPreallocation(A, 0, NULL, 0, NULL)); 605 PetscCall(MatSetOption(A, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_FALSE)); 606 607 /* copy over the A part */ 608 Aloc = (Mat_SeqSELL *)sell->A->data; 609 acolidx = Aloc->colidx; 610 aval = Aloc->val; 611 for (i = 0; i < Aloc->totalslices; i++) { /* loop over slices */ 612 for (j = Aloc->sliidx[i]; j < Aloc->sliidx[i + 1]; j++) { 613 isnonzero = (PetscBool)((j - Aloc->sliidx[i]) / 8 < Aloc->rlen[(i << 3) + (j & 0x07)]); 614 if (isnonzero) { /* check the mask bit */ 615 row = (i << 3) + (j & 0x07) + mat->rmap->rstart; /* i<<3 is the starting row of this slice */ 616 col = *acolidx + mat->rmap->rstart; 617 PetscCall(MatSetValues(A, 1, &row, 1, &col, aval, INSERT_VALUES)); 618 } 619 aval++; 620 acolidx++; 621 } 622 } 623 624 /* copy over the B part */ 625 Aloc = (Mat_SeqSELL *)sell->B->data; 626 acolidx = Aloc->colidx; 627 aval = Aloc->val; 628 for (i = 0; i < Aloc->totalslices; i++) { 629 for (j = Aloc->sliidx[i]; j < Aloc->sliidx[i + 1]; j++) { 630 isnonzero = (PetscBool)((j - Aloc->sliidx[i]) / 8 < Aloc->rlen[(i << 3) + (j & 0x07)]); 631 if (isnonzero) { 632 row = (i << 3) + (j & 0x07) + mat->rmap->rstart; 633 col = sell->garray[*acolidx]; 634 PetscCall(MatSetValues(A, 1, &row, 1, &col, aval, INSERT_VALUES)); 635 } 636 aval++; 637 acolidx++; 638 } 639 } 640 641 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 642 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 643 /* 644 Everyone has to call to draw the matrix since the graphics waits are 645 synchronized across all processors that share the PetscDraw object 646 */ 647 PetscCall(PetscViewerGetSubViewer(viewer, PETSC_COMM_SELF, &sviewer)); 648 if (rank == 0) { 649 PetscCall(PetscObjectSetName((PetscObject)((Mat_MPISELL *)(A->data))->A, ((PetscObject)mat)->name)); 650 PetscCall(MatView_SeqSELL(((Mat_MPISELL *)(A->data))->A, sviewer)); 651 } 652 PetscCall(PetscViewerRestoreSubViewer(viewer, PETSC_COMM_SELF, &sviewer)); 653 PetscCall(PetscViewerFlush(viewer)); 654 PetscCall(MatDestroy(&A)); 655 } 656 PetscFunctionReturn(PETSC_SUCCESS); 657 } 658 659 PetscErrorCode MatView_MPISELL(Mat mat, PetscViewer viewer) 660 { 661 PetscBool iascii, isdraw, issocket, isbinary; 662 663 PetscFunctionBegin; 664 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 665 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw)); 666 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERBINARY, &isbinary)); 667 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERSOCKET, &issocket)); 668 if (iascii || isdraw || isbinary || issocket) PetscCall(MatView_MPISELL_ASCIIorDraworSocket(mat, viewer)); 669 PetscFunctionReturn(PETSC_SUCCESS); 670 } 671 672 PetscErrorCode MatGetGhosts_MPISELL(Mat mat, PetscInt *nghosts, const PetscInt *ghosts[]) 673 { 674 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 675 676 PetscFunctionBegin; 677 PetscCall(MatGetSize(sell->B, NULL, nghosts)); 678 if (ghosts) *ghosts = sell->garray; 679 PetscFunctionReturn(PETSC_SUCCESS); 680 } 681 682 PetscErrorCode MatGetInfo_MPISELL(Mat matin, MatInfoType flag, MatInfo *info) 683 { 684 Mat_MPISELL *mat = (Mat_MPISELL *)matin->data; 685 Mat A = mat->A, B = mat->B; 686 PetscLogDouble isend[5], irecv[5]; 687 688 PetscFunctionBegin; 689 info->block_size = 1.0; 690 PetscCall(MatGetInfo(A, MAT_LOCAL, info)); 691 692 isend[0] = info->nz_used; 693 isend[1] = info->nz_allocated; 694 isend[2] = info->nz_unneeded; 695 isend[3] = info->memory; 696 isend[4] = info->mallocs; 697 698 PetscCall(MatGetInfo(B, MAT_LOCAL, info)); 699 700 isend[0] += info->nz_used; 701 isend[1] += info->nz_allocated; 702 isend[2] += info->nz_unneeded; 703 isend[3] += info->memory; 704 isend[4] += info->mallocs; 705 if (flag == MAT_LOCAL) { 706 info->nz_used = isend[0]; 707 info->nz_allocated = isend[1]; 708 info->nz_unneeded = isend[2]; 709 info->memory = isend[3]; 710 info->mallocs = isend[4]; 711 } else if (flag == MAT_GLOBAL_MAX) { 712 PetscCall(MPIU_Allreduce(isend, irecv, 5, MPIU_PETSCLOGDOUBLE, MPI_MAX, PetscObjectComm((PetscObject)matin))); 713 714 info->nz_used = irecv[0]; 715 info->nz_allocated = irecv[1]; 716 info->nz_unneeded = irecv[2]; 717 info->memory = irecv[3]; 718 info->mallocs = irecv[4]; 719 } else if (flag == MAT_GLOBAL_SUM) { 720 PetscCall(MPIU_Allreduce(isend, irecv, 5, MPIU_PETSCLOGDOUBLE, MPI_SUM, PetscObjectComm((PetscObject)matin))); 721 722 info->nz_used = irecv[0]; 723 info->nz_allocated = irecv[1]; 724 info->nz_unneeded = irecv[2]; 725 info->memory = irecv[3]; 726 info->mallocs = irecv[4]; 727 } 728 info->fill_ratio_given = 0; /* no parallel LU/ILU/Cholesky */ 729 info->fill_ratio_needed = 0; 730 info->factor_mallocs = 0; 731 PetscFunctionReturn(PETSC_SUCCESS); 732 } 733 734 PetscErrorCode MatSetOption_MPISELL(Mat A, MatOption op, PetscBool flg) 735 { 736 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 737 738 PetscFunctionBegin; 739 switch (op) { 740 case MAT_NEW_NONZERO_LOCATIONS: 741 case MAT_NEW_NONZERO_ALLOCATION_ERR: 742 case MAT_UNUSED_NONZERO_LOCATION_ERR: 743 case MAT_KEEP_NONZERO_PATTERN: 744 case MAT_NEW_NONZERO_LOCATION_ERR: 745 case MAT_USE_INODES: 746 case MAT_IGNORE_ZERO_ENTRIES: 747 MatCheckPreallocated(A, 1); 748 PetscCall(MatSetOption(a->A, op, flg)); 749 PetscCall(MatSetOption(a->B, op, flg)); 750 break; 751 case MAT_ROW_ORIENTED: 752 MatCheckPreallocated(A, 1); 753 a->roworiented = flg; 754 755 PetscCall(MatSetOption(a->A, op, flg)); 756 PetscCall(MatSetOption(a->B, op, flg)); 757 break; 758 case MAT_FORCE_DIAGONAL_ENTRIES: 759 case MAT_SORTED_FULL: 760 PetscCall(PetscInfo(A, "Option %s ignored\n", MatOptions[op])); 761 break; 762 case MAT_IGNORE_OFF_PROC_ENTRIES: 763 a->donotstash = flg; 764 break; 765 case MAT_SPD: 766 case MAT_SPD_ETERNAL: 767 break; 768 case MAT_SYMMETRIC: 769 MatCheckPreallocated(A, 1); 770 PetscCall(MatSetOption(a->A, op, flg)); 771 break; 772 case MAT_STRUCTURALLY_SYMMETRIC: 773 MatCheckPreallocated(A, 1); 774 PetscCall(MatSetOption(a->A, op, flg)); 775 break; 776 case MAT_HERMITIAN: 777 MatCheckPreallocated(A, 1); 778 PetscCall(MatSetOption(a->A, op, flg)); 779 break; 780 case MAT_SYMMETRY_ETERNAL: 781 MatCheckPreallocated(A, 1); 782 PetscCall(MatSetOption(a->A, op, flg)); 783 break; 784 case MAT_STRUCTURAL_SYMMETRY_ETERNAL: 785 MatCheckPreallocated(A, 1); 786 PetscCall(MatSetOption(a->A, op, flg)); 787 break; 788 default: 789 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "unknown option %d", op); 790 } 791 PetscFunctionReturn(PETSC_SUCCESS); 792 } 793 794 PetscErrorCode MatDiagonalScale_MPISELL(Mat mat, Vec ll, Vec rr) 795 { 796 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 797 Mat a = sell->A, b = sell->B; 798 PetscInt s1, s2, s3; 799 800 PetscFunctionBegin; 801 PetscCall(MatGetLocalSize(mat, &s2, &s3)); 802 if (rr) { 803 PetscCall(VecGetLocalSize(rr, &s1)); 804 PetscCheck(s1 == s3, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "right vector non-conforming local size"); 805 /* Overlap communication with computation. */ 806 PetscCall(VecScatterBegin(sell->Mvctx, rr, sell->lvec, INSERT_VALUES, SCATTER_FORWARD)); 807 } 808 if (ll) { 809 PetscCall(VecGetLocalSize(ll, &s1)); 810 PetscCheck(s1 == s2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "left vector non-conforming local size"); 811 PetscUseTypeMethod(b, diagonalscale, ll, NULL); 812 } 813 /* scale the diagonal block */ 814 PetscUseTypeMethod(a, diagonalscale, ll, rr); 815 816 if (rr) { 817 /* Do a scatter end and then right scale the off-diagonal block */ 818 PetscCall(VecScatterEnd(sell->Mvctx, rr, sell->lvec, INSERT_VALUES, SCATTER_FORWARD)); 819 PetscUseTypeMethod(b, diagonalscale, NULL, sell->lvec); 820 } 821 PetscFunctionReturn(PETSC_SUCCESS); 822 } 823 824 PetscErrorCode MatSetUnfactored_MPISELL(Mat A) 825 { 826 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 827 828 PetscFunctionBegin; 829 PetscCall(MatSetUnfactored(a->A)); 830 PetscFunctionReturn(PETSC_SUCCESS); 831 } 832 833 PetscErrorCode MatEqual_MPISELL(Mat A, Mat B, PetscBool *flag) 834 { 835 Mat_MPISELL *matB = (Mat_MPISELL *)B->data, *matA = (Mat_MPISELL *)A->data; 836 Mat a, b, c, d; 837 PetscBool flg; 838 839 PetscFunctionBegin; 840 a = matA->A; 841 b = matA->B; 842 c = matB->A; 843 d = matB->B; 844 845 PetscCall(MatEqual(a, c, &flg)); 846 if (flg) PetscCall(MatEqual(b, d, &flg)); 847 PetscCall(MPIU_Allreduce(&flg, flag, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)A))); 848 PetscFunctionReturn(PETSC_SUCCESS); 849 } 850 851 PetscErrorCode MatCopy_MPISELL(Mat A, Mat B, MatStructure str) 852 { 853 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 854 Mat_MPISELL *b = (Mat_MPISELL *)B->data; 855 856 PetscFunctionBegin; 857 /* If the two matrices don't have the same copy implementation, they aren't compatible for fast copy. */ 858 if ((str != SAME_NONZERO_PATTERN) || (A->ops->copy != B->ops->copy)) { 859 /* because of the column compression in the off-processor part of the matrix a->B, 860 the number of columns in a->B and b->B may be different, hence we cannot call 861 the MatCopy() directly on the two parts. If need be, we can provide a more 862 efficient copy than the MatCopy_Basic() by first uncompressing the a->B matrices 863 then copying the submatrices */ 864 PetscCall(MatCopy_Basic(A, B, str)); 865 } else { 866 PetscCall(MatCopy(a->A, b->A, str)); 867 PetscCall(MatCopy(a->B, b->B, str)); 868 } 869 PetscFunctionReturn(PETSC_SUCCESS); 870 } 871 872 PetscErrorCode MatSetUp_MPISELL(Mat A) 873 { 874 PetscFunctionBegin; 875 PetscCall(MatMPISELLSetPreallocation(A, PETSC_DEFAULT, NULL, PETSC_DEFAULT, NULL)); 876 PetscFunctionReturn(PETSC_SUCCESS); 877 } 878 879 extern PetscErrorCode MatConjugate_SeqSELL(Mat); 880 881 PetscErrorCode MatConjugate_MPISELL(Mat mat) 882 { 883 PetscFunctionBegin; 884 if (PetscDefined(USE_COMPLEX)) { 885 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 886 887 PetscCall(MatConjugate_SeqSELL(sell->A)); 888 PetscCall(MatConjugate_SeqSELL(sell->B)); 889 } 890 PetscFunctionReturn(PETSC_SUCCESS); 891 } 892 893 PetscErrorCode MatRealPart_MPISELL(Mat A) 894 { 895 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 896 897 PetscFunctionBegin; 898 PetscCall(MatRealPart(a->A)); 899 PetscCall(MatRealPart(a->B)); 900 PetscFunctionReturn(PETSC_SUCCESS); 901 } 902 903 PetscErrorCode MatImaginaryPart_MPISELL(Mat A) 904 { 905 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 906 907 PetscFunctionBegin; 908 PetscCall(MatImaginaryPart(a->A)); 909 PetscCall(MatImaginaryPart(a->B)); 910 PetscFunctionReturn(PETSC_SUCCESS); 911 } 912 913 PetscErrorCode MatInvertBlockDiagonal_MPISELL(Mat A, const PetscScalar **values) 914 { 915 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 916 917 PetscFunctionBegin; 918 PetscCall(MatInvertBlockDiagonal(a->A, values)); 919 A->factorerrortype = a->A->factorerrortype; 920 PetscFunctionReturn(PETSC_SUCCESS); 921 } 922 923 static PetscErrorCode MatSetRandom_MPISELL(Mat x, PetscRandom rctx) 924 { 925 Mat_MPISELL *sell = (Mat_MPISELL *)x->data; 926 927 PetscFunctionBegin; 928 PetscCall(MatSetRandom(sell->A, rctx)); 929 PetscCall(MatSetRandom(sell->B, rctx)); 930 PetscCall(MatAssemblyBegin(x, MAT_FINAL_ASSEMBLY)); 931 PetscCall(MatAssemblyEnd(x, MAT_FINAL_ASSEMBLY)); 932 PetscFunctionReturn(PETSC_SUCCESS); 933 } 934 935 PetscErrorCode MatSetFromOptions_MPISELL(Mat A, PetscOptionItems *PetscOptionsObject) 936 { 937 PetscFunctionBegin; 938 PetscOptionsHeadBegin(PetscOptionsObject, "MPISELL options"); 939 PetscOptionsHeadEnd(); 940 PetscFunctionReturn(PETSC_SUCCESS); 941 } 942 943 PetscErrorCode MatShift_MPISELL(Mat Y, PetscScalar a) 944 { 945 Mat_MPISELL *msell = (Mat_MPISELL *)Y->data; 946 Mat_SeqSELL *sell = (Mat_SeqSELL *)msell->A->data; 947 948 PetscFunctionBegin; 949 if (!Y->preallocated) { 950 PetscCall(MatMPISELLSetPreallocation(Y, 1, NULL, 0, NULL)); 951 } else if (!sell->nz) { 952 PetscInt nonew = sell->nonew; 953 PetscCall(MatSeqSELLSetPreallocation(msell->A, 1, NULL)); 954 sell->nonew = nonew; 955 } 956 PetscCall(MatShift_Basic(Y, a)); 957 PetscFunctionReturn(PETSC_SUCCESS); 958 } 959 960 PetscErrorCode MatMissingDiagonal_MPISELL(Mat A, PetscBool *missing, PetscInt *d) 961 { 962 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 963 964 PetscFunctionBegin; 965 PetscCheck(A->rmap->n == A->cmap->n, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only works for square matrices"); 966 PetscCall(MatMissingDiagonal(a->A, missing, d)); 967 if (d) { 968 PetscInt rstart; 969 PetscCall(MatGetOwnershipRange(A, &rstart, NULL)); 970 *d += rstart; 971 } 972 PetscFunctionReturn(PETSC_SUCCESS); 973 } 974 975 PetscErrorCode MatGetDiagonalBlock_MPISELL(Mat A, Mat *a) 976 { 977 PetscFunctionBegin; 978 *a = ((Mat_MPISELL *)A->data)->A; 979 PetscFunctionReturn(PETSC_SUCCESS); 980 } 981 982 static struct _MatOps MatOps_Values = {MatSetValues_MPISELL, 983 NULL, 984 NULL, 985 MatMult_MPISELL, 986 /* 4*/ MatMultAdd_MPISELL, 987 MatMultTranspose_MPISELL, 988 MatMultTransposeAdd_MPISELL, 989 NULL, 990 NULL, 991 NULL, 992 /*10*/ NULL, 993 NULL, 994 NULL, 995 MatSOR_MPISELL, 996 NULL, 997 /*15*/ MatGetInfo_MPISELL, 998 MatEqual_MPISELL, 999 MatGetDiagonal_MPISELL, 1000 MatDiagonalScale_MPISELL, 1001 NULL, 1002 /*20*/ MatAssemblyBegin_MPISELL, 1003 MatAssemblyEnd_MPISELL, 1004 MatSetOption_MPISELL, 1005 MatZeroEntries_MPISELL, 1006 /*24*/ NULL, 1007 NULL, 1008 NULL, 1009 NULL, 1010 NULL, 1011 /*29*/ MatSetUp_MPISELL, 1012 NULL, 1013 NULL, 1014 MatGetDiagonalBlock_MPISELL, 1015 NULL, 1016 /*34*/ MatDuplicate_MPISELL, 1017 NULL, 1018 NULL, 1019 NULL, 1020 NULL, 1021 /*39*/ NULL, 1022 NULL, 1023 NULL, 1024 MatGetValues_MPISELL, 1025 MatCopy_MPISELL, 1026 /*44*/ NULL, 1027 MatScale_MPISELL, 1028 MatShift_MPISELL, 1029 MatDiagonalSet_MPISELL, 1030 NULL, 1031 /*49*/ MatSetRandom_MPISELL, 1032 NULL, 1033 NULL, 1034 NULL, 1035 NULL, 1036 /*54*/ MatFDColoringCreate_MPIXAIJ, 1037 NULL, 1038 MatSetUnfactored_MPISELL, 1039 NULL, 1040 NULL, 1041 /*59*/ NULL, 1042 MatDestroy_MPISELL, 1043 MatView_MPISELL, 1044 NULL, 1045 NULL, 1046 /*64*/ NULL, 1047 NULL, 1048 NULL, 1049 NULL, 1050 NULL, 1051 /*69*/ NULL, 1052 NULL, 1053 NULL, 1054 NULL, 1055 NULL, 1056 NULL, 1057 /*75*/ MatFDColoringApply_AIJ, /* reuse AIJ function */ 1058 MatSetFromOptions_MPISELL, 1059 NULL, 1060 NULL, 1061 NULL, 1062 /*80*/ NULL, 1063 NULL, 1064 NULL, 1065 /*83*/ NULL, 1066 NULL, 1067 NULL, 1068 NULL, 1069 NULL, 1070 NULL, 1071 /*89*/ NULL, 1072 NULL, 1073 NULL, 1074 NULL, 1075 NULL, 1076 /*94*/ NULL, 1077 NULL, 1078 NULL, 1079 NULL, 1080 NULL, 1081 /*99*/ NULL, 1082 NULL, 1083 NULL, 1084 MatConjugate_MPISELL, 1085 NULL, 1086 /*104*/ NULL, 1087 MatRealPart_MPISELL, 1088 MatImaginaryPart_MPISELL, 1089 NULL, 1090 NULL, 1091 /*109*/ NULL, 1092 NULL, 1093 NULL, 1094 NULL, 1095 MatMissingDiagonal_MPISELL, 1096 /*114*/ NULL, 1097 NULL, 1098 MatGetGhosts_MPISELL, 1099 NULL, 1100 NULL, 1101 /*119*/ NULL, 1102 NULL, 1103 NULL, 1104 NULL, 1105 NULL, 1106 /*124*/ NULL, 1107 NULL, 1108 MatInvertBlockDiagonal_MPISELL, 1109 NULL, 1110 NULL, 1111 /*129*/ NULL, 1112 NULL, 1113 NULL, 1114 NULL, 1115 NULL, 1116 /*134*/ NULL, 1117 NULL, 1118 NULL, 1119 NULL, 1120 NULL, 1121 /*139*/ NULL, 1122 NULL, 1123 NULL, 1124 MatFDColoringSetUp_MPIXAIJ, 1125 NULL, 1126 /*144*/ NULL, 1127 NULL, 1128 NULL, 1129 NULL, 1130 NULL, 1131 NULL, 1132 /*150*/ NULL, 1133 NULL}; 1134 1135 PetscErrorCode MatStoreValues_MPISELL(Mat mat) 1136 { 1137 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 1138 1139 PetscFunctionBegin; 1140 PetscCall(MatStoreValues(sell->A)); 1141 PetscCall(MatStoreValues(sell->B)); 1142 PetscFunctionReturn(PETSC_SUCCESS); 1143 } 1144 1145 PetscErrorCode MatRetrieveValues_MPISELL(Mat mat) 1146 { 1147 Mat_MPISELL *sell = (Mat_MPISELL *)mat->data; 1148 1149 PetscFunctionBegin; 1150 PetscCall(MatRetrieveValues(sell->A)); 1151 PetscCall(MatRetrieveValues(sell->B)); 1152 PetscFunctionReturn(PETSC_SUCCESS); 1153 } 1154 1155 PetscErrorCode MatMPISELLSetPreallocation_MPISELL(Mat B, PetscInt d_rlenmax, const PetscInt d_rlen[], PetscInt o_rlenmax, const PetscInt o_rlen[]) 1156 { 1157 Mat_MPISELL *b; 1158 1159 PetscFunctionBegin; 1160 PetscCall(PetscLayoutSetUp(B->rmap)); 1161 PetscCall(PetscLayoutSetUp(B->cmap)); 1162 b = (Mat_MPISELL *)B->data; 1163 1164 if (!B->preallocated) { 1165 /* Explicitly create 2 MATSEQSELL matrices. */ 1166 PetscCall(MatCreate(PETSC_COMM_SELF, &b->A)); 1167 PetscCall(MatSetSizes(b->A, B->rmap->n, B->cmap->n, B->rmap->n, B->cmap->n)); 1168 PetscCall(MatSetBlockSizesFromMats(b->A, B, B)); 1169 PetscCall(MatSetType(b->A, MATSEQSELL)); 1170 PetscCall(MatCreate(PETSC_COMM_SELF, &b->B)); 1171 PetscCall(MatSetSizes(b->B, B->rmap->n, B->cmap->N, B->rmap->n, B->cmap->N)); 1172 PetscCall(MatSetBlockSizesFromMats(b->B, B, B)); 1173 PetscCall(MatSetType(b->B, MATSEQSELL)); 1174 } 1175 1176 PetscCall(MatSeqSELLSetPreallocation(b->A, d_rlenmax, d_rlen)); 1177 PetscCall(MatSeqSELLSetPreallocation(b->B, o_rlenmax, o_rlen)); 1178 B->preallocated = PETSC_TRUE; 1179 B->was_assembled = PETSC_FALSE; 1180 /* 1181 critical for MatAssemblyEnd to work. 1182 MatAssemblyBegin checks it to set up was_assembled 1183 and MatAssemblyEnd checks was_assembled to determine whether to build garray 1184 */ 1185 B->assembled = PETSC_FALSE; 1186 PetscFunctionReturn(PETSC_SUCCESS); 1187 } 1188 1189 PetscErrorCode MatDuplicate_MPISELL(Mat matin, MatDuplicateOption cpvalues, Mat *newmat) 1190 { 1191 Mat mat; 1192 Mat_MPISELL *a, *oldmat = (Mat_MPISELL *)matin->data; 1193 1194 PetscFunctionBegin; 1195 *newmat = NULL; 1196 PetscCall(MatCreate(PetscObjectComm((PetscObject)matin), &mat)); 1197 PetscCall(MatSetSizes(mat, matin->rmap->n, matin->cmap->n, matin->rmap->N, matin->cmap->N)); 1198 PetscCall(MatSetBlockSizesFromMats(mat, matin, matin)); 1199 PetscCall(MatSetType(mat, ((PetscObject)matin)->type_name)); 1200 a = (Mat_MPISELL *)mat->data; 1201 1202 mat->factortype = matin->factortype; 1203 mat->assembled = PETSC_TRUE; 1204 mat->insertmode = NOT_SET_VALUES; 1205 mat->preallocated = PETSC_TRUE; 1206 1207 a->size = oldmat->size; 1208 a->rank = oldmat->rank; 1209 a->donotstash = oldmat->donotstash; 1210 a->roworiented = oldmat->roworiented; 1211 a->rowindices = NULL; 1212 a->rowvalues = NULL; 1213 a->getrowactive = PETSC_FALSE; 1214 1215 PetscCall(PetscLayoutReference(matin->rmap, &mat->rmap)); 1216 PetscCall(PetscLayoutReference(matin->cmap, &mat->cmap)); 1217 1218 if (oldmat->colmap) { 1219 #if defined(PETSC_USE_CTABLE) 1220 PetscCall(PetscHMapIDuplicate(oldmat->colmap, &a->colmap)); 1221 #else 1222 PetscCall(PetscMalloc1(mat->cmap->N, &a->colmap)); 1223 PetscCall(PetscArraycpy(a->colmap, oldmat->colmap, mat->cmap->N)); 1224 #endif 1225 } else a->colmap = NULL; 1226 if (oldmat->garray) { 1227 PetscInt len; 1228 len = oldmat->B->cmap->n; 1229 PetscCall(PetscMalloc1(len + 1, &a->garray)); 1230 if (len) PetscCall(PetscArraycpy(a->garray, oldmat->garray, len)); 1231 } else a->garray = NULL; 1232 1233 PetscCall(VecDuplicate(oldmat->lvec, &a->lvec)); 1234 PetscCall(VecScatterCopy(oldmat->Mvctx, &a->Mvctx)); 1235 PetscCall(MatDuplicate(oldmat->A, cpvalues, &a->A)); 1236 PetscCall(MatDuplicate(oldmat->B, cpvalues, &a->B)); 1237 PetscCall(PetscFunctionListDuplicate(((PetscObject)matin)->qlist, &((PetscObject)mat)->qlist)); 1238 *newmat = mat; 1239 PetscFunctionReturn(PETSC_SUCCESS); 1240 } 1241 1242 /*@C 1243 MatMPISELLSetPreallocation - Preallocates memory for a `MATMPISELL` sparse parallel matrix in sell format. 1244 For good matrix assembly performance the user should preallocate the matrix storage by 1245 setting the parameters `d_nz` (or `d_nnz`) and `o_nz` (or `o_nnz`). 1246 1247 Collective 1248 1249 Input Parameters: 1250 + B - the matrix 1251 . d_nz - number of nonzeros per row in DIAGONAL portion of local submatrix 1252 (same value is used for all local rows) 1253 . d_nnz - array containing the number of nonzeros in the various rows of the 1254 DIAGONAL portion of the local submatrix (possibly different for each row) 1255 or NULL (`PETSC_NULL_INTEGER` in Fortran), if `d_nz` is used to specify the nonzero structure. 1256 The size of this array is equal to the number of local rows, i.e 'm'. 1257 For matrices that will be factored, you must leave room for (and set) 1258 the diagonal entry even if it is zero. 1259 . o_nz - number of nonzeros per row in the OFF-DIAGONAL portion of local 1260 submatrix (same value is used for all local rows). 1261 - o_nnz - array containing the number of nonzeros in the various rows of the 1262 OFF-DIAGONAL portion of the local submatrix (possibly different for 1263 each row) or NULL (`PETSC_NULL_INTEGER` in Fortran), if `o_nz` is used to specify the nonzero 1264 structure. The size of this array is equal to the number 1265 of local rows, i.e 'm'. 1266 1267 Example usage: 1268 Consider the following 8x8 matrix with 34 non-zero values, that is 1269 assembled across 3 processors. Lets assume that proc0 owns 3 rows, 1270 proc1 owns 3 rows, proc2 owns 2 rows. This division can be shown 1271 as follows 1272 1273 .vb 1274 1 2 0 | 0 3 0 | 0 4 1275 Proc0 0 5 6 | 7 0 0 | 8 0 1276 9 0 10 | 11 0 0 | 12 0 1277 ------------------------------------- 1278 13 0 14 | 15 16 17 | 0 0 1279 Proc1 0 18 0 | 19 20 21 | 0 0 1280 0 0 0 | 22 23 0 | 24 0 1281 ------------------------------------- 1282 Proc2 25 26 27 | 0 0 28 | 29 0 1283 30 0 0 | 31 32 33 | 0 34 1284 .ve 1285 1286 This can be represented as a collection of submatrices as 1287 1288 .vb 1289 A B C 1290 D E F 1291 G H I 1292 .ve 1293 1294 Where the submatrices A,B,C are owned by proc0, D,E,F are 1295 owned by proc1, G,H,I are owned by proc2. 1296 1297 The 'm' parameters for proc0,proc1,proc2 are 3,3,2 respectively. 1298 The 'n' parameters for proc0,proc1,proc2 are 3,3,2 respectively. 1299 The 'M','N' parameters are 8,8, and have the same values on all procs. 1300 1301 The DIAGONAL submatrices corresponding to proc0,proc1,proc2 are 1302 submatrices [A], [E], [I] respectively. The OFF-DIAGONAL submatrices 1303 corresponding to proc0,proc1,proc2 are [BC], [DF], [GH] respectively. 1304 Internally, each processor stores the DIAGONAL part, and the OFF-DIAGONAL 1305 part as `MATSEQSELL` matrices. For example, proc1 will store [E] as a `MATSEQSELL` 1306 matrix, ans [DF] as another SeqSELL matrix. 1307 1308 When `d_nz`, `o_nz` parameters are specified, `d_nz` storage elements are 1309 allocated for every row of the local diagonal submatrix, and o_nz 1310 storage locations are allocated for every row of the OFF-DIAGONAL submat. 1311 One way to choose `d_nz` and `o_nz` is to use the max nonzerors per local 1312 rows for each of the local DIAGONAL, and the OFF-DIAGONAL submatrices. 1313 In this case, the values of d_nz,o_nz are 1314 .vb 1315 proc0 dnz = 2, o_nz = 2 1316 proc1 dnz = 3, o_nz = 2 1317 proc2 dnz = 1, o_nz = 4 1318 .ve 1319 We are allocating m*(d_nz+o_nz) storage locations for every proc. This 1320 translates to 3*(2+2)=12 for proc0, 3*(3+2)=15 for proc1, 2*(1+4)=10 1321 for proc3. i.e we are using 12+15+10=37 storage locations to store 1322 34 values. 1323 1324 When `d_nnz`, `o_nnz` parameters are specified, the storage is specified 1325 for every row, corresponding to both DIAGONAL and OFF-DIAGONAL submatrices. 1326 In the above case the values for d_nnz,o_nnz are 1327 .vb 1328 proc0 d_nnz = [2,2,2] and o_nnz = [2,2,2] 1329 proc1 d_nnz = [3,3,2] and o_nnz = [2,1,1] 1330 proc2 d_nnz = [1,1] and o_nnz = [4,4] 1331 .ve 1332 Here the space allocated is according to nz (or maximum values in the nnz 1333 if nnz is provided) for DIAGONAL and OFF-DIAGONAL submatrices, i.e (2+2+3+2)*3+(1+4)*2=37 1334 1335 Level: intermediate 1336 1337 Notes: 1338 If the *_nnz parameter is given then the *_nz parameter is ignored 1339 1340 The stored row and column indices begin with zero. 1341 1342 The parallel matrix is partitioned such that the first m0 rows belong to 1343 process 0, the next m1 rows belong to process 1, the next m2 rows belong 1344 to process 2 etc.. where m0,m1,m2... are the input parameter 'm'. 1345 1346 The DIAGONAL portion of the local submatrix of a processor can be defined 1347 as the submatrix which is obtained by extraction the part corresponding to 1348 the rows r1-r2 and columns c1-c2 of the global matrix, where r1 is the 1349 first row that belongs to the processor, r2 is the last row belonging to 1350 the this processor, and c1-c2 is range of indices of the local part of a 1351 vector suitable for applying the matrix to. This is an mxn matrix. In the 1352 common case of a square matrix, the row and column ranges are the same and 1353 the DIAGONAL part is also square. The remaining portion of the local 1354 submatrix (mxN) constitute the OFF-DIAGONAL portion. 1355 1356 If `o_nnz`, `d_nnz` are specified, then `o_nz`, and `d_nz` are ignored. 1357 1358 You can call `MatGetInfo()` to get information on how effective the preallocation was; 1359 for example the fields mallocs,nz_allocated,nz_used,nz_unneeded; 1360 You can also run with the option -info and look for messages with the string 1361 malloc in them to see if additional memory allocation was needed. 1362 1363 .seealso: `Mat`, `MatCreate()`, `MatCreateSeqSELL()`, `MatSetValues()`, `MatCreatesell()`, 1364 `MATMPISELL`, `MatGetInfo()`, `PetscSplitOwnership()`, `MATSELL` 1365 @*/ 1366 PetscErrorCode MatMPISELLSetPreallocation(Mat B, PetscInt d_nz, const PetscInt d_nnz[], PetscInt o_nz, const PetscInt o_nnz[]) 1367 { 1368 PetscFunctionBegin; 1369 PetscValidHeaderSpecific(B, MAT_CLASSID, 1); 1370 PetscValidType(B, 1); 1371 PetscTryMethod(B, "MatMPISELLSetPreallocation_C", (Mat, PetscInt, const PetscInt[], PetscInt, const PetscInt[]), (B, d_nz, d_nnz, o_nz, o_nnz)); 1372 PetscFunctionReturn(PETSC_SUCCESS); 1373 } 1374 1375 /*MC 1376 MATMPISELL - MATMPISELL = "mpisell" - A matrix type to be used for MPI sparse matrices, 1377 based on the sliced Ellpack format 1378 1379 Options Database Key: 1380 . -mat_type sell - sets the matrix type to `MATSELL` during a call to `MatSetFromOptions()` 1381 1382 Level: beginner 1383 1384 .seealso: `Mat`, `MatCreateSell()`, `MATSEQSELL`, `MATSELL`, `MATSEQAIJ`, `MATAIJ`, `MATMPIAIJ` 1385 M*/ 1386 1387 /*@C 1388 MatCreateSELL - Creates a sparse parallel matrix in `MATSELL` format. 1389 1390 Collective 1391 1392 Input Parameters: 1393 + comm - MPI communicator 1394 . m - number of local rows (or `PETSC_DECIDE` to have calculated if M is given) 1395 This value should be the same as the local size used in creating the 1396 y vector for the matrix-vector product y = Ax. 1397 . n - This value should be the same as the local size used in creating the 1398 x vector for the matrix-vector product y = Ax. (or `PETSC_DECIDE` to have 1399 calculated if `N` is given) For square matrices n is almost always `m`. 1400 . M - number of global rows (or `PETSC_DETERMINE` to have calculated if `m` is given) 1401 . N - number of global columns (or `PETSC_DETERMINE` to have calculated if `n` is given) 1402 . d_rlenmax - max number of nonzeros per row in DIAGONAL portion of local submatrix 1403 (same value is used for all local rows) 1404 . d_rlen - array containing the number of nonzeros in the various rows of the 1405 DIAGONAL portion of the local submatrix (possibly different for each row) 1406 or `NULL`, if d_rlenmax is used to specify the nonzero structure. 1407 The size of this array is equal to the number of local rows, i.e `m`. 1408 . o_rlenmax - max number of nonzeros per row in the OFF-DIAGONAL portion of local 1409 submatrix (same value is used for all local rows). 1410 - o_rlen - array containing the number of nonzeros in the various rows of the 1411 OFF-DIAGONAL portion of the local submatrix (possibly different for 1412 each row) or `NULL`, if `o_rlenmax` is used to specify the nonzero 1413 structure. The size of this array is equal to the number 1414 of local rows, i.e `m`. 1415 1416 Output Parameter: 1417 . A - the matrix 1418 1419 Options Database Key: 1420 - -mat_sell_oneindex - Internally use indexing starting at 1 1421 rather than 0. When calling `MatSetValues()`, 1422 the user still MUST index entries starting at 0! 1423 1424 Example: 1425 Consider the following 8x8 matrix with 34 non-zero values, that is 1426 assembled across 3 processors. Lets assume that proc0 owns 3 rows, 1427 proc1 owns 3 rows, proc2 owns 2 rows. This division can be shown 1428 as follows 1429 1430 .vb 1431 1 2 0 | 0 3 0 | 0 4 1432 Proc0 0 5 6 | 7 0 0 | 8 0 1433 9 0 10 | 11 0 0 | 12 0 1434 ------------------------------------- 1435 13 0 14 | 15 16 17 | 0 0 1436 Proc1 0 18 0 | 19 20 21 | 0 0 1437 0 0 0 | 22 23 0 | 24 0 1438 ------------------------------------- 1439 Proc2 25 26 27 | 0 0 28 | 29 0 1440 30 0 0 | 31 32 33 | 0 34 1441 .ve 1442 1443 This can be represented as a collection of submatrices as 1444 .vb 1445 A B C 1446 D E F 1447 G H I 1448 .ve 1449 1450 Where the submatrices A,B,C are owned by proc0, D,E,F are 1451 owned by proc1, G,H,I are owned by proc2. 1452 1453 The 'm' parameters for proc0,proc1,proc2 are 3,3,2 respectively. 1454 The 'n' parameters for proc0,proc1,proc2 are 3,3,2 respectively. 1455 The 'M','N' parameters are 8,8, and have the same values on all procs. 1456 1457 The DIAGONAL submatrices corresponding to proc0,proc1,proc2 are 1458 submatrices [A], [E], [I] respectively. The OFF-DIAGONAL submatrices 1459 corresponding to proc0,proc1,proc2 are [BC], [DF], [GH] respectively. 1460 Internally, each processor stores the DIAGONAL part, and the OFF-DIAGONAL 1461 part as `MATSEQSELL` matrices. For example, proc1 will store [E] as a `MATSEQSELL` 1462 matrix, ans [DF] as another `MATSEQSELL` matrix. 1463 1464 When d_rlenmax, o_rlenmax parameters are specified, d_rlenmax storage elements are 1465 allocated for every row of the local diagonal submatrix, and o_rlenmax 1466 storage locations are allocated for every row of the OFF-DIAGONAL submat. 1467 One way to choose d_rlenmax and o_rlenmax is to use the max nonzerors per local 1468 rows for each of the local DIAGONAL, and the OFF-DIAGONAL submatrices. 1469 In this case, the values of d_rlenmax,o_rlenmax are 1470 .vb 1471 proc0 - d_rlenmax = 2, o_rlenmax = 2 1472 proc1 - d_rlenmax = 3, o_rlenmax = 2 1473 proc2 - d_rlenmax = 1, o_rlenmax = 4 1474 .ve 1475 We are allocating m*(d_rlenmax+o_rlenmax) storage locations for every proc. This 1476 translates to 3*(2+2)=12 for proc0, 3*(3+2)=15 for proc1, 2*(1+4)=10 1477 for proc3. i.e we are using 12+15+10=37 storage locations to store 1478 34 values. 1479 1480 When `d_rlen`, `o_rlen` parameters are specified, the storage is specified 1481 for every row, corresponding to both DIAGONAL and OFF-DIAGONAL submatrices. 1482 In the above case the values for `d_nnz`, `o_nnz` are 1483 .vb 1484 proc0 - d_nnz = [2,2,2] and o_nnz = [2,2,2] 1485 proc1 - d_nnz = [3,3,2] and o_nnz = [2,1,1] 1486 proc2 - d_nnz = [1,1] and o_nnz = [4,4] 1487 .ve 1488 Here the space allocated is still 37 though there are 34 nonzeros because 1489 the allocation is always done according to rlenmax. 1490 1491 Level: intermediate 1492 1493 Notes: 1494 It is recommended that one use the `MatCreate()`, `MatSetType()` and/or `MatSetFromOptions()`, 1495 MatXXXXSetPreallocation() paradigm instead of this routine directly. 1496 [MatXXXXSetPreallocation() is, for example, `MatSeqSELLSetPreallocation()`] 1497 1498 If the *_rlen parameter is given then the *_rlenmax parameter is ignored 1499 1500 `m`, `n`, `M`, `N` parameters specify the size of the matrix, and its partitioning across 1501 processors, while `d_rlenmax`, `d_rlen`, `o_rlenmax` , `o_rlen` parameters specify the approximate 1502 storage requirements for this matrix. 1503 1504 If `PETSC_DECIDE` or `PETSC_DETERMINE` is used for a particular argument on one 1505 processor than it must be used on all processors that share the object for 1506 that argument. 1507 1508 The user MUST specify either the local or global matrix dimensions 1509 (possibly both). 1510 1511 The parallel matrix is partitioned across processors such that the 1512 first m0 rows belong to process 0, the next m1 rows belong to 1513 process 1, the next m2 rows belong to process 2 etc.. where 1514 m0,m1,m2,.. are the input parameter 'm'. i.e each processor stores 1515 values corresponding to [`m` x `N`] submatrix. 1516 1517 The columns are logically partitioned with the n0 columns belonging 1518 to 0th partition, the next n1 columns belonging to the next 1519 partition etc.. where n0,n1,n2... are the input parameter `n`. 1520 1521 The DIAGONAL portion of the local submatrix on any given processor 1522 is the submatrix corresponding to the rows and columns `m`, `n` 1523 corresponding to the given processor. i.e diagonal matrix on 1524 process 0 is [m0 x n0], diagonal matrix on process 1 is [m1 x n1] 1525 etc. The remaining portion of the local submatrix [m x (N-n)] 1526 constitute the OFF-DIAGONAL portion. The example below better 1527 illustrates this concept. 1528 1529 For a square global matrix we define each processor's diagonal portion 1530 to be its local rows and the corresponding columns (a square submatrix); 1531 each processor's off-diagonal portion encompasses the remainder of the 1532 local matrix (a rectangular submatrix). 1533 1534 If `o_rlen`, `d_rlen` are specified, then `o_rlenmax`, and `d_rlenmax` are ignored. 1535 1536 When calling this routine with a single process communicator, a matrix of 1537 type `MATSEQSELL` is returned. If a matrix of type `MATMPISELL` is desired for this 1538 type of communicator, use the construction mechanism 1539 .vb 1540 MatCreate(...,&A); 1541 MatSetType(A,MATMPISELL); 1542 MatSetSizes(A, m,n,M,N); 1543 MatMPISELLSetPreallocation(A,...); 1544 .ve 1545 1546 .seealso: `Mat`, `MATSELL`, `MatCreate()`, `MatCreateSeqSELL()`, `MatSetValues()`, `MatMPISELLSetPreallocation()`, `MatMPISELLSetPreallocationSELL()`, 1547 `MATMPISELL`, `MatCreateMPISELLWithArrays()` 1548 @*/ 1549 PetscErrorCode MatCreateSELL(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, PetscInt N, PetscInt d_rlenmax, const PetscInt d_rlen[], PetscInt o_rlenmax, const PetscInt o_rlen[], Mat *A) 1550 { 1551 PetscMPIInt size; 1552 1553 PetscFunctionBegin; 1554 PetscCall(MatCreate(comm, A)); 1555 PetscCall(MatSetSizes(*A, m, n, M, N)); 1556 PetscCallMPI(MPI_Comm_size(comm, &size)); 1557 if (size > 1) { 1558 PetscCall(MatSetType(*A, MATMPISELL)); 1559 PetscCall(MatMPISELLSetPreallocation(*A, d_rlenmax, d_rlen, o_rlenmax, o_rlen)); 1560 } else { 1561 PetscCall(MatSetType(*A, MATSEQSELL)); 1562 PetscCall(MatSeqSELLSetPreallocation(*A, d_rlenmax, d_rlen)); 1563 } 1564 PetscFunctionReturn(PETSC_SUCCESS); 1565 } 1566 1567 PetscErrorCode MatMPISELLGetSeqSELL(Mat A, Mat *Ad, Mat *Ao, const PetscInt *colmap[]) 1568 { 1569 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 1570 PetscBool flg; 1571 1572 PetscFunctionBegin; 1573 PetscCall(PetscObjectTypeCompare((PetscObject)A, MATMPISELL, &flg)); 1574 PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "This function requires a MATMPISELL matrix as input"); 1575 if (Ad) *Ad = a->A; 1576 if (Ao) *Ao = a->B; 1577 if (colmap) *colmap = a->garray; 1578 PetscFunctionReturn(PETSC_SUCCESS); 1579 } 1580 1581 /*@C 1582 MatMPISELLGetLocalMatCondensed - Creates a `MATSEQSELL` matrix from an `MATMPISELL` matrix by taking all its local rows and NON-ZERO columns 1583 1584 Not Collective 1585 1586 Input Parameters: 1587 + A - the matrix 1588 . scall - either `MAT_INITIAL_MATRIX` or `MAT_REUSE_MATRIX` 1589 . row - index sets of rows to extract (or `NULL`) 1590 - col - index sets of columns to extract (or `NULL`) 1591 1592 Output Parameter: 1593 . A_loc - the local sequential matrix generated 1594 1595 Level: developer 1596 1597 .seealso: `Mat`, `MATSEQSELL`, `MATMPISELL`, `MatGetOwnershipRange()`, `MatMPISELLGetLocalMat()` 1598 @*/ 1599 PetscErrorCode MatMPISELLGetLocalMatCondensed(Mat A, MatReuse scall, IS *row, IS *col, Mat *A_loc) 1600 { 1601 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 1602 PetscInt i, start, end, ncols, nzA, nzB, *cmap, imark, *idx; 1603 IS isrowa, iscola; 1604 Mat *aloc; 1605 PetscBool match; 1606 1607 PetscFunctionBegin; 1608 PetscCall(PetscObjectTypeCompare((PetscObject)A, MATMPISELL, &match)); 1609 PetscCheck(match, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Requires MATMPISELL matrix as input"); 1610 PetscCall(PetscLogEventBegin(MAT_Getlocalmatcondensed, A, 0, 0, 0)); 1611 if (!row) { 1612 start = A->rmap->rstart; 1613 end = A->rmap->rend; 1614 PetscCall(ISCreateStride(PETSC_COMM_SELF, end - start, start, 1, &isrowa)); 1615 } else { 1616 isrowa = *row; 1617 } 1618 if (!col) { 1619 start = A->cmap->rstart; 1620 cmap = a->garray; 1621 nzA = a->A->cmap->n; 1622 nzB = a->B->cmap->n; 1623 PetscCall(PetscMalloc1(nzA + nzB, &idx)); 1624 ncols = 0; 1625 for (i = 0; i < nzB; i++) { 1626 if (cmap[i] < start) idx[ncols++] = cmap[i]; 1627 else break; 1628 } 1629 imark = i; 1630 for (i = 0; i < nzA; i++) idx[ncols++] = start + i; 1631 for (i = imark; i < nzB; i++) idx[ncols++] = cmap[i]; 1632 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, ncols, idx, PETSC_OWN_POINTER, &iscola)); 1633 } else { 1634 iscola = *col; 1635 } 1636 if (scall != MAT_INITIAL_MATRIX) { 1637 PetscCall(PetscMalloc1(1, &aloc)); 1638 aloc[0] = *A_loc; 1639 } 1640 PetscCall(MatCreateSubMatrices(A, 1, &isrowa, &iscola, scall, &aloc)); 1641 *A_loc = aloc[0]; 1642 PetscCall(PetscFree(aloc)); 1643 if (!row) PetscCall(ISDestroy(&isrowa)); 1644 if (!col) PetscCall(ISDestroy(&iscola)); 1645 PetscCall(PetscLogEventEnd(MAT_Getlocalmatcondensed, A, 0, 0, 0)); 1646 PetscFunctionReturn(PETSC_SUCCESS); 1647 } 1648 1649 #include <../src/mat/impls/aij/mpi/mpiaij.h> 1650 1651 PetscErrorCode MatConvert_MPISELL_MPIAIJ(Mat A, MatType newtype, MatReuse reuse, Mat *newmat) 1652 { 1653 Mat_MPISELL *a = (Mat_MPISELL *)A->data; 1654 Mat B; 1655 Mat_MPIAIJ *b; 1656 1657 PetscFunctionBegin; 1658 PetscCheck(A->assembled, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Matrix must be assembled"); 1659 1660 if (reuse == MAT_REUSE_MATRIX) { 1661 B = *newmat; 1662 } else { 1663 PetscCall(MatCreate(PetscObjectComm((PetscObject)A), &B)); 1664 PetscCall(MatSetType(B, MATMPIAIJ)); 1665 PetscCall(MatSetSizes(B, A->rmap->n, A->cmap->n, A->rmap->N, A->cmap->N)); 1666 PetscCall(MatSetBlockSizes(B, A->rmap->bs, A->cmap->bs)); 1667 PetscCall(MatSeqAIJSetPreallocation(B, 0, NULL)); 1668 PetscCall(MatMPIAIJSetPreallocation(B, 0, NULL, 0, NULL)); 1669 } 1670 b = (Mat_MPIAIJ *)B->data; 1671 1672 if (reuse == MAT_REUSE_MATRIX) { 1673 PetscCall(MatConvert_SeqSELL_SeqAIJ(a->A, MATSEQAIJ, MAT_REUSE_MATRIX, &b->A)); 1674 PetscCall(MatConvert_SeqSELL_SeqAIJ(a->B, MATSEQAIJ, MAT_REUSE_MATRIX, &b->B)); 1675 } else { 1676 PetscCall(MatDestroy(&b->A)); 1677 PetscCall(MatDestroy(&b->B)); 1678 PetscCall(MatDisAssemble_MPISELL(A)); 1679 PetscCall(MatConvert_SeqSELL_SeqAIJ(a->A, MATSEQAIJ, MAT_INITIAL_MATRIX, &b->A)); 1680 PetscCall(MatConvert_SeqSELL_SeqAIJ(a->B, MATSEQAIJ, MAT_INITIAL_MATRIX, &b->B)); 1681 PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY)); 1682 PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY)); 1683 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 1684 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 1685 } 1686 1687 if (reuse == MAT_INPLACE_MATRIX) { 1688 PetscCall(MatHeaderReplace(A, &B)); 1689 } else { 1690 *newmat = B; 1691 } 1692 PetscFunctionReturn(PETSC_SUCCESS); 1693 } 1694 1695 PetscErrorCode MatConvert_MPIAIJ_MPISELL(Mat A, MatType newtype, MatReuse reuse, Mat *newmat) 1696 { 1697 Mat_MPIAIJ *a = (Mat_MPIAIJ *)A->data; 1698 Mat B; 1699 Mat_MPISELL *b; 1700 1701 PetscFunctionBegin; 1702 PetscCheck(A->assembled, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Matrix must be assembled"); 1703 1704 if (reuse == MAT_REUSE_MATRIX) { 1705 B = *newmat; 1706 } else { 1707 PetscCall(MatCreate(PetscObjectComm((PetscObject)A), &B)); 1708 PetscCall(MatSetType(B, MATMPISELL)); 1709 PetscCall(MatSetSizes(B, A->rmap->n, A->cmap->n, A->rmap->N, A->cmap->N)); 1710 PetscCall(MatSetBlockSizes(B, A->rmap->bs, A->cmap->bs)); 1711 PetscCall(MatSeqAIJSetPreallocation(B, 0, NULL)); 1712 PetscCall(MatMPIAIJSetPreallocation(B, 0, NULL, 0, NULL)); 1713 } 1714 b = (Mat_MPISELL *)B->data; 1715 1716 if (reuse == MAT_REUSE_MATRIX) { 1717 PetscCall(MatConvert_SeqAIJ_SeqSELL(a->A, MATSEQSELL, MAT_REUSE_MATRIX, &b->A)); 1718 PetscCall(MatConvert_SeqAIJ_SeqSELL(a->B, MATSEQSELL, MAT_REUSE_MATRIX, &b->B)); 1719 } else { 1720 PetscCall(MatDestroy(&b->A)); 1721 PetscCall(MatDestroy(&b->B)); 1722 PetscCall(MatDisAssemble_MPIAIJ(A)); 1723 PetscCall(MatConvert_SeqAIJ_SeqSELL(a->A, MATSEQSELL, MAT_INITIAL_MATRIX, &b->A)); 1724 PetscCall(MatConvert_SeqAIJ_SeqSELL(a->B, MATSEQSELL, MAT_INITIAL_MATRIX, &b->B)); 1725 PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY)); 1726 PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY)); 1727 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 1728 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 1729 } 1730 1731 if (reuse == MAT_INPLACE_MATRIX) { 1732 PetscCall(MatHeaderReplace(A, &B)); 1733 } else { 1734 *newmat = B; 1735 } 1736 PetscFunctionReturn(PETSC_SUCCESS); 1737 } 1738 1739 PetscErrorCode MatSOR_MPISELL(Mat matin, Vec bb, PetscReal omega, MatSORType flag, PetscReal fshift, PetscInt its, PetscInt lits, Vec xx) 1740 { 1741 Mat_MPISELL *mat = (Mat_MPISELL *)matin->data; 1742 Vec bb1 = NULL; 1743 1744 PetscFunctionBegin; 1745 if (flag == SOR_APPLY_UPPER) { 1746 PetscCall((*mat->A->ops->sor)(mat->A, bb, omega, flag, fshift, lits, 1, xx)); 1747 PetscFunctionReturn(PETSC_SUCCESS); 1748 } 1749 1750 if (its > 1 || ~flag & SOR_ZERO_INITIAL_GUESS || flag & SOR_EISENSTAT) PetscCall(VecDuplicate(bb, &bb1)); 1751 1752 if ((flag & SOR_LOCAL_SYMMETRIC_SWEEP) == SOR_LOCAL_SYMMETRIC_SWEEP) { 1753 if (flag & SOR_ZERO_INITIAL_GUESS) { 1754 PetscCall((*mat->A->ops->sor)(mat->A, bb, omega, flag, fshift, lits, 1, xx)); 1755 its--; 1756 } 1757 1758 while (its--) { 1759 PetscCall(VecScatterBegin(mat->Mvctx, xx, mat->lvec, INSERT_VALUES, SCATTER_FORWARD)); 1760 PetscCall(VecScatterEnd(mat->Mvctx, xx, mat->lvec, INSERT_VALUES, SCATTER_FORWARD)); 1761 1762 /* update rhs: bb1 = bb - B*x */ 1763 PetscCall(VecScale(mat->lvec, -1.0)); 1764 PetscCall((*mat->B->ops->multadd)(mat->B, mat->lvec, bb, bb1)); 1765 1766 /* local sweep */ 1767 PetscCall((*mat->A->ops->sor)(mat->A, bb1, omega, SOR_SYMMETRIC_SWEEP, fshift, lits, 1, xx)); 1768 } 1769 } else if (flag & SOR_LOCAL_FORWARD_SWEEP) { 1770 if (flag & SOR_ZERO_INITIAL_GUESS) { 1771 PetscCall((*mat->A->ops->sor)(mat->A, bb, omega, flag, fshift, lits, 1, xx)); 1772 its--; 1773 } 1774 while (its--) { 1775 PetscCall(VecScatterBegin(mat->Mvctx, xx, mat->lvec, INSERT_VALUES, SCATTER_FORWARD)); 1776 PetscCall(VecScatterEnd(mat->Mvctx, xx, mat->lvec, INSERT_VALUES, SCATTER_FORWARD)); 1777 1778 /* update rhs: bb1 = bb - B*x */ 1779 PetscCall(VecScale(mat->lvec, -1.0)); 1780 PetscCall((*mat->B->ops->multadd)(mat->B, mat->lvec, bb, bb1)); 1781 1782 /* local sweep */ 1783 PetscCall((*mat->A->ops->sor)(mat->A, bb1, omega, SOR_FORWARD_SWEEP, fshift, lits, 1, xx)); 1784 } 1785 } else if (flag & SOR_LOCAL_BACKWARD_SWEEP) { 1786 if (flag & SOR_ZERO_INITIAL_GUESS) { 1787 PetscCall((*mat->A->ops->sor)(mat->A, bb, omega, flag, fshift, lits, 1, xx)); 1788 its--; 1789 } 1790 while (its--) { 1791 PetscCall(VecScatterBegin(mat->Mvctx, xx, mat->lvec, INSERT_VALUES, SCATTER_FORWARD)); 1792 PetscCall(VecScatterEnd(mat->Mvctx, xx, mat->lvec, INSERT_VALUES, SCATTER_FORWARD)); 1793 1794 /* update rhs: bb1 = bb - B*x */ 1795 PetscCall(VecScale(mat->lvec, -1.0)); 1796 PetscCall((*mat->B->ops->multadd)(mat->B, mat->lvec, bb, bb1)); 1797 1798 /* local sweep */ 1799 PetscCall((*mat->A->ops->sor)(mat->A, bb1, omega, SOR_BACKWARD_SWEEP, fshift, lits, 1, xx)); 1800 } 1801 } else SETERRQ(PetscObjectComm((PetscObject)matin), PETSC_ERR_SUP, "Parallel SOR not supported"); 1802 1803 PetscCall(VecDestroy(&bb1)); 1804 1805 matin->factorerrortype = mat->A->factorerrortype; 1806 PetscFunctionReturn(PETSC_SUCCESS); 1807 } 1808 1809 /*MC 1810 MATMPISELL - MATMPISELL = "MPISELL" - A matrix type to be used for parallel sparse matrices. 1811 1812 Options Database Keys: 1813 . -mat_type mpisell - sets the matrix type to `MATMPISELL` during a call to `MatSetFromOptions()` 1814 1815 Level: beginner 1816 1817 .seealso: `Mat`, `MATSELL`, `MATSEQSELL` `MatCreateSELL()` 1818 M*/ 1819 PETSC_EXTERN PetscErrorCode MatCreate_MPISELL(Mat B) 1820 { 1821 Mat_MPISELL *b; 1822 PetscMPIInt size; 1823 1824 PetscFunctionBegin; 1825 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)B), &size)); 1826 PetscCall(PetscNew(&b)); 1827 B->data = (void *)b; 1828 PetscCall(PetscMemcpy(B->ops, &MatOps_Values, sizeof(struct _MatOps))); 1829 B->assembled = PETSC_FALSE; 1830 B->insertmode = NOT_SET_VALUES; 1831 b->size = size; 1832 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)B), &b->rank)); 1833 /* build cache for off array entries formed */ 1834 PetscCall(MatStashCreate_Private(PetscObjectComm((PetscObject)B), 1, &B->stash)); 1835 1836 b->donotstash = PETSC_FALSE; 1837 b->colmap = NULL; 1838 b->garray = NULL; 1839 b->roworiented = PETSC_TRUE; 1840 1841 /* stuff used for matrix vector multiply */ 1842 b->lvec = NULL; 1843 b->Mvctx = NULL; 1844 1845 /* stuff for MatGetRow() */ 1846 b->rowindices = NULL; 1847 b->rowvalues = NULL; 1848 b->getrowactive = PETSC_FALSE; 1849 1850 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatStoreValues_C", MatStoreValues_MPISELL)); 1851 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatRetrieveValues_C", MatRetrieveValues_MPISELL)); 1852 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatIsTranspose_C", MatIsTranspose_MPISELL)); 1853 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatMPISELLSetPreallocation_C", MatMPISELLSetPreallocation_MPISELL)); 1854 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatConvert_mpisell_mpiaij_C", MatConvert_MPISELL_MPIAIJ)); 1855 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatDiagonalScaleLocal_C", MatDiagonalScaleLocal_MPISELL)); 1856 PetscCall(PetscObjectChangeTypeName((PetscObject)B, MATMPISELL)); 1857 PetscFunctionReturn(PETSC_SUCCESS); 1858 } 1859