1 #include <petsc/private/vecimpl.h> 2 #include <petsc/private/matimpl.h> 3 #include <petsc/private/petschpddm.h> /*I "petscpc.h" I*/ 4 #include <petsc/private/pcimpl.h> 5 #include <petsc/private/dmimpl.h> /* this must be included after petschpddm.h so that DM_MAX_WORK_VECTORS is not defined */ 6 /* otherwise, it is assumed that one is compiling libhpddm_petsc => circular dependency */ 7 #if PetscDefined(USE_FORTRAN_BINDINGS) 8 #include <petsc/private/fortranimpl.h> 9 #endif 10 11 static PetscErrorCode (*loadedSym)(HPDDM::Schwarz<PetscScalar> *const, IS, Mat, Mat, Mat, std::vector<Vec>, PC_HPDDM_Level **const) = nullptr; 12 13 static PetscBool PCHPDDMPackageInitialized = PETSC_FALSE; 14 15 PetscLogEvent PC_HPDDM_Strc; 16 PetscLogEvent PC_HPDDM_PtAP; 17 PetscLogEvent PC_HPDDM_PtBP; 18 PetscLogEvent PC_HPDDM_Next; 19 PetscLogEvent PC_HPDDM_SetUp[PETSC_PCHPDDM_MAXLEVELS]; 20 PetscLogEvent PC_HPDDM_Solve[PETSC_PCHPDDM_MAXLEVELS]; 21 22 const char *const PCHPDDMCoarseCorrectionTypes[] = {"DEFLATED", "ADDITIVE", "BALANCED", "PCHPDDMCoarseCorrectionType", "PC_HPDDM_COARSE_CORRECTION_", nullptr}; 23 const char *const PCHPDDMSchurPreTypes[] = {"LEAST_SQUARES", "GENEO", "PCHPDDMSchurPreType", "PC_HPDDM_SCHUR_PRE", nullptr}; 24 25 static PetscErrorCode PCReset_HPDDM(PC pc) 26 { 27 PC_HPDDM *data = (PC_HPDDM *)pc->data; 28 PetscInt i; 29 30 PetscFunctionBegin; 31 if (data->levels) { 32 for (i = 0; i < PETSC_PCHPDDM_MAXLEVELS && data->levels[i]; ++i) { 33 PetscCall(KSPDestroy(&data->levels[i]->ksp)); 34 PetscCall(PCDestroy(&data->levels[i]->pc)); 35 PetscCall(PetscFree(data->levels[i])); 36 } 37 PetscCall(PetscFree(data->levels)); 38 } 39 40 PetscCall(ISDestroy(&data->is)); 41 PetscCall(MatDestroy(&data->aux)); 42 PetscCall(MatDestroy(&data->B)); 43 PetscCall(VecDestroy(&data->normal)); 44 data->correction = PC_HPDDM_COARSE_CORRECTION_DEFLATED; 45 data->Neumann = PETSC_BOOL3_UNKNOWN; 46 data->deflation = PETSC_FALSE; 47 data->setup = nullptr; 48 data->setup_ctx = nullptr; 49 PetscFunctionReturn(PETSC_SUCCESS); 50 } 51 52 static PetscErrorCode PCDestroy_HPDDM(PC pc) 53 { 54 PC_HPDDM *data = (PC_HPDDM *)pc->data; 55 56 PetscFunctionBegin; 57 PetscCall(PCReset_HPDDM(pc)); 58 PetscCall(PetscFree(data)); 59 PetscCall(PetscObjectChangeTypeName((PetscObject)pc, nullptr)); 60 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetAuxiliaryMat_C", nullptr)); 61 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMHasNeumannMat_C", nullptr)); 62 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetRHSMat_C", nullptr)); 63 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetCoarseCorrectionType_C", nullptr)); 64 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetCoarseCorrectionType_C", nullptr)); 65 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetSTShareSubKSP_C", nullptr)); 66 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetSTShareSubKSP_C", nullptr)); 67 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetDeflationMat_C", nullptr)); 68 PetscCall(PetscObjectCompose((PetscObject)pc, "_PCHPDDM_Schur", nullptr)); 69 PetscFunctionReturn(PETSC_SUCCESS); 70 } 71 72 static inline PetscErrorCode PCHPDDMSetAuxiliaryMat_Private(PC pc, IS is, Mat A, PetscBool deflation) 73 { 74 PC_HPDDM *data = (PC_HPDDM *)pc->data; 75 76 PetscFunctionBegin; 77 if (is) { 78 PetscCall(PetscObjectReference((PetscObject)is)); 79 if (data->is) { /* new overlap definition resets the PC */ 80 PetscCall(PCReset_HPDDM(pc)); 81 pc->setfromoptionscalled = 0; 82 pc->setupcalled = 0; 83 } 84 PetscCall(ISDestroy(&data->is)); 85 data->is = is; 86 } 87 if (A) { 88 PetscCall(PetscObjectReference((PetscObject)A)); 89 PetscCall(MatDestroy(&data->aux)); 90 data->aux = A; 91 } 92 data->deflation = deflation; 93 PetscFunctionReturn(PETSC_SUCCESS); 94 } 95 96 static inline PetscErrorCode PCHPDDMSetAuxiliaryMatNormal_Private(PC pc, Mat A, Mat N, Mat *B, const char *pcpre, Vec *diagonal = nullptr) 97 { 98 PC_HPDDM *data = (PC_HPDDM *)pc->data; 99 Mat *splitting, *sub, aux; 100 Vec d; 101 IS is, cols[2], rows; 102 PetscReal norm; 103 PetscBool flg; 104 char type[256] = {}; /* same size as in src/ksp/pc/interface/pcset.c */ 105 106 PetscFunctionBegin; 107 PetscCall(MatConvert(N, MATAIJ, MAT_INITIAL_MATRIX, B)); 108 PetscCall(ISCreateStride(PETSC_COMM_SELF, A->cmap->n, A->cmap->rstart, 1, cols)); 109 PetscCall(ISCreateStride(PETSC_COMM_SELF, A->rmap->N, 0, 1, &rows)); 110 PetscCall(MatSetOption(A, MAT_SUBMAT_SINGLEIS, PETSC_TRUE)); 111 PetscCall(MatIncreaseOverlap(*B, 1, cols, 1)); 112 PetscCall(MatCreateSubMatrices(A, 1, &rows, cols, MAT_INITIAL_MATRIX, &splitting)); 113 PetscCall(ISCreateStride(PETSC_COMM_SELF, A->cmap->n, A->cmap->rstart, 1, &is)); 114 PetscCall(ISEmbed(*cols, is, PETSC_TRUE, cols + 1)); 115 PetscCall(ISDestroy(&is)); 116 PetscCall(MatCreateSubMatrices(*splitting, 1, &rows, cols + 1, MAT_INITIAL_MATRIX, &sub)); 117 PetscCall(ISDestroy(cols + 1)); 118 PetscCall(MatFindZeroRows(*sub, &is)); 119 PetscCall(MatDestroySubMatrices(1, &sub)); 120 PetscCall(ISDestroy(&rows)); 121 PetscCall(MatSetOption(*splitting, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE)); 122 PetscCall(MatZeroRowsIS(*splitting, is, 0.0, nullptr, nullptr)); 123 PetscCall(ISDestroy(&is)); 124 PetscCall(PetscOptionsGetString(nullptr, pcpre, "-pc_hpddm_levels_1_sub_pc_type", type, sizeof(type), nullptr)); 125 PetscCall(PetscStrcmp(type, PCQR, &flg)); 126 if (!flg) { 127 Mat conjugate = *splitting; 128 if (PetscDefined(USE_COMPLEX)) { 129 PetscCall(MatDuplicate(*splitting, MAT_COPY_VALUES, &conjugate)); 130 PetscCall(MatConjugate(conjugate)); 131 } 132 PetscCall(MatTransposeMatMult(conjugate, *splitting, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &aux)); 133 if (PetscDefined(USE_COMPLEX)) PetscCall(MatDestroy(&conjugate)); 134 PetscCall(MatNorm(aux, NORM_FROBENIUS, &norm)); 135 PetscCall(MatSetOption(aux, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE)); 136 if (diagonal) { 137 PetscCall(VecNorm(*diagonal, NORM_INFINITY, &norm)); 138 if (norm > PETSC_SMALL) { 139 VecScatter scatter; 140 PetscInt n; 141 PetscCall(ISGetLocalSize(*cols, &n)); 142 PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)pc), n, PETSC_DECIDE, &d)); 143 PetscCall(VecScatterCreate(*diagonal, *cols, d, nullptr, &scatter)); 144 PetscCall(VecScatterBegin(scatter, *diagonal, d, INSERT_VALUES, SCATTER_FORWARD)); 145 PetscCall(VecScatterEnd(scatter, *diagonal, d, INSERT_VALUES, SCATTER_FORWARD)); 146 PetscCall(VecScatterDestroy(&scatter)); 147 PetscCall(MatScale(aux, -1.0)); 148 PetscCall(MatDiagonalSet(aux, d, ADD_VALUES)); 149 PetscCall(VecDestroy(&d)); 150 } else PetscCall(VecDestroy(diagonal)); 151 } 152 if (!diagonal) PetscCall(MatShift(aux, PETSC_SMALL * norm)); 153 } else { 154 PetscBool flg; 155 if (diagonal) { 156 PetscCall(VecNorm(*diagonal, NORM_INFINITY, &norm)); 157 PetscCheck(norm < PETSC_SMALL, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Nonzero diagonal A11 block"); 158 PetscCall(VecDestroy(diagonal)); 159 } 160 PetscCall(PetscObjectTypeCompare((PetscObject)N, MATNORMAL, &flg)); 161 if (flg) PetscCall(MatCreateNormal(*splitting, &aux)); 162 else PetscCall(MatCreateNormalHermitian(*splitting, &aux)); 163 } 164 PetscCall(MatDestroySubMatrices(1, &splitting)); 165 PetscCall(PCHPDDMSetAuxiliaryMat(pc, *cols, aux, nullptr, nullptr)); 166 data->Neumann = PETSC_BOOL3_TRUE; 167 PetscCall(ISDestroy(cols)); 168 PetscCall(MatDestroy(&aux)); 169 PetscFunctionReturn(PETSC_SUCCESS); 170 } 171 172 static PetscErrorCode PCHPDDMSetAuxiliaryMat_HPDDM(PC pc, IS is, Mat A, PetscErrorCode (*setup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void *setup_ctx) 173 { 174 PC_HPDDM *data = (PC_HPDDM *)pc->data; 175 176 PetscFunctionBegin; 177 PetscCall(PCHPDDMSetAuxiliaryMat_Private(pc, is, A, PETSC_FALSE)); 178 if (setup) { 179 data->setup = setup; 180 data->setup_ctx = setup_ctx; 181 } 182 PetscFunctionReturn(PETSC_SUCCESS); 183 } 184 185 /*@C 186 PCHPDDMSetAuxiliaryMat - Sets the auxiliary matrix used by `PCHPDDM` for the concurrent GenEO problems at the finest level. 187 188 Input Parameters: 189 + pc - preconditioner context 190 . is - index set of the local auxiliary, e.g., Neumann, matrix 191 . A - auxiliary sequential matrix 192 . setup - function for generating the auxiliary matrix entries, may be `NULL` 193 - ctx - context for `setup`, may be `NULL` 194 195 Calling sequence of `setup`: 196 + J - matrix whose values are to be set 197 . t - time 198 . X - linearization point 199 . X_t - time-derivative of the linearization point 200 . s - step 201 . ovl - index set of the local auxiliary, e.g., Neumann, matrix 202 - ctx - context for `setup`, may be `NULL` 203 204 Level: intermediate 205 206 Note: 207 As an example, in a finite element context with nonoverlapping subdomains plus (overlapping) ghost elements, this could be the unassembled (Neumann) 208 local overlapping operator. As opposed to the assembled (Dirichlet) local overlapping operator obtained by summing neighborhood contributions 209 at the interface of ghost elements. 210 211 Fortran Notes: 212 Only `PETSC_NULL_FUNCTION` is supported for `setup` and `ctx` is never accessed 213 214 .seealso: `PCHPDDM`, `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCHPDDMSetRHSMat()`, `MATIS` 215 @*/ 216 PetscErrorCode PCHPDDMSetAuxiliaryMat(PC pc, IS is, Mat A, PetscErrorCode (*setup)(Mat J, PetscReal t, Vec X, Vec X_t, PetscReal s, IS ovl, void *ctx), void *ctx) 217 { 218 PetscFunctionBegin; 219 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 220 if (is) PetscValidHeaderSpecific(is, IS_CLASSID, 2); 221 if (A) PetscValidHeaderSpecific(A, MAT_CLASSID, 3); 222 PetscTryMethod(pc, "PCHPDDMSetAuxiliaryMat_C", (PC, IS, Mat, PetscErrorCode(*)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void *), (pc, is, A, setup, ctx)); 223 PetscFunctionReturn(PETSC_SUCCESS); 224 } 225 226 static PetscErrorCode PCHPDDMHasNeumannMat_HPDDM(PC pc, PetscBool has) 227 { 228 PC_HPDDM *data = (PC_HPDDM *)pc->data; 229 230 PetscFunctionBegin; 231 data->Neumann = PetscBoolToBool3(has); 232 PetscFunctionReturn(PETSC_SUCCESS); 233 } 234 235 /*@ 236 PCHPDDMHasNeumannMat - Informs `PCHPDDM` that the `Mat` passed to `PCHPDDMSetAuxiliaryMat()` is the local Neumann matrix. 237 238 Input Parameters: 239 + pc - preconditioner context 240 - has - Boolean value 241 242 Level: intermediate 243 244 Notes: 245 This may be used to bypass a call to `MatCreateSubMatrices()` and to `MatConvert()` for `MATSBAIJ` matrices. 246 247 If a `DMCreateNeumannOverlap()` implementation is available in the `DM` attached to the Pmat, or the Amat, or the `PC`, the flag is internally set to `PETSC_TRUE`. Its default value is otherwise `PETSC_FALSE`. 248 249 .seealso: `PCHPDDM`, `PCHPDDMSetAuxiliaryMat()` 250 @*/ 251 PetscErrorCode PCHPDDMHasNeumannMat(PC pc, PetscBool has) 252 { 253 PetscFunctionBegin; 254 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 255 PetscTryMethod(pc, "PCHPDDMHasNeumannMat_C", (PC, PetscBool), (pc, has)); 256 PetscFunctionReturn(PETSC_SUCCESS); 257 } 258 259 static PetscErrorCode PCHPDDMSetRHSMat_HPDDM(PC pc, Mat B) 260 { 261 PC_HPDDM *data = (PC_HPDDM *)pc->data; 262 263 PetscFunctionBegin; 264 PetscCall(PetscObjectReference((PetscObject)B)); 265 PetscCall(MatDestroy(&data->B)); 266 data->B = B; 267 PetscFunctionReturn(PETSC_SUCCESS); 268 } 269 270 /*@ 271 PCHPDDMSetRHSMat - Sets the right-hand side matrix used by `PCHPDDM` for the concurrent GenEO problems at the finest level. 272 273 Input Parameters: 274 + pc - preconditioner context 275 - B - right-hand side sequential matrix 276 277 Level: advanced 278 279 Note: 280 Must be used in conjunction with `PCHPDDMSetAuxiliaryMat`(N), so that Nv = lambda Bv is solved using `EPSSetOperators`(N, B). 281 It is assumed that N and `B` are provided using the same numbering. This provides a means to try more advanced methods such as GenEO-II or H-GenEO. 282 283 .seealso: `PCHPDDMSetAuxiliaryMat()`, `PCHPDDM` 284 @*/ 285 PetscErrorCode PCHPDDMSetRHSMat(PC pc, Mat B) 286 { 287 PetscFunctionBegin; 288 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 289 if (B) { 290 PetscValidHeaderSpecific(B, MAT_CLASSID, 2); 291 PetscTryMethod(pc, "PCHPDDMSetRHSMat_C", (PC, Mat), (pc, B)); 292 } 293 PetscFunctionReturn(PETSC_SUCCESS); 294 } 295 296 static PetscErrorCode PCSetFromOptions_HPDDM(PC pc, PetscOptionItems *PetscOptionsObject) 297 { 298 PC_HPDDM *data = (PC_HPDDM *)pc->data; 299 PC_HPDDM_Level **levels = data->levels; 300 char prefix[256]; 301 int i = 1; 302 PetscMPIInt size, previous; 303 PetscInt n, overlap = 1; 304 PCHPDDMCoarseCorrectionType type; 305 PetscBool flg = PETSC_TRUE, set; 306 307 PetscFunctionBegin; 308 if (!data->levels) { 309 PetscCall(PetscCalloc1(PETSC_PCHPDDM_MAXLEVELS, &levels)); 310 data->levels = levels; 311 } 312 PetscOptionsHeadBegin(PetscOptionsObject, "PCHPDDM options"); 313 PetscCall(PetscOptionsBoundedInt("-pc_hpddm_harmonic_overlap", "Overlap prior to computing local harmonic extensions", "PCHPDDM", overlap, &overlap, &set, 1)); 314 if (!set) overlap = -1; 315 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size)); 316 previous = size; 317 while (i < PETSC_PCHPDDM_MAXLEVELS) { 318 PetscInt p = 1; 319 320 if (!data->levels[i - 1]) PetscCall(PetscNew(data->levels + i - 1)); 321 data->levels[i - 1]->parent = data; 322 /* if the previous level has a single process, it is not possible to coarsen further */ 323 if (previous == 1 || !flg) break; 324 data->levels[i - 1]->nu = 0; 325 data->levels[i - 1]->threshold = -1.0; 326 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_nev", i)); 327 PetscCall(PetscOptionsBoundedInt(prefix, "Local number of deflation vectors computed by SLEPc", "EPSSetDimensions", data->levels[i - 1]->nu, &data->levels[i - 1]->nu, nullptr, 0)); 328 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_threshold", i)); 329 PetscCall(PetscOptionsReal(prefix, "Local threshold for selecting deflation vectors returned by SLEPc", "PCHPDDM", data->levels[i - 1]->threshold, &data->levels[i - 1]->threshold, nullptr)); 330 if (i == 1) { 331 PetscCheck(overlap == -1 || PetscAbsReal(data->levels[i - 1]->threshold + PetscReal(1.0)) < PETSC_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot supply both -pc_hpddm_levels_1_eps_threshold and -pc_hpddm_harmonic_overlap"); 332 if (overlap != -1) { 333 PetscInt nsv = 0; 334 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_svd_nsv", i)); 335 PetscCall(PetscOptionsBoundedInt(prefix, "Local number of deflation vectors computed by SLEPc", "SVDSetDimensions", nsv, &nsv, nullptr, 0)); 336 PetscCheck(bool(data->levels[0]->nu) != bool(nsv), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot supply %s -pc_hpddm_levels_1_eps_nev %s -pc_hpddm_levels_1_svd_nsv", nsv ? "both" : "neither", nsv ? "and" : "nor"); 337 if (nsv) { 338 data->levels[0]->nu = nsv; 339 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_svd_relative_threshold", i)); 340 } else PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_relative_threshold", i)); 341 PetscCall(PetscOptionsReal(prefix, "Local relative threshold for selecting deflation vectors returned by SLEPc", "PCHPDDM", data->levels[0]->threshold, &data->levels[0]->threshold, nullptr)); 342 } 343 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_1_st_share_sub_ksp")); 344 PetscCall(PetscOptionsBool(prefix, "Shared KSP between SLEPc ST and the fine-level subdomain solver", "PCHPDDMSetSTShareSubKSP", PETSC_FALSE, &data->share, nullptr)); 345 } 346 /* if there is no prescribed coarsening, just break out of the loop */ 347 if (data->levels[i - 1]->threshold <= PetscReal() && data->levels[i - 1]->nu <= 0 && !(data->deflation && i == 1)) break; 348 else { 349 ++i; 350 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_nev", i)); 351 PetscCall(PetscOptionsHasName(PetscOptionsObject->options, PetscOptionsObject->prefix, prefix, &flg)); 352 if (!flg) { 353 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_threshold", i)); 354 PetscCall(PetscOptionsHasName(PetscOptionsObject->options, PetscOptionsObject->prefix, prefix, &flg)); 355 } 356 if (flg) { 357 /* if there are coarsening options for the next level, then register it */ 358 /* otherwise, don't to avoid having both options levels_N_p and coarse_p */ 359 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_p", i)); 360 PetscCall(PetscOptionsRangeInt(prefix, "Number of processes used to assemble the coarse operator at this level", "PCHPDDM", p, &p, &flg, 1, PetscMax(1, previous / 2))); 361 previous = p; 362 } 363 } 364 } 365 data->N = i; 366 n = 1; 367 if (i > 1) { 368 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_coarse_p")); 369 PetscCall(PetscOptionsRangeInt(prefix, "Number of processes used to assemble the coarsest operator", "PCHPDDM", n, &n, nullptr, 1, PetscMax(1, previous / 2))); 370 #if PetscDefined(HAVE_MUMPS) 371 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "pc_hpddm_coarse_")); 372 PetscCall(PetscOptionsHasName(nullptr, prefix, "-mat_mumps_use_omp_threads", &flg)); 373 if (flg) { 374 char type[64]; /* same size as in src/ksp/pc/impls/factor/factimpl.c */ 375 PetscCall(PetscStrncpy(type, n > 1 && PetscDefined(HAVE_MUMPS) ? MATSOLVERMUMPS : MATSOLVERPETSC, sizeof(type))); /* default solver for a MatMPIAIJ or a MatSeqAIJ */ 376 PetscCall(PetscOptionsGetString(nullptr, prefix, "-pc_factor_mat_solver_type", type, sizeof(type), nullptr)); 377 PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg)); 378 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "-%smat_mumps_use_omp_threads and -%spc_factor_mat_solver_type != %s", prefix, prefix, MATSOLVERMUMPS); 379 size = n; 380 n = -1; 381 PetscCall(PetscOptionsGetInt(nullptr, prefix, "-mat_mumps_use_omp_threads", &n, nullptr)); 382 PetscCheck(n >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Need to specify a positive integer for -%smat_mumps_use_omp_threads", prefix); 383 PetscCheck(n * size <= previous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "%d MPI process%s x %d OpenMP thread%s greater than %d available MPI process%s for the coarsest operator", (int)size, size > 1 ? "es" : "", (int)n, n > 1 ? "s" : "", (int)previous, previous > 1 ? "es" : ""); 384 } 385 #endif 386 PetscCall(PetscOptionsEnum("-pc_hpddm_coarse_correction", "Type of coarse correction applied each iteration", "PCHPDDMSetCoarseCorrectionType", PCHPDDMCoarseCorrectionTypes, (PetscEnum)data->correction, (PetscEnum *)&type, &flg)); 387 if (flg) PetscCall(PCHPDDMSetCoarseCorrectionType(pc, type)); 388 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_has_neumann")); 389 PetscCall(PetscOptionsBool(prefix, "Is the auxiliary Mat the local Neumann matrix?", "PCHPDDMHasNeumannMat", PetscBool3ToBool(data->Neumann), &flg, &set)); 390 if (set) data->Neumann = PetscBoolToBool3(flg); 391 data->log_separate = PETSC_FALSE; 392 if (PetscDefined(USE_LOG)) { 393 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_log_separate")); 394 PetscCall(PetscOptionsBool(prefix, "Log events level by level instead of inside PCSetUp()/KSPSolve()", nullptr, data->log_separate, &data->log_separate, nullptr)); 395 } 396 } 397 PetscOptionsHeadEnd(); 398 while (i < PETSC_PCHPDDM_MAXLEVELS && data->levels[i]) PetscCall(PetscFree(data->levels[i++])); 399 PetscFunctionReturn(PETSC_SUCCESS); 400 } 401 402 static PetscErrorCode PCApply_HPDDM(PC pc, Vec x, Vec y) 403 { 404 PC_HPDDM *data = (PC_HPDDM *)pc->data; 405 406 PetscFunctionBegin; 407 PetscCall(PetscCitationsRegister(HPDDMCitation, &HPDDMCite)); 408 PetscCheck(data->levels[0]->ksp, PETSC_COMM_SELF, PETSC_ERR_PLIB, "No KSP attached to PCHPDDM"); 409 if (data->log_separate) PetscCall(PetscLogEventBegin(PC_HPDDM_Solve[0], data->levels[0]->ksp, nullptr, nullptr, nullptr)); /* coarser-level events are directly triggered in HPDDM */ 410 PetscCall(KSPSolve(data->levels[0]->ksp, x, y)); 411 if (data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_Solve[0], data->levels[0]->ksp, nullptr, nullptr, nullptr)); 412 PetscFunctionReturn(PETSC_SUCCESS); 413 } 414 415 static PetscErrorCode PCMatApply_HPDDM(PC pc, Mat X, Mat Y) 416 { 417 PC_HPDDM *data = (PC_HPDDM *)pc->data; 418 419 PetscFunctionBegin; 420 PetscCall(PetscCitationsRegister(HPDDMCitation, &HPDDMCite)); 421 PetscCheck(data->levels[0]->ksp, PETSC_COMM_SELF, PETSC_ERR_PLIB, "No KSP attached to PCHPDDM"); 422 PetscCall(KSPMatSolve(data->levels[0]->ksp, X, Y)); 423 PetscFunctionReturn(PETSC_SUCCESS); 424 } 425 426 // PetscClangLinter pragma disable: -fdoc-internal-linkage 427 /*@C 428 PCHPDDMGetComplexities - Computes the grid and operator complexities. 429 430 Input Parameter: 431 . pc - preconditioner context 432 433 Output Parameters: 434 + gc - grid complexity = sum_i(m_i) / m_1 435 - oc - operator complexity = sum_i(nnz_i) / nnz_1 436 437 Level: advanced 438 439 .seealso: `PCMGGetGridComplexity()`, `PCHPDDM`, `PCHYPRE`, `PCGAMG` 440 @*/ 441 static PetscErrorCode PCHPDDMGetComplexities(PC pc, PetscReal *gc, PetscReal *oc) 442 { 443 PC_HPDDM *data = (PC_HPDDM *)pc->data; 444 MatInfo info; 445 PetscInt n, m; 446 PetscLogDouble accumulate[2]{}, nnz1 = 1.0, m1 = 1.0; 447 448 PetscFunctionBegin; 449 for (n = 0, *gc = 0, *oc = 0; n < data->N; ++n) { 450 if (data->levels[n]->ksp) { 451 Mat P, A = nullptr; 452 PetscBool flg = PETSC_FALSE; 453 454 PetscCall(KSPGetOperators(data->levels[n]->ksp, nullptr, &P)); 455 PetscCall(MatGetSize(P, &m, nullptr)); 456 accumulate[0] += m; 457 if (n == 0) { 458 PetscCall(PetscObjectTypeCompareAny((PetscObject)P, &flg, MATNORMAL, MATNORMALHERMITIAN, "")); 459 if (flg) { 460 PetscCall(MatConvert(P, MATAIJ, MAT_INITIAL_MATRIX, &A)); 461 P = A; 462 } else { 463 PetscCall(PetscObjectTypeCompare((PetscObject)P, MATSCHURCOMPLEMENT, &flg)); 464 PetscCall(PetscObjectReference((PetscObject)P)); 465 } 466 } 467 if (!A && flg) accumulate[1] += m * m; /* assumption that a MATSCHURCOMPLEMENT is dense if stored explicitly */ 468 else if (P->ops->getinfo) { 469 PetscCall(MatGetInfo(P, MAT_GLOBAL_SUM, &info)); 470 accumulate[1] += info.nz_used; 471 } 472 if (n == 0) { 473 m1 = m; 474 if (!A && flg) nnz1 = m * m; 475 else if (P->ops->getinfo) nnz1 = info.nz_used; 476 PetscCall(MatDestroy(&P)); 477 } 478 } 479 } 480 *gc = static_cast<PetscReal>(accumulate[0] / m1); 481 *oc = static_cast<PetscReal>(accumulate[1] / nnz1); 482 PetscFunctionReturn(PETSC_SUCCESS); 483 } 484 485 static PetscErrorCode PCView_HPDDM(PC pc, PetscViewer viewer) 486 { 487 PC_HPDDM *data = (PC_HPDDM *)pc->data; 488 PetscViewer subviewer; 489 PetscViewerFormat format; 490 PetscSubcomm subcomm; 491 PetscReal oc, gc; 492 PetscInt i, tabs; 493 PetscMPIInt size, color, rank; 494 PetscBool flg; 495 const char *name; 496 497 PetscFunctionBegin; 498 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &flg)); 499 if (flg) { 500 PetscCall(PetscViewerASCIIPrintf(viewer, "level%s: %" PetscInt_FMT "\n", data->N > 1 ? "s" : "", data->N)); 501 PetscCall(PCHPDDMGetComplexities(pc, &gc, &oc)); 502 if (data->N > 1) { 503 if (!data->deflation) { 504 PetscCall(PetscViewerASCIIPrintf(viewer, "Neumann matrix attached? %s\n", PetscBools[PetscBool3ToBool(data->Neumann)])); 505 PetscCall(PetscViewerASCIIPrintf(viewer, "shared subdomain KSP between SLEPc and PETSc? %s\n", PetscBools[data->share])); 506 } else PetscCall(PetscViewerASCIIPrintf(viewer, "user-supplied deflation matrix\n")); 507 PetscCall(PetscViewerASCIIPrintf(viewer, "coarse correction: %s\n", PCHPDDMCoarseCorrectionTypes[data->correction])); 508 PetscCall(PetscViewerASCIIPrintf(viewer, "on process #0, value%s (+ threshold%s if available) for selecting deflation vectors:", data->N > 2 ? "s" : "", data->N > 2 ? "s" : "")); 509 PetscCall(PetscViewerASCIIGetTab(viewer, &tabs)); 510 PetscCall(PetscViewerASCIISetTab(viewer, 0)); 511 for (i = 1; i < data->N; ++i) { 512 PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, data->levels[i - 1]->nu)); 513 if (data->levels[i - 1]->threshold > static_cast<PetscReal>(-0.1)) PetscCall(PetscViewerASCIIPrintf(viewer, " (%g)", (double)data->levels[i - 1]->threshold)); 514 } 515 PetscCall(PetscViewerASCIIPrintf(viewer, "\n")); 516 PetscCall(PetscViewerASCIISetTab(viewer, tabs)); 517 } 518 PetscCall(PetscViewerASCIIPrintf(viewer, "grid and operator complexities: %g %g\n", (double)gc, (double)oc)); 519 if (data->levels[0]->ksp) { 520 PetscCall(KSPView(data->levels[0]->ksp, viewer)); 521 if (data->levels[0]->pc) PetscCall(PCView(data->levels[0]->pc, viewer)); 522 for (i = 1; i < data->N; ++i) { 523 if (data->levels[i]->ksp) color = 1; 524 else color = 0; 525 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size)); 526 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)pc), &rank)); 527 PetscCall(PetscSubcommCreate(PetscObjectComm((PetscObject)pc), &subcomm)); 528 PetscCall(PetscSubcommSetNumber(subcomm, PetscMin(size, 2))); 529 PetscCall(PetscSubcommSetTypeGeneral(subcomm, color, rank)); 530 PetscCall(PetscViewerASCIIPushTab(viewer)); 531 PetscCall(PetscViewerGetSubViewer(viewer, PetscSubcommChild(subcomm), &subviewer)); 532 if (color == 1) { 533 PetscCall(KSPView(data->levels[i]->ksp, subviewer)); 534 if (data->levels[i]->pc) PetscCall(PCView(data->levels[i]->pc, subviewer)); 535 PetscCall(PetscViewerFlush(subviewer)); 536 } 537 PetscCall(PetscViewerRestoreSubViewer(viewer, PetscSubcommChild(subcomm), &subviewer)); 538 PetscCall(PetscViewerASCIIPopTab(viewer)); 539 PetscCall(PetscSubcommDestroy(&subcomm)); 540 PetscCall(PetscViewerFlush(viewer)); 541 } 542 } 543 PetscCall(PetscViewerGetFormat(viewer, &format)); 544 if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) { 545 PetscCall(PetscViewerFileGetName(viewer, &name)); 546 if (name) { 547 IS is; 548 PetscInt sizes[4] = {pc->mat->rmap->n, pc->mat->cmap->n, pc->mat->rmap->N, pc->mat->cmap->N}; 549 char *tmp; 550 std::string prefix, suffix; 551 size_t pos; 552 553 PetscCall(PetscStrstr(name, ".", &tmp)); 554 if (tmp) { 555 pos = std::distance(const_cast<char *>(name), tmp); 556 prefix = std::string(name, pos); 557 suffix = std::string(name + pos + 1); 558 } else prefix = name; 559 if (data->aux) { 560 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_SELF, std::string(prefix + "_aux_" + std::to_string(rank) + "_" + std::to_string(size) + (tmp ? ("." + suffix) : "")).c_str(), FILE_MODE_WRITE, &subviewer)); 561 PetscCall(MatView(data->aux, subviewer)); 562 PetscCall(PetscViewerDestroy(&subviewer)); 563 } 564 if (data->is) { 565 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_SELF, std::string(prefix + "_is_" + std::to_string(rank) + "_" + std::to_string(size) + (tmp ? ("." + suffix) : "")).c_str(), FILE_MODE_WRITE, &subviewer)); 566 PetscCall(ISView(data->is, subviewer)); 567 PetscCall(PetscViewerDestroy(&subviewer)); 568 } 569 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, PETSC_STATIC_ARRAY_LENGTH(sizes), sizes, PETSC_USE_POINTER, &is)); 570 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_SELF, std::string(prefix + "_sizes_" + std::to_string(rank) + "_" + std::to_string(size) + (tmp ? ("." + suffix) : "")).c_str(), FILE_MODE_WRITE, &subviewer)); 571 PetscCall(ISView(is, subviewer)); 572 PetscCall(PetscViewerDestroy(&subviewer)); 573 PetscCall(ISDestroy(&is)); 574 } 575 } 576 } 577 PetscFunctionReturn(PETSC_SUCCESS); 578 } 579 580 static PetscErrorCode PCPreSolve_HPDDM(PC pc, KSP ksp, Vec, Vec) 581 { 582 PC_HPDDM *data = (PC_HPDDM *)pc->data; 583 PetscBool flg; 584 Mat A; 585 586 PetscFunctionBegin; 587 if (ksp) { 588 PetscCall(PetscObjectTypeCompare((PetscObject)ksp, KSPLSQR, &flg)); 589 if (flg && !data->normal) { 590 PetscCall(KSPGetOperators(ksp, &A, nullptr)); 591 PetscCall(MatCreateVecs(A, nullptr, &data->normal)); /* temporary Vec used in PCApply_HPDDMShell() for coarse grid corrections */ 592 } 593 } 594 PetscFunctionReturn(PETSC_SUCCESS); 595 } 596 597 static PetscErrorCode PCSetUp_HPDDMShell(PC pc) 598 { 599 PC_HPDDM_Level *ctx; 600 Mat A, P; 601 Vec x; 602 const char *pcpre; 603 604 PetscFunctionBegin; 605 PetscCall(PCShellGetContext(pc, &ctx)); 606 PetscCall(KSPGetOptionsPrefix(ctx->ksp, &pcpre)); 607 PetscCall(KSPGetOperators(ctx->ksp, &A, &P)); 608 /* smoother */ 609 PetscCall(PCSetOptionsPrefix(ctx->pc, pcpre)); 610 PetscCall(PCSetOperators(ctx->pc, A, P)); 611 if (!ctx->v[0]) { 612 PetscCall(VecDuplicateVecs(ctx->D, 1, &ctx->v[0])); 613 if (!std::is_same<PetscScalar, PetscReal>::value) PetscCall(VecDestroy(&ctx->D)); 614 PetscCall(MatCreateVecs(A, &x, nullptr)); 615 PetscCall(VecDuplicateVecs(x, 2, &ctx->v[1])); 616 PetscCall(VecDestroy(&x)); 617 } 618 std::fill_n(ctx->V, 3, nullptr); 619 PetscFunctionReturn(PETSC_SUCCESS); 620 } 621 622 template <class Type, typename std::enable_if<std::is_same<Type, Vec>::value>::type * = nullptr> 623 static inline PetscErrorCode PCHPDDMDeflate_Private(PC pc, Type x, Type y) 624 { 625 PC_HPDDM_Level *ctx; 626 PetscScalar *out; 627 628 PetscFunctionBegin; 629 PetscCall(PCShellGetContext(pc, &ctx)); 630 /* going from PETSc to HPDDM numbering */ 631 PetscCall(VecScatterBegin(ctx->scatter, x, ctx->v[0][0], INSERT_VALUES, SCATTER_FORWARD)); 632 PetscCall(VecScatterEnd(ctx->scatter, x, ctx->v[0][0], INSERT_VALUES, SCATTER_FORWARD)); 633 PetscCall(VecGetArrayWrite(ctx->v[0][0], &out)); 634 ctx->P->deflation<false>(nullptr, out, 1); /* y = Q x */ 635 PetscCall(VecRestoreArrayWrite(ctx->v[0][0], &out)); 636 /* going from HPDDM to PETSc numbering */ 637 PetscCall(VecScatterBegin(ctx->scatter, ctx->v[0][0], y, INSERT_VALUES, SCATTER_REVERSE)); 638 PetscCall(VecScatterEnd(ctx->scatter, ctx->v[0][0], y, INSERT_VALUES, SCATTER_REVERSE)); 639 PetscFunctionReturn(PETSC_SUCCESS); 640 } 641 642 template <class Type, typename std::enable_if<std::is_same<Type, Mat>::value>::type * = nullptr> 643 static inline PetscErrorCode PCHPDDMDeflate_Private(PC pc, Type X, Type Y) 644 { 645 PC_HPDDM_Level *ctx; 646 Vec vX, vY, vC; 647 PetscScalar *out; 648 PetscInt i, N; 649 650 PetscFunctionBegin; 651 PetscCall(PCShellGetContext(pc, &ctx)); 652 PetscCall(MatGetSize(X, nullptr, &N)); 653 /* going from PETSc to HPDDM numbering */ 654 for (i = 0; i < N; ++i) { 655 PetscCall(MatDenseGetColumnVecRead(X, i, &vX)); 656 PetscCall(MatDenseGetColumnVecWrite(ctx->V[0], i, &vC)); 657 PetscCall(VecScatterBegin(ctx->scatter, vX, vC, INSERT_VALUES, SCATTER_FORWARD)); 658 PetscCall(VecScatterEnd(ctx->scatter, vX, vC, INSERT_VALUES, SCATTER_FORWARD)); 659 PetscCall(MatDenseRestoreColumnVecWrite(ctx->V[0], i, &vC)); 660 PetscCall(MatDenseRestoreColumnVecRead(X, i, &vX)); 661 } 662 PetscCall(MatDenseGetArrayWrite(ctx->V[0], &out)); 663 ctx->P->deflation<false>(nullptr, out, N); /* Y = Q X */ 664 PetscCall(MatDenseRestoreArrayWrite(ctx->V[0], &out)); 665 /* going from HPDDM to PETSc numbering */ 666 for (i = 0; i < N; ++i) { 667 PetscCall(MatDenseGetColumnVecRead(ctx->V[0], i, &vC)); 668 PetscCall(MatDenseGetColumnVecWrite(Y, i, &vY)); 669 PetscCall(VecScatterBegin(ctx->scatter, vC, vY, INSERT_VALUES, SCATTER_REVERSE)); 670 PetscCall(VecScatterEnd(ctx->scatter, vC, vY, INSERT_VALUES, SCATTER_REVERSE)); 671 PetscCall(MatDenseRestoreColumnVecWrite(Y, i, &vY)); 672 PetscCall(MatDenseRestoreColumnVecRead(ctx->V[0], i, &vC)); 673 } 674 PetscFunctionReturn(PETSC_SUCCESS); 675 } 676 677 /* 678 PCApply_HPDDMShell - Applies a (2) deflated, (1) additive, or (3) balanced coarse correction. In what follows, E = Z Pmat Z^T and Q = Z^T E^-1 Z. 679 680 .vb 681 (1) y = Pmat^-1 x + Q x, 682 (2) y = Pmat^-1 (I - Amat Q) x + Q x (default), 683 (3) y = (I - Q Amat^T) Pmat^-1 (I - Amat Q) x + Q x. 684 .ve 685 686 Input Parameters: 687 + pc - preconditioner context 688 - x - input vector 689 690 Output Parameter: 691 . y - output vector 692 693 Notes: 694 The options of Pmat^1 = pc(Pmat) are prefixed by -pc_hpddm_levels_1_pc_. Z is a tall-and-skiny matrix assembled by HPDDM. The number of processes on which (Z Pmat Z^T) is aggregated is set via -pc_hpddm_coarse_p. 695 The options of (Z Pmat Z^T)^-1 = ksp(Z Pmat Z^T) are prefixed by -pc_hpddm_coarse_ (`KSPPREONLY` and `PCCHOLESKY` by default), unless a multilevel correction is turned on, in which case, this function is called recursively at each level except the coarsest one. 696 (1) and (2) visit the "next" level (in terms of coarsening) once per application, while (3) visits it twice, so it is asymptotically twice costlier. (2) is not symmetric even if both Amat and Pmat are symmetric. 697 698 Level: advanced 699 700 Developer Note: 701 Since this is not an actual manual page the material below should be moved to an appropriate manual page with the appropriate context, i.e. explaining when it is used and how 702 to trigger it. Likely the manual page is `PCHPDDM` 703 704 .seealso: `PCHPDDM`, `PCHPDDMCoarseCorrectionType` 705 */ 706 static PetscErrorCode PCApply_HPDDMShell(PC pc, Vec x, Vec y) 707 { 708 PC_HPDDM_Level *ctx; 709 Mat A; 710 711 PetscFunctionBegin; 712 PetscCall(PCShellGetContext(pc, &ctx)); 713 PetscCheck(ctx->P, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with no HPDDM object"); 714 PetscCall(KSPGetOperators(ctx->ksp, &A, nullptr)); 715 PetscCall(PCHPDDMDeflate_Private(pc, x, y)); /* y = Q x */ 716 if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED || ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) { 717 if (!ctx->parent->normal || ctx != ctx->parent->levels[0]) PetscCall(MatMult(A, y, ctx->v[1][0])); /* y = A Q x */ 718 else { /* KSPLSQR and finest level */ PetscCall(MatMult(A, y, ctx->parent->normal)); /* y = A Q x */ 719 PetscCall(MatMultHermitianTranspose(A, ctx->parent->normal, ctx->v[1][0])); /* y = A^T A Q x */ 720 } 721 PetscCall(VecWAXPY(ctx->v[1][1], -1.0, ctx->v[1][0], x)); /* y = (I - A Q) x */ 722 PetscCall(PCApply(ctx->pc, ctx->v[1][1], ctx->v[1][0])); /* y = M^-1 (I - A Q) x */ 723 if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) { 724 if (!ctx->parent->normal || ctx != ctx->parent->levels[0]) PetscCall(MatMultHermitianTranspose(A, ctx->v[1][0], ctx->v[1][1])); /* z = A^T y */ 725 else { 726 PetscCall(MatMult(A, ctx->v[1][0], ctx->parent->normal)); 727 PetscCall(MatMultHermitianTranspose(A, ctx->parent->normal, ctx->v[1][1])); /* z = A^T A y */ 728 } 729 PetscCall(PCHPDDMDeflate_Private(pc, ctx->v[1][1], ctx->v[1][1])); 730 PetscCall(VecAXPBYPCZ(y, -1.0, 1.0, 1.0, ctx->v[1][1], ctx->v[1][0])); /* y = (I - Q A^T) y + Q x */ 731 } else PetscCall(VecAXPY(y, 1.0, ctx->v[1][0])); /* y = Q M^-1 (I - A Q) x + Q x */ 732 } else { 733 PetscCheck(ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_ADDITIVE, PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with an unknown PCHPDDMCoarseCorrectionType %d", ctx->parent->correction); 734 PetscCall(PCApply(ctx->pc, x, ctx->v[1][0])); 735 PetscCall(VecAXPY(y, 1.0, ctx->v[1][0])); /* y = M^-1 x + Q x */ 736 } 737 PetscFunctionReturn(PETSC_SUCCESS); 738 } 739 740 /* 741 PCMatApply_HPDDMShell - Variant of PCApply_HPDDMShell() for blocks of vectors. 742 743 Input Parameters: 744 + pc - preconditioner context 745 - X - block of input vectors 746 747 Output Parameter: 748 . Y - block of output vectors 749 750 Level: advanced 751 752 .seealso: `PCHPDDM`, `PCApply_HPDDMShell()`, `PCHPDDMCoarseCorrectionType` 753 */ 754 static PetscErrorCode PCMatApply_HPDDMShell(PC pc, Mat X, Mat Y) 755 { 756 PC_HPDDM_Level *ctx; 757 Mat A, *ptr; 758 PetscContainer container = nullptr; 759 PetscScalar *array; 760 PetscInt m, M, N, prev = 0; 761 PetscBool reset = PETSC_FALSE; 762 763 PetscFunctionBegin; 764 PetscCall(PCShellGetContext(pc, &ctx)); 765 PetscCheck(ctx->P, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with no HPDDM object"); 766 PetscCall(MatGetSize(X, nullptr, &N)); 767 PetscCall(KSPGetOperators(ctx->ksp, &A, nullptr)); 768 PetscCall(PetscObjectQuery((PetscObject)A, "_HPDDM_MatProduct", (PetscObject *)&container)); 769 if (container) { /* MatProduct container already attached */ 770 PetscCall(PetscContainerGetPointer(container, (void **)&ptr)); 771 if (ptr[1] != ctx->V[2]) /* Mat has changed or may have been set first in KSPHPDDM */ 772 for (m = 0; m < 2; ++m) { 773 PetscCall(MatDestroy(ctx->V + m + 1)); 774 ctx->V[m + 1] = ptr[m]; 775 PetscCall(PetscObjectReference((PetscObject)ctx->V[m + 1])); 776 } 777 } 778 if (ctx->V[1]) PetscCall(MatGetSize(ctx->V[1], nullptr, &prev)); 779 if (N != prev || !ctx->V[0]) { 780 PetscCall(MatDestroy(ctx->V)); 781 PetscCall(VecGetLocalSize(ctx->v[0][0], &m)); 782 PetscCall(MatCreateDense(PetscObjectComm((PetscObject)pc), m, PETSC_DECIDE, PETSC_DECIDE, N, nullptr, ctx->V)); 783 if (N != prev) { 784 PetscCall(MatDestroy(ctx->V + 1)); 785 PetscCall(MatDestroy(ctx->V + 2)); 786 PetscCall(MatGetLocalSize(X, &m, nullptr)); 787 PetscCall(MatGetSize(X, &M, nullptr)); 788 if (ctx->parent->correction != PC_HPDDM_COARSE_CORRECTION_BALANCED) PetscCall(MatDenseGetArrayWrite(ctx->V[0], &array)); 789 else array = nullptr; 790 PetscCall(MatCreateDense(PetscObjectComm((PetscObject)pc), m, PETSC_DECIDE, M, N, array, ctx->V + 1)); 791 if (ctx->parent->correction != PC_HPDDM_COARSE_CORRECTION_BALANCED) PetscCall(MatDenseRestoreArrayWrite(ctx->V[0], &array)); 792 PetscCall(MatCreateDense(PetscObjectComm((PetscObject)pc), m, PETSC_DECIDE, M, N, nullptr, ctx->V + 2)); 793 PetscCall(MatProductCreateWithMat(A, Y, nullptr, ctx->V[1])); 794 PetscCall(MatProductSetType(ctx->V[1], MATPRODUCT_AB)); 795 PetscCall(MatProductSetFromOptions(ctx->V[1])); 796 PetscCall(MatProductSymbolic(ctx->V[1])); 797 if (!container) { /* no MatProduct container attached, create one to be queried in KSPHPDDM or at the next call to PCMatApply() */ 798 PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)A), &container)); 799 PetscCall(PetscObjectCompose((PetscObject)A, "_HPDDM_MatProduct", (PetscObject)container)); 800 } 801 PetscCall(PetscContainerSetPointer(container, ctx->V + 1)); /* need to compose B and D from MatProductCreateWithMath(A, B, NULL, D), which are stored in the contiguous array ctx->V */ 802 } 803 if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) { 804 PetscCall(MatProductCreateWithMat(A, ctx->V[1], nullptr, ctx->V[2])); 805 PetscCall(MatProductSetType(ctx->V[2], MATPRODUCT_AtB)); 806 PetscCall(MatProductSetFromOptions(ctx->V[2])); 807 PetscCall(MatProductSymbolic(ctx->V[2])); 808 } 809 ctx->P->start(N); 810 } 811 if (N == prev || container) { /* when MatProduct container is attached, always need to MatProductReplaceMats() since KSPHPDDM may have replaced the Mat as well */ 812 PetscCall(MatProductReplaceMats(nullptr, Y, nullptr, ctx->V[1])); 813 if (container && ctx->parent->correction != PC_HPDDM_COARSE_CORRECTION_BALANCED) { 814 PetscCall(MatDenseGetArrayWrite(ctx->V[0], &array)); 815 PetscCall(MatDensePlaceArray(ctx->V[1], array)); 816 PetscCall(MatDenseRestoreArrayWrite(ctx->V[0], &array)); 817 reset = PETSC_TRUE; 818 } 819 } 820 PetscCall(PCHPDDMDeflate_Private(pc, X, Y)); 821 if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED || ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) { 822 PetscCall(MatProductNumeric(ctx->V[1])); 823 PetscCall(MatCopy(ctx->V[1], ctx->V[2], SAME_NONZERO_PATTERN)); 824 PetscCall(MatAXPY(ctx->V[2], -1.0, X, SAME_NONZERO_PATTERN)); 825 PetscCall(PCMatApply(ctx->pc, ctx->V[2], ctx->V[1])); 826 if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) { 827 PetscCall(MatProductNumeric(ctx->V[2])); 828 PetscCall(PCHPDDMDeflate_Private(pc, ctx->V[2], ctx->V[2])); 829 PetscCall(MatAXPY(ctx->V[1], -1.0, ctx->V[2], SAME_NONZERO_PATTERN)); 830 } 831 PetscCall(MatAXPY(Y, -1.0, ctx->V[1], SAME_NONZERO_PATTERN)); 832 } else { 833 PetscCheck(ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_ADDITIVE, PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with an unknown PCHPDDMCoarseCorrectionType %d", ctx->parent->correction); 834 PetscCall(PCMatApply(ctx->pc, X, ctx->V[1])); 835 PetscCall(MatAXPY(Y, 1.0, ctx->V[1], SAME_NONZERO_PATTERN)); 836 } 837 if (reset) PetscCall(MatDenseResetArray(ctx->V[1])); 838 PetscFunctionReturn(PETSC_SUCCESS); 839 } 840 841 static PetscErrorCode PCDestroy_HPDDMShell(PC pc) 842 { 843 PC_HPDDM_Level *ctx; 844 PetscContainer container; 845 846 PetscFunctionBegin; 847 PetscCall(PCShellGetContext(pc, &ctx)); 848 PetscCall(HPDDM::Schwarz<PetscScalar>::destroy(ctx, PETSC_TRUE)); 849 PetscCall(VecDestroyVecs(1, &ctx->v[0])); 850 PetscCall(VecDestroyVecs(2, &ctx->v[1])); 851 PetscCall(PetscObjectQuery((PetscObject)(ctx->pc)->mat, "_HPDDM_MatProduct", (PetscObject *)&container)); 852 PetscCall(PetscContainerDestroy(&container)); 853 PetscCall(PetscObjectCompose((PetscObject)(ctx->pc)->mat, "_HPDDM_MatProduct", nullptr)); 854 PetscCall(MatDestroy(ctx->V)); 855 PetscCall(MatDestroy(ctx->V + 1)); 856 PetscCall(MatDestroy(ctx->V + 2)); 857 PetscCall(VecDestroy(&ctx->D)); 858 PetscCall(VecScatterDestroy(&ctx->scatter)); 859 PetscCall(PCDestroy(&ctx->pc)); 860 PetscFunctionReturn(PETSC_SUCCESS); 861 } 862 863 template <class Type, bool T = false, typename std::enable_if<std::is_same<Type, Vec>::value>::type * = nullptr> 864 static inline PetscErrorCode PCApply_Schur_Private(std::tuple<KSP, IS, Vec[2]> *p, PC factor, Type x, Type y) 865 { 866 PetscFunctionBegin; 867 PetscCall(VecISCopy(std::get<2>(*p)[0], std::get<1>(*p), SCATTER_FORWARD, x)); 868 if (!T) PetscCall(PCApply(factor, std::get<2>(*p)[0], std::get<2>(*p)[1])); 869 else PetscCall(PCApplyTranspose(factor, std::get<2>(*p)[0], std::get<2>(*p)[1])); 870 PetscCall(VecISCopy(std::get<2>(*p)[1], std::get<1>(*p), SCATTER_REVERSE, y)); 871 PetscFunctionReturn(PETSC_SUCCESS); 872 } 873 874 template <class Type, bool = false, typename std::enable_if<std::is_same<Type, Mat>::value>::type * = nullptr> 875 static inline PetscErrorCode PCApply_Schur_Private(std::tuple<KSP, IS, Vec[2]> *p, PC factor, Type X, Type Y) 876 { 877 Mat B[2]; 878 Vec x, y; 879 880 PetscFunctionBegin; 881 PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, factor->mat->rmap->n, X->cmap->n, nullptr, B)); 882 PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, factor->mat->rmap->n, X->cmap->n, nullptr, B + 1)); 883 for (PetscInt i = 0; i < X->cmap->n; ++i) { 884 PetscCall(MatDenseGetColumnVecRead(X, i, &x)); 885 PetscCall(MatDenseGetColumnVecWrite(B[0], i, &y)); 886 PetscCall(VecISCopy(y, std::get<1>(*p), SCATTER_FORWARD, x)); 887 PetscCall(MatDenseRestoreColumnVecWrite(B[0], i, &y)); 888 PetscCall(MatDenseRestoreColumnVecRead(X, i, &x)); 889 } 890 PetscCall(PCMatApply(factor, B[0], B[1])); 891 PetscCall(MatDestroy(B)); 892 for (PetscInt i = 0; i < X->cmap->n; ++i) { 893 PetscCall(MatDenseGetColumnVecRead(B[1], i, &x)); 894 PetscCall(MatDenseGetColumnVecWrite(Y, i, &y)); 895 PetscCall(VecISCopy(x, std::get<1>(*p), SCATTER_REVERSE, y)); 896 PetscCall(MatDenseRestoreColumnVecWrite(Y, i, &y)); 897 PetscCall(MatDenseRestoreColumnVecRead(B[1], i, &x)); 898 } 899 PetscCall(MatDestroy(B + 1)); 900 PetscFunctionReturn(PETSC_SUCCESS); 901 } 902 903 template <class Type = Vec, bool T = false> 904 static PetscErrorCode PCApply_Schur(PC pc, Type x, Type y) 905 { 906 PC factor; 907 Mat A; 908 MatSolverType type; 909 PetscBool flg; 910 std::tuple<KSP, IS, Vec[2]> *p; 911 912 PetscFunctionBegin; 913 PetscCall(PCShellGetContext(pc, &p)); 914 PetscCall(KSPGetPC(std::get<0>(*p), &factor)); 915 PetscCall(PCFactorGetMatSolverType(factor, &type)); 916 PetscCall(PCFactorGetMatrix(factor, &A)); 917 PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg)); 918 if (flg) { 919 PetscCheck(PetscDefined(HAVE_MUMPS), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Inconsistent MatSolverType"); 920 #if PetscDefined(HAVE_MUMPS) 921 PetscCall(MatMumpsSetIcntl(A, 26, 0)); 922 #endif 923 } else { 924 PetscCall(PetscStrcmp(type, MATSOLVERMKL_PARDISO, &flg)); 925 PetscCheck(flg && PetscDefined(HAVE_MKL_PARDISO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Inconsistent MatSolverType"); 926 flg = PETSC_FALSE; 927 #if PetscDefined(HAVE_MKL_PARDISO) 928 PetscCall(MatMkl_PardisoSetCntl(A, 70, 1)); 929 #endif 930 } 931 PetscCall(PCApply_Schur_Private<Type, T>(p, factor, x, y)); 932 if (flg) { 933 #if PetscDefined(HAVE_MUMPS) 934 PetscCall(MatMumpsSetIcntl(A, 26, -1)); 935 #endif 936 } else { 937 #if PetscDefined(HAVE_MKL_PARDISO) 938 PetscCall(MatMkl_PardisoSetCntl(A, 70, 0)); 939 #endif 940 } 941 PetscFunctionReturn(PETSC_SUCCESS); 942 } 943 944 static PetscErrorCode PCDestroy_Schur(PC pc) 945 { 946 std::tuple<KSP, IS, Vec[2]> *p; 947 948 PetscFunctionBegin; 949 PetscCall(PCShellGetContext(pc, &p)); 950 PetscCall(ISDestroy(&std::get<1>(*p))); 951 PetscCall(VecDestroy(std::get<2>(*p))); 952 PetscCall(VecDestroy(std::get<2>(*p) + 1)); 953 PetscCall(PetscFree(p)); 954 PetscFunctionReturn(PETSC_SUCCESS); 955 } 956 957 static PetscErrorCode PCHPDDMSolve_Private(const PC_HPDDM_Level *ctx, PetscScalar *rhs, const unsigned short &mu) 958 { 959 Mat B, X; 960 PetscInt n, N, j = 0; 961 962 PetscFunctionBegin; 963 PetscCall(KSPGetOperators(ctx->ksp, &B, nullptr)); 964 PetscCall(MatGetLocalSize(B, &n, nullptr)); 965 PetscCall(MatGetSize(B, &N, nullptr)); 966 if (ctx->parent->log_separate) { 967 j = std::distance(ctx->parent->levels, std::find(ctx->parent->levels, ctx->parent->levels + ctx->parent->N, ctx)); 968 PetscCall(PetscLogEventBegin(PC_HPDDM_Solve[j], ctx->ksp, nullptr, nullptr, nullptr)); 969 } 970 if (mu == 1) { 971 if (!ctx->ksp->vec_rhs) { 972 PetscCall(VecCreateMPIWithArray(PetscObjectComm((PetscObject)ctx->ksp), 1, n, N, nullptr, &ctx->ksp->vec_rhs)); 973 PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)ctx->ksp), n, N, &ctx->ksp->vec_sol)); 974 } 975 PetscCall(VecPlaceArray(ctx->ksp->vec_rhs, rhs)); 976 PetscCall(KSPSolve(ctx->ksp, nullptr, nullptr)); 977 PetscCall(VecCopy(ctx->ksp->vec_sol, ctx->ksp->vec_rhs)); 978 PetscCall(VecResetArray(ctx->ksp->vec_rhs)); 979 } else { 980 PetscCall(MatCreateDense(PetscObjectComm((PetscObject)ctx->ksp), n, PETSC_DECIDE, N, mu, rhs, &B)); 981 PetscCall(MatCreateDense(PetscObjectComm((PetscObject)ctx->ksp), n, PETSC_DECIDE, N, mu, nullptr, &X)); 982 PetscCall(KSPMatSolve(ctx->ksp, B, X)); 983 PetscCall(MatCopy(X, B, SAME_NONZERO_PATTERN)); 984 PetscCall(MatDestroy(&X)); 985 PetscCall(MatDestroy(&B)); 986 } 987 if (ctx->parent->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_Solve[j], ctx->ksp, nullptr, nullptr, nullptr)); 988 PetscFunctionReturn(PETSC_SUCCESS); 989 } 990 991 static PetscErrorCode PCHPDDMSetUpNeumannOverlap_Private(PC pc) 992 { 993 PC_HPDDM *data = (PC_HPDDM *)pc->data; 994 995 PetscFunctionBegin; 996 if (data->setup) { 997 Mat P; 998 Vec x, xt = nullptr; 999 PetscReal t = 0.0, s = 0.0; 1000 1001 PetscCall(PCGetOperators(pc, nullptr, &P)); 1002 PetscCall(PetscObjectQuery((PetscObject)P, "__SNES_latest_X", (PetscObject *)&x)); 1003 PetscCallBack("PCHPDDM Neumann callback", (*data->setup)(data->aux, t, x, xt, s, data->is, data->setup_ctx)); 1004 } 1005 PetscFunctionReturn(PETSC_SUCCESS); 1006 } 1007 1008 static PetscErrorCode PCHPDDMCreateSubMatrices_Private(Mat mat, PetscInt n, const IS *, const IS *, MatReuse scall, Mat *submat[]) 1009 { 1010 Mat A; 1011 PetscBool flg; 1012 1013 PetscFunctionBegin; 1014 PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "MatCreateSubMatrices() called to extract %" PetscInt_FMT " submatrices, which is different than 1", n); 1015 /* previously composed Mat */ 1016 PetscCall(PetscObjectQuery((PetscObject)mat, "_PCHPDDM_SubMatrices", (PetscObject *)&A)); 1017 PetscCheck(A, PETSC_COMM_SELF, PETSC_ERR_PLIB, "SubMatrices not found in Mat"); 1018 PetscCall(PetscObjectTypeCompare((PetscObject)A, MATSCHURCOMPLEMENT, &flg)); /* MATSCHURCOMPLEMENT has neither a MatDuplicate() nor a MatCopy() implementation */ 1019 if (scall == MAT_INITIAL_MATRIX) { 1020 PetscCall(PetscCalloc1(2, submat)); /* allocate an extra Mat to avoid errors in MatDestroySubMatrices_Dummy() */ 1021 if (!flg) PetscCall(MatDuplicate(A, MAT_COPY_VALUES, *submat)); 1022 } else if (!flg) PetscCall(MatCopy(A, (*submat)[0], SAME_NONZERO_PATTERN)); 1023 if (flg) { 1024 (*submat)[0] = A; 1025 PetscCall(PetscObjectReference((PetscObject)A)); 1026 } 1027 PetscFunctionReturn(PETSC_SUCCESS); 1028 } 1029 1030 static PetscErrorCode PCHPDDMCommunicationAvoidingPCASM_Private(PC pc, Mat C, PetscBool sorted) 1031 { 1032 void (*op)(void); 1033 1034 PetscFunctionBegin; 1035 /* previously-composed Mat */ 1036 PetscCall(PetscObjectCompose((PetscObject)pc->pmat, "_PCHPDDM_SubMatrices", (PetscObject)C)); 1037 PetscCall(MatGetOperation(pc->pmat, MATOP_CREATE_SUBMATRICES, &op)); 1038 /* trick suggested by Barry https://lists.mcs.anl.gov/pipermail/petsc-dev/2020-January/025491.html */ 1039 PetscCall(MatSetOperation(pc->pmat, MATOP_CREATE_SUBMATRICES, (void (*)(void))PCHPDDMCreateSubMatrices_Private)); 1040 if (sorted) PetscCall(PCASMSetSortIndices(pc, PETSC_FALSE)); /* everything is already sorted */ 1041 PetscCall(PCSetFromOptions(pc)); /* otherwise -pc_hpddm_levels_1_pc_asm_sub_mat_type is not used */ 1042 PetscCall(PCSetUp(pc)); 1043 /* reset MatCreateSubMatrices() */ 1044 PetscCall(MatSetOperation(pc->pmat, MATOP_CREATE_SUBMATRICES, op)); 1045 PetscCall(PetscObjectCompose((PetscObject)pc->pmat, "_PCHPDDM_SubMatrices", nullptr)); 1046 PetscFunctionReturn(PETSC_SUCCESS); 1047 } 1048 1049 static PetscErrorCode PCHPDDMPermute_Private(IS is, IS in_is, IS *out_is, Mat in_C, Mat *out_C, IS *p) 1050 { 1051 IS perm; 1052 const PetscInt *ptr; 1053 PetscInt *concatenate, size, n, bs; 1054 std::map<PetscInt, PetscInt> order; 1055 PetscBool sorted; 1056 1057 PetscFunctionBegin; 1058 PetscCall(ISSorted(is, &sorted)); 1059 if (!sorted) { 1060 PetscCall(ISGetLocalSize(is, &size)); 1061 PetscCall(ISGetIndices(is, &ptr)); 1062 PetscCall(ISGetBlockSize(is, &bs)); 1063 /* MatCreateSubMatrices(), called by PCASM, follows the global numbering of Pmat */ 1064 for (n = 0; n < size; n += bs) order.insert(std::make_pair(ptr[n] / bs, n / bs)); 1065 PetscCall(ISRestoreIndices(is, &ptr)); 1066 size /= bs; 1067 if (out_C) { 1068 PetscCall(PetscMalloc1(size, &concatenate)); 1069 for (const std::pair<const PetscInt, PetscInt> &i : order) *concatenate++ = i.second; 1070 concatenate -= size; 1071 PetscCall(ISCreateBlock(PetscObjectComm((PetscObject)in_C), bs, size, concatenate, PETSC_OWN_POINTER, &perm)); 1072 PetscCall(ISSetPermutation(perm)); 1073 /* permute user-provided Mat so that it matches with MatCreateSubMatrices() numbering */ 1074 PetscCall(MatPermute(in_C, perm, perm, out_C)); 1075 if (p) *p = perm; 1076 else PetscCall(ISDestroy(&perm)); /* no need to save the permutation */ 1077 } 1078 if (out_is) { 1079 PetscCall(PetscMalloc1(size, &concatenate)); 1080 for (const std::pair<const PetscInt, PetscInt> &i : order) *concatenate++ = i.first; 1081 concatenate -= size; 1082 /* permute user-provided IS so that it matches with MatCreateSubMatrices() numbering */ 1083 PetscCall(ISCreateBlock(PetscObjectComm((PetscObject)in_is), bs, size, concatenate, PETSC_OWN_POINTER, out_is)); 1084 } 1085 } else { /* input IS is sorted, nothing to permute, simply duplicate inputs when needed */ 1086 if (out_C) PetscCall(MatDuplicate(in_C, MAT_COPY_VALUES, out_C)); 1087 if (out_is) PetscCall(ISDuplicate(in_is, out_is)); 1088 } 1089 PetscFunctionReturn(PETSC_SUCCESS); 1090 } 1091 1092 static PetscErrorCode PCHPDDMCheckSymmetry_Private(PC pc, Mat A01, Mat A10) 1093 { 1094 Mat T, U = nullptr, B = nullptr; 1095 IS z; 1096 PetscBool flg[2]; 1097 1098 PetscFunctionBegin; 1099 PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATTRANSPOSEVIRTUAL, flg)); 1100 if (flg[0]) PetscCall(MatTransposeGetMat(A10, &U)); 1101 else { 1102 PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATHERMITIANTRANSPOSEVIRTUAL, flg + 1)); 1103 if (flg[1]) PetscCall(MatHermitianTransposeGetMat(A10, &U)); 1104 } 1105 if (U) PetscCall(MatDuplicate(U, MAT_COPY_VALUES, &T)); 1106 else PetscCall(MatHermitianTranspose(A10, MAT_INITIAL_MATRIX, &T)); 1107 PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATTRANSPOSEVIRTUAL, flg)); 1108 if (flg[0]) { 1109 PetscCall(MatTransposeGetMat(A01, &A01)); 1110 PetscCall(MatTranspose(A01, MAT_INITIAL_MATRIX, &B)); 1111 A01 = B; 1112 } else { 1113 PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATHERMITIANTRANSPOSEVIRTUAL, flg)); 1114 if (flg[0]) { 1115 PetscCall(MatHermitianTransposeGetMat(A01, &A01)); 1116 PetscCall(MatHermitianTranspose(A01, MAT_INITIAL_MATRIX, &B)); 1117 A01 = B; 1118 } 1119 } 1120 PetscCall(PetscLayoutCompare(T->rmap, A01->rmap, flg)); 1121 if (flg[0]) { 1122 PetscCall(PetscLayoutCompare(T->cmap, A01->cmap, flg)); 1123 if (flg[0]) { 1124 PetscCall(MatFindZeroRows(A01, &z)); /* for essential boundary conditions, some implementations will */ 1125 if (z) { /* zero rows in [P00 A01] except for the diagonal of P00 */ 1126 PetscCall(MatSetOption(T, MAT_NO_OFF_PROC_ZERO_ROWS, PETSC_TRUE)); 1127 PetscCall(MatZeroRowsIS(T, z, 0.0, nullptr, nullptr)); /* corresponding zero rows from A01 */ 1128 PetscCall(ISDestroy(&z)); 1129 } 1130 PetscCall(MatMultEqual(A01, T, 20, flg)); 1131 PetscCheck(flg[0], PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "A01 != A10^T"); 1132 } else PetscCall(PetscInfo(pc, "A01 and A10^T have non-congruent column layouts, cannot test for equality\n")); 1133 } 1134 PetscCall(MatDestroy(&B)); 1135 PetscCall(MatDestroy(&T)); 1136 PetscFunctionReturn(PETSC_SUCCESS); 1137 } 1138 1139 static PetscErrorCode PCHPDDMDestroySubMatrices_Private(PetscBool flg, PetscBool algebraic, Mat *sub) 1140 { 1141 IS is; 1142 1143 PetscFunctionBegin; 1144 if (!flg) { 1145 if (algebraic) { 1146 PetscCall(PetscObjectQuery((PetscObject)sub[0], "_PCHPDDM_Embed", (PetscObject *)&is)); 1147 PetscCall(ISDestroy(&is)); 1148 PetscCall(PetscObjectCompose((PetscObject)sub[0], "_PCHPDDM_Embed", nullptr)); 1149 PetscCall(PetscObjectCompose((PetscObject)sub[0], "_PCHPDDM_Compact", nullptr)); 1150 } 1151 PetscCall(MatDestroySubMatrices(algebraic ? 2 : 1, &sub)); 1152 } 1153 PetscFunctionReturn(PETSC_SUCCESS); 1154 } 1155 1156 static PetscErrorCode PCHPDDMAlgebraicAuxiliaryMat_Private(Mat P, IS *is, Mat *sub[], PetscBool block) 1157 { 1158 IS icol[3], irow[2]; 1159 Mat *M, Q; 1160 PetscReal *ptr; 1161 PetscInt *idx, p = 0, n, bs = PetscAbs(P->cmap->bs); 1162 PetscBool flg; 1163 1164 PetscFunctionBegin; 1165 PetscCall(ISCreateStride(PETSC_COMM_SELF, P->cmap->N, 0, 1, icol + 2)); 1166 PetscCall(ISSetBlockSize(icol[2], bs)); 1167 PetscCall(ISSetIdentity(icol[2])); 1168 PetscCall(PetscObjectTypeCompare((PetscObject)P, MATMPISBAIJ, &flg)); 1169 if (flg) { 1170 /* MatCreateSubMatrices() does not handle MATMPISBAIJ properly when iscol != isrow, so convert first to MATMPIBAIJ */ 1171 PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &Q)); 1172 std::swap(P, Q); 1173 } 1174 PetscCall(MatCreateSubMatrices(P, 1, is, icol + 2, MAT_INITIAL_MATRIX, &M)); 1175 if (flg) { 1176 std::swap(P, Q); 1177 PetscCall(MatDestroy(&Q)); 1178 } 1179 PetscCall(ISDestroy(icol + 2)); 1180 PetscCall(ISCreateStride(PETSC_COMM_SELF, M[0]->rmap->N, 0, 1, irow)); 1181 PetscCall(ISSetBlockSize(irow[0], bs)); 1182 PetscCall(ISSetIdentity(irow[0])); 1183 if (!block) { 1184 PetscCall(PetscMalloc2(P->cmap->N, &ptr, P->cmap->N / bs, &idx)); 1185 PetscCall(MatGetColumnNorms(M[0], NORM_INFINITY, ptr)); 1186 /* check for nonzero columns so that M[0] may be expressed in compact form */ 1187 for (n = 0; n < P->cmap->N; n += bs) { 1188 if (std::find_if(ptr + n, ptr + n + bs, [](PetscReal v) { return v > PETSC_MACHINE_EPSILON; }) != ptr + n + bs) idx[p++] = n / bs; 1189 } 1190 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, p, idx, PETSC_USE_POINTER, icol + 1)); 1191 PetscCall(ISSetInfo(icol[1], IS_SORTED, IS_GLOBAL, PETSC_TRUE, PETSC_TRUE)); 1192 PetscCall(ISEmbed(*is, icol[1], PETSC_FALSE, icol + 2)); 1193 irow[1] = irow[0]; 1194 /* first Mat will be used in PCASM (if it is used as a PC on this level) and as the left-hand side of GenEO */ 1195 icol[0] = is[0]; 1196 PetscCall(MatCreateSubMatrices(M[0], 2, irow, icol, MAT_INITIAL_MATRIX, sub)); 1197 PetscCall(ISDestroy(icol + 1)); 1198 PetscCall(PetscFree2(ptr, idx)); 1199 /* IS used to go back and forth between the augmented and the original local linear system, see eq. (3.4) of [2022b] */ 1200 PetscCall(PetscObjectCompose((PetscObject)(*sub)[0], "_PCHPDDM_Embed", (PetscObject)icol[2])); 1201 /* Mat used in eq. (3.1) of [2022b] */ 1202 PetscCall(PetscObjectCompose((PetscObject)(*sub)[0], "_PCHPDDM_Compact", (PetscObject)(*sub)[1])); 1203 } else { 1204 Mat aux; 1205 PetscCall(MatSetOption(M[0], MAT_SUBMAT_SINGLEIS, PETSC_TRUE)); 1206 /* diagonal block of the overlapping rows */ 1207 PetscCall(MatCreateSubMatrices(M[0], 1, irow, is, MAT_INITIAL_MATRIX, sub)); 1208 PetscCall(MatDuplicate((*sub)[0], MAT_COPY_VALUES, &aux)); 1209 PetscCall(MatSetOption(aux, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE)); 1210 if (bs == 1) { /* scalar case */ 1211 Vec sum[2]; 1212 PetscCall(MatCreateVecs(aux, sum, sum + 1)); 1213 PetscCall(MatGetRowSum(M[0], sum[0])); 1214 PetscCall(MatGetRowSum(aux, sum[1])); 1215 /* off-diagonal block row sum (full rows - diagonal block rows) */ 1216 PetscCall(VecAXPY(sum[0], -1.0, sum[1])); 1217 /* subdomain matrix plus off-diagonal block row sum */ 1218 PetscCall(MatDiagonalSet(aux, sum[0], ADD_VALUES)); 1219 PetscCall(VecDestroy(sum)); 1220 PetscCall(VecDestroy(sum + 1)); 1221 } else { /* vectorial case */ 1222 /* TODO: missing MatGetValuesBlocked(), so the code below is */ 1223 /* an extension of the scalar case for when bs > 1, but it could */ 1224 /* be more efficient by avoiding all these MatMatMult() */ 1225 Mat sum[2], ones; 1226 PetscScalar *ptr; 1227 PetscCall(PetscCalloc1(M[0]->cmap->n * bs, &ptr)); 1228 PetscCall(MatCreateDense(PETSC_COMM_SELF, M[0]->cmap->n, bs, M[0]->cmap->n, bs, ptr, &ones)); 1229 for (n = 0; n < M[0]->cmap->n; n += bs) { 1230 for (p = 0; p < bs; ++p) ptr[n + p * (M[0]->cmap->n + 1)] = 1.0; 1231 } 1232 PetscCall(MatMatMult(M[0], ones, MAT_INITIAL_MATRIX, PETSC_DEFAULT, sum)); 1233 PetscCall(MatDestroy(&ones)); 1234 PetscCall(MatCreateDense(PETSC_COMM_SELF, aux->cmap->n, bs, aux->cmap->n, bs, ptr, &ones)); 1235 PetscCall(MatDenseSetLDA(ones, M[0]->cmap->n)); 1236 PetscCall(MatMatMult(aux, ones, MAT_INITIAL_MATRIX, PETSC_DEFAULT, sum + 1)); 1237 PetscCall(MatDestroy(&ones)); 1238 PetscCall(PetscFree(ptr)); 1239 /* off-diagonal block row sum (full rows - diagonal block rows) */ 1240 PetscCall(MatAXPY(sum[0], -1.0, sum[1], SAME_NONZERO_PATTERN)); 1241 PetscCall(MatDestroy(sum + 1)); 1242 /* re-order values to be consistent with MatSetValuesBlocked() */ 1243 /* equivalent to MatTranspose() which does not truly handle */ 1244 /* MAT_INPLACE_MATRIX in the rectangular case, as it calls PetscMalloc() */ 1245 PetscCall(MatDenseGetArrayWrite(sum[0], &ptr)); 1246 HPDDM::Wrapper<PetscScalar>::imatcopy<'T'>(bs, sum[0]->rmap->n, ptr, sum[0]->rmap->n, bs); 1247 /* subdomain matrix plus off-diagonal block row sum */ 1248 for (n = 0; n < aux->cmap->n / bs; ++n) PetscCall(MatSetValuesBlocked(aux, 1, &n, 1, &n, ptr + n * bs * bs, ADD_VALUES)); 1249 PetscCall(MatAssemblyBegin(aux, MAT_FINAL_ASSEMBLY)); 1250 PetscCall(MatAssemblyEnd(aux, MAT_FINAL_ASSEMBLY)); 1251 PetscCall(MatDenseRestoreArrayWrite(sum[0], &ptr)); 1252 PetscCall(MatDestroy(sum)); 1253 } 1254 PetscCall(MatSetOption(aux, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE)); 1255 /* left-hand side of GenEO, with the same sparsity pattern as PCASM subdomain solvers */ 1256 PetscCall(PetscObjectCompose((PetscObject)(*sub)[0], "_PCHPDDM_Neumann_Mat", (PetscObject)aux)); 1257 } 1258 PetscCall(ISDestroy(irow)); 1259 PetscCall(MatDestroySubMatrices(1, &M)); 1260 PetscFunctionReturn(PETSC_SUCCESS); 1261 } 1262 1263 static PetscErrorCode PCApply_Nest(PC pc, Vec x, Vec y) 1264 { 1265 Mat A; 1266 MatSolverType type; 1267 IS is[2]; 1268 PetscBool flg; 1269 std::pair<PC, Vec[2]> *p; 1270 1271 PetscFunctionBegin; 1272 PetscCall(PCShellGetContext(pc, &p)); 1273 PetscCall(PCGetOperators(p->first, &A, nullptr)); 1274 PetscCall(MatNestGetISs(A, is, nullptr)); 1275 PetscCall(PCFactorGetMatSolverType(p->first, &type)); 1276 PetscCall(PCFactorGetMatrix(p->first, &A)); 1277 PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg)); 1278 if (flg && A->schur) { 1279 #if PetscDefined(HAVE_MUMPS) 1280 PetscCall(MatMumpsSetIcntl(A, 26, 1)); /* reduction/condensation phase followed by Schur complement solve */ 1281 #endif 1282 } 1283 PetscCall(VecISCopy(p->second[0], is[1], SCATTER_FORWARD, x)); /* assign the RHS associated to the Schur complement */ 1284 PetscCall(PCApply(p->first, p->second[0], p->second[1])); 1285 PetscCall(VecISCopy(p->second[1], is[1], SCATTER_REVERSE, y)); /* retrieve the partial solution associated to the Schur complement */ 1286 if (flg) { 1287 #if PetscDefined(HAVE_MUMPS) 1288 PetscCall(MatMumpsSetIcntl(A, 26, -1)); /* default ICNTL(26) value in PETSc */ 1289 #endif 1290 } 1291 PetscFunctionReturn(PETSC_SUCCESS); 1292 } 1293 1294 static PetscErrorCode PCView_Nest(PC pc, PetscViewer viewer) 1295 { 1296 std::pair<PC, Vec[2]> *p; 1297 1298 PetscFunctionBegin; 1299 PetscCall(PCShellGetContext(pc, &p)); 1300 PetscCall(PCView(p->first, viewer)); 1301 PetscFunctionReturn(PETSC_SUCCESS); 1302 } 1303 1304 static PetscErrorCode PCDestroy_Nest(PC pc) 1305 { 1306 std::pair<PC, Vec[2]> *p; 1307 1308 PetscFunctionBegin; 1309 PetscCall(PCShellGetContext(pc, &p)); 1310 PetscCall(VecDestroy(p->second)); 1311 PetscCall(VecDestroy(p->second + 1)); 1312 PetscCall(PCDestroy(&p->first)); 1313 PetscCall(PetscFree(p)); 1314 PetscFunctionReturn(PETSC_SUCCESS); 1315 } 1316 1317 template <bool T = false> 1318 static PetscErrorCode MatMult_Schur(Mat A, Vec x, Vec y) 1319 { 1320 std::tuple<Mat, VecScatter, Vec[2]> *ctx; 1321 1322 PetscFunctionBegin; 1323 PetscCall(MatShellGetContext(A, &ctx)); 1324 PetscCall(VecScatterBegin(std::get<1>(*ctx), x, std::get<2>(*ctx)[0], INSERT_VALUES, SCATTER_FORWARD)); /* local Vec with overlap */ 1325 PetscCall(VecScatterEnd(std::get<1>(*ctx), x, std::get<2>(*ctx)[0], INSERT_VALUES, SCATTER_FORWARD)); 1326 if (!T) PetscCall(MatMult(std::get<0>(*ctx), std::get<2>(*ctx)[0], std::get<2>(*ctx)[1])); /* local Schur complement */ 1327 else PetscCall(MatMultTranspose(std::get<0>(*ctx), std::get<2>(*ctx)[0], std::get<2>(*ctx)[1])); 1328 PetscCall(VecSet(y, 0.0)); 1329 PetscCall(VecScatterBegin(std::get<1>(*ctx), std::get<2>(*ctx)[1], y, ADD_VALUES, SCATTER_REVERSE)); /* global Vec with summed up contributions on the overlap */ 1330 PetscCall(VecScatterEnd(std::get<1>(*ctx), std::get<2>(*ctx)[1], y, ADD_VALUES, SCATTER_REVERSE)); 1331 PetscFunctionReturn(PETSC_SUCCESS); 1332 } 1333 1334 static PetscErrorCode MatDestroy_Schur(Mat A) 1335 { 1336 std::tuple<Mat, VecScatter, Vec[2]> *ctx; 1337 1338 PetscFunctionBegin; 1339 PetscCall(MatShellGetContext(A, &ctx)); 1340 PetscCall(VecDestroy(std::get<2>(*ctx))); 1341 PetscCall(VecDestroy(std::get<2>(*ctx) + 1)); 1342 PetscCall(PetscFree(ctx)); 1343 PetscFunctionReturn(PETSC_SUCCESS); 1344 } 1345 1346 static PetscErrorCode MatMult_SchurCorrection(Mat A, Vec x, Vec y) 1347 { 1348 PC pc; 1349 std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx; 1350 1351 PetscFunctionBegin; 1352 PetscCall(MatShellGetContext(A, &ctx)); 1353 pc = ((PC_HPDDM *)std::get<0>(*ctx)[0]->data)->levels[0]->ksp->pc; 1354 if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) { /* Q_0 is the coarse correction associated to the A00 block from PCFIELDSPLIT */ 1355 PetscCall(MatMult(std::get<1>(*ctx)[0], x, std::get<3>(*ctx)[1])); /* A_01 x */ 1356 PetscCall(PCHPDDMDeflate_Private(pc, std::get<3>(*ctx)[1], std::get<3>(*ctx)[1])); /* Q_0 A_01 x */ 1357 PetscCall(MatMult(std::get<1>(*ctx)[1], std::get<3>(*ctx)[1], std::get<3>(*ctx)[0])); /* A_10 Q_0 A_01 x */ 1358 PetscCall(PCApply(std::get<0>(*ctx)[1], std::get<3>(*ctx)[0], y)); /* y = M_S^-1 A_10 Q_0 A_01 x */ 1359 } else { 1360 PetscCall(PCApply(std::get<0>(*ctx)[1], x, std::get<3>(*ctx)[0])); /* M_S^-1 x */ 1361 PetscCall(MatMult(std::get<1>(*ctx)[0], std::get<3>(*ctx)[0], std::get<3>(*ctx)[1])); /* A_01 M_S^-1 x */ 1362 PetscCall(PCHPDDMDeflate_Private(pc, std::get<3>(*ctx)[1], std::get<3>(*ctx)[1])); /* Q_0 A_01 M_S^-1 x */ 1363 PetscCall(MatMult(std::get<1>(*ctx)[1], std::get<3>(*ctx)[1], y)); /* y = A_10 Q_0 A_01 M_S^-1 x */ 1364 } 1365 PetscCall(VecAXPY(y, -1.0, x)); /* y -= x, preconditioned eq. (24) of https://hal.science/hal-02343808v6/document (with a sign flip) */ 1366 PetscFunctionReturn(PETSC_SUCCESS); 1367 } 1368 1369 static PetscErrorCode MatView_SchurCorrection(Mat A, PetscViewer viewer) 1370 { 1371 PetscBool ascii; 1372 std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx; 1373 1374 PetscFunctionBegin; 1375 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &ascii)); 1376 if (ascii) { 1377 PetscCall(MatShellGetContext(A, &ctx)); 1378 PetscCall(PetscViewerASCIIPrintf(viewer, "action of %s\n", std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT ? "(I - M_S^-1 A_10 Q_0 A_01)" : "(I - A_10 Q_0 A_01 M_S^-1)")); 1379 PetscCall(PCView(std::get<0>(*ctx)[1], viewer)); /* no need to PCView(Q_0) since it will be done by PCFIELDSPLIT */ 1380 } 1381 PetscFunctionReturn(PETSC_SUCCESS); 1382 } 1383 1384 static PetscErrorCode MatDestroy_SchurCorrection(Mat A) 1385 { 1386 std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx; 1387 1388 PetscFunctionBegin; 1389 PetscCall(MatShellGetContext(A, &ctx)); 1390 PetscCall(VecDestroy(std::get<3>(*ctx))); 1391 PetscCall(VecDestroy(std::get<3>(*ctx) + 1)); 1392 PetscCall(VecDestroy(std::get<3>(*ctx) + 2)); 1393 PetscCall(PCDestroy(std::get<0>(*ctx) + 1)); 1394 PetscCall(PetscFree(ctx)); 1395 PetscFunctionReturn(PETSC_SUCCESS); 1396 } 1397 1398 static PetscErrorCode KSPPreSolve_SchurCorrection(KSP, Vec b, Vec, void *context) 1399 { 1400 std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx = reinterpret_cast<std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *>(context); 1401 1402 PetscFunctionBegin; 1403 if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) { 1404 PetscCall(PCApply(std::get<0>(*ctx)[1], b, std::get<3>(*ctx)[2])); 1405 std::swap(*b, *std::get<3>(*ctx)[2]); /* replace b by M^-1 b, but need to keep a copy of the original RHS, so swap it with the work Vec */ 1406 } 1407 PetscFunctionReturn(PETSC_SUCCESS); 1408 } 1409 1410 static PetscErrorCode KSPPostSolve_SchurCorrection(KSP, Vec b, Vec x, void *context) 1411 { 1412 std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx = reinterpret_cast<std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *>(context); 1413 1414 PetscFunctionBegin; 1415 if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) std::swap(*b, *std::get<3>(*ctx)[2]); /* put back the original RHS where it belongs */ 1416 else { 1417 PetscCall(PCApply(std::get<0>(*ctx)[1], x, std::get<3>(*ctx)[2])); 1418 PetscCall(VecCopy(std::get<3>(*ctx)[2], x)); /* replace x by M^-1 x */ 1419 } 1420 PetscFunctionReturn(PETSC_SUCCESS); 1421 } 1422 1423 static PetscErrorCode MatMult_Harmonic(Mat, Vec, Vec); 1424 static PetscErrorCode MatMultTranspose_Harmonic(Mat, Vec, Vec); 1425 static PetscErrorCode MatProduct_AB_Harmonic(Mat, Mat, Mat, void *); 1426 static PetscErrorCode MatDestroy_Harmonic(Mat); 1427 1428 static PetscErrorCode PCSetUp_HPDDM(PC pc) 1429 { 1430 PC_HPDDM *data = (PC_HPDDM *)pc->data; 1431 PC inner; 1432 KSP *ksp; 1433 Mat *sub, A, P, N, C = nullptr, uaux = nullptr, weighted, subA[2], S; 1434 Vec xin, v; 1435 std::vector<Vec> initial; 1436 IS is[1], loc, uis = data->is, unsorted = nullptr; 1437 ISLocalToGlobalMapping l2g; 1438 char prefix[256]; 1439 const char *pcpre; 1440 const PetscScalar *const *ev; 1441 PetscInt n, requested = data->N, reused = 0, overlap = -1; 1442 MatStructure structure = UNKNOWN_NONZERO_PATTERN; 1443 PetscBool subdomains = PETSC_FALSE, flg = PETSC_FALSE, ismatis, swap = PETSC_FALSE, algebraic = PETSC_FALSE, block = PETSC_FALSE; 1444 DM dm; 1445 std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx = nullptr; 1446 #if PetscDefined(USE_DEBUG) 1447 IS dis = nullptr; 1448 Mat daux = nullptr; 1449 #endif 1450 1451 PetscFunctionBegin; 1452 PetscCheck(data->levels && data->levels[0], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not a single level allocated"); 1453 PetscCall(PCGetOptionsPrefix(pc, &pcpre)); 1454 PetscCall(PCGetOperators(pc, &A, &P)); 1455 if (!data->levels[0]->ksp) { 1456 PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &data->levels[0]->ksp)); 1457 PetscCall(KSPSetNestLevel(data->levels[0]->ksp, pc->kspnestlevel)); 1458 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_%s_", pcpre ? pcpre : "", data->N > 1 ? "levels_1" : "coarse")); 1459 PetscCall(KSPSetOptionsPrefix(data->levels[0]->ksp, prefix)); 1460 PetscCall(KSPSetType(data->levels[0]->ksp, KSPPREONLY)); 1461 } else if (data->levels[0]->ksp->pc && data->levels[0]->ksp->pc->setupcalled == 1 && data->levels[0]->ksp->pc->reusepreconditioner) { 1462 /* if the fine-level PCSHELL exists, its setup has succeeded, and one wants to reuse it, */ 1463 /* then just propagate the appropriate flag to the coarser levels */ 1464 for (n = 0; n < PETSC_PCHPDDM_MAXLEVELS && data->levels[n]; ++n) { 1465 /* the following KSP and PC may be NULL for some processes, hence the check */ 1466 if (data->levels[n]->ksp) PetscCall(KSPSetReusePreconditioner(data->levels[n]->ksp, PETSC_TRUE)); 1467 if (data->levels[n]->pc) PetscCall(PCSetReusePreconditioner(data->levels[n]->pc, PETSC_TRUE)); 1468 } 1469 /* early bail out because there is nothing to do */ 1470 PetscFunctionReturn(PETSC_SUCCESS); 1471 } else { 1472 /* reset coarser levels */ 1473 for (n = 1; n < PETSC_PCHPDDM_MAXLEVELS && data->levels[n]; ++n) { 1474 if (data->levels[n]->ksp && data->levels[n]->ksp->pc && data->levels[n]->ksp->pc->setupcalled == 1 && data->levels[n]->ksp->pc->reusepreconditioner && n < data->N) { 1475 reused = data->N - n; 1476 break; 1477 } 1478 PetscCall(KSPDestroy(&data->levels[n]->ksp)); 1479 PetscCall(PCDestroy(&data->levels[n]->pc)); 1480 } 1481 /* check if some coarser levels are being reused */ 1482 PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &reused, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)pc))); 1483 const int *addr = data->levels[0]->P ? data->levels[0]->P->getAddrLocal() : &HPDDM::i__0; 1484 1485 if (addr != &HPDDM::i__0 && reused != data->N - 1) { 1486 /* reuse previously computed eigenvectors */ 1487 ev = data->levels[0]->P->getVectors(); 1488 if (ev) { 1489 initial.reserve(*addr); 1490 PetscCall(VecCreateSeqWithArray(PETSC_COMM_SELF, 1, data->levels[0]->P->getDof(), ev[0], &xin)); 1491 for (n = 0; n < *addr; ++n) { 1492 PetscCall(VecDuplicate(xin, &v)); 1493 PetscCall(VecPlaceArray(xin, ev[n])); 1494 PetscCall(VecCopy(xin, v)); 1495 initial.emplace_back(v); 1496 PetscCall(VecResetArray(xin)); 1497 } 1498 PetscCall(VecDestroy(&xin)); 1499 } 1500 } 1501 } 1502 data->N -= reused; 1503 PetscCall(KSPSetOperators(data->levels[0]->ksp, A, P)); 1504 1505 PetscCall(PetscObjectTypeCompare((PetscObject)P, MATIS, &ismatis)); 1506 if (!data->is && !ismatis) { 1507 PetscErrorCode (*create)(DM, IS *, Mat *, PetscErrorCode (**)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void **) = nullptr; 1508 PetscErrorCode (*usetup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *) = nullptr; 1509 void *uctx = nullptr; 1510 1511 /* first see if we can get the data from the DM */ 1512 PetscCall(MatGetDM(P, &dm)); 1513 if (!dm) PetscCall(MatGetDM(A, &dm)); 1514 if (!dm) PetscCall(PCGetDM(pc, &dm)); 1515 if (dm) { /* this is the hook for DMPLEX and DMDA for which the auxiliary Mat is the local Neumann matrix */ 1516 PetscCall(PetscObjectQueryFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", &create)); 1517 if (create) { 1518 PetscCall((*create)(dm, &uis, &uaux, &usetup, &uctx)); 1519 if (data->Neumann == PETSC_BOOL3_UNKNOWN) data->Neumann = PETSC_BOOL3_TRUE; /* set the value only if it was not already provided by the user */ 1520 } 1521 } 1522 if (!create) { 1523 if (!uis) { 1524 PetscCall(PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Neumann_IS", (PetscObject *)&uis)); 1525 PetscCall(PetscObjectReference((PetscObject)uis)); 1526 } 1527 if (!uaux) { 1528 PetscCall(PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Neumann_Mat", (PetscObject *)&uaux)); 1529 PetscCall(PetscObjectReference((PetscObject)uaux)); 1530 } 1531 /* look inside the Pmat instead of the PC, needed for MatSchurComplementComputeExplicitOperator() */ 1532 if (!uis) { 1533 PetscCall(PetscObjectQuery((PetscObject)P, "_PCHPDDM_Neumann_IS", (PetscObject *)&uis)); 1534 PetscCall(PetscObjectReference((PetscObject)uis)); 1535 } 1536 if (!uaux) { 1537 PetscCall(PetscObjectQuery((PetscObject)P, "_PCHPDDM_Neumann_Mat", (PetscObject *)&uaux)); 1538 PetscCall(PetscObjectReference((PetscObject)uaux)); 1539 } 1540 } 1541 PetscCall(PCHPDDMSetAuxiliaryMat(pc, uis, uaux, usetup, uctx)); 1542 PetscCall(MatDestroy(&uaux)); 1543 PetscCall(ISDestroy(&uis)); 1544 } 1545 1546 if (!ismatis) { 1547 PetscCall(PCHPDDMSetUpNeumannOverlap_Private(pc)); 1548 PetscCall(PetscOptionsGetBool(nullptr, pcpre, "-pc_hpddm_block_splitting", &block, nullptr)); 1549 PetscCall(PetscOptionsGetInt(nullptr, pcpre, "-pc_hpddm_harmonic_overlap", &overlap, nullptr)); 1550 PetscCall(PetscObjectTypeCompare((PetscObject)P, MATSCHURCOMPLEMENT, &flg)); 1551 if (data->is || (data->N > 1 && flg)) { 1552 if (block || overlap != -1) { 1553 PetscCall(ISDestroy(&data->is)); 1554 PetscCall(MatDestroy(&data->aux)); 1555 } else if (data->N > 1 && flg) { 1556 PCHPDDMSchurPreType type = PC_HPDDM_SCHUR_PRE_GENEO; 1557 1558 PetscCall(PetscOptionsGetEnum(nullptr, pcpre, "-pc_hpddm_schur_precondition", PCHPDDMSchurPreTypes, (PetscEnum *)&type, &flg)); 1559 if (type == PC_HPDDM_SCHUR_PRE_LEAST_SQUARES) { 1560 PetscCall(ISDestroy(&data->is)); /* destroy any previously user-set objects since they will be set automatically */ 1561 PetscCall(MatDestroy(&data->aux)); 1562 } else if (type == PC_HPDDM_SCHUR_PRE_GENEO) { 1563 PetscContainer container = nullptr; 1564 1565 PetscCall(PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Schur", (PetscObject *)&container)); 1566 if (!container) { /* first call to PCSetUp() on the PC associated to the Schur complement */ 1567 PC_HPDDM *data_00; 1568 KSP ksp, inner_ksp; 1569 PC pc_00; 1570 char *prefix; 1571 PetscReal norm; 1572 1573 PetscCall(MatSchurComplementGetKSP(P, &ksp)); 1574 PetscCall(KSPGetPC(ksp, &pc_00)); 1575 PetscCall(PetscObjectTypeCompare((PetscObject)pc_00, PCHPDDM, &flg)); 1576 PetscCheck(flg, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_type %s (!= %s)", pcpre ? pcpre : "", ((PetscObject)pc_00)->prefix ? ((PetscObject)pc_00)->prefix : "", 1577 ((PetscObject)pc_00)->type_name, PCHPDDM); 1578 data_00 = (PC_HPDDM *)pc_00->data; 1579 PetscCheck(data_00->N == 2, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and %" PetscInt_FMT " level%s instead of 2 for the A00 block -%s", pcpre ? pcpre : "", data_00->N, data_00->N > 1 ? "s" : "", 1580 ((PetscObject)pc_00)->prefix); 1581 PetscCall(PetscObjectTypeCompare((PetscObject)data_00->levels[0]->pc, PCASM, &flg)); 1582 PetscCheck(flg, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_type %s (!= %s)", pcpre ? pcpre : "", ((PetscObject)data_00->levels[0]->pc)->prefix, 1583 ((PetscObject)data_00->levels[0]->pc)->type_name, PCASM); 1584 PetscCheck(data->Neumann == PETSC_BOOL3_TRUE, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_hpddm_has_neumann != true", pcpre ? pcpre : "", pcpre ? pcpre : ""); 1585 if (PetscDefined(USE_DEBUG) || !data->is) { 1586 Mat A01, A10, B = nullptr, C = nullptr, *sub; 1587 1588 PetscCall(MatSchurComplementGetSubMatrices(P, &A, nullptr, &A01, &A10, nullptr)); 1589 PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATTRANSPOSEVIRTUAL, &flg)); 1590 if (flg) { 1591 PetscCall(MatTransposeGetMat(A10, &C)); 1592 PetscCall(MatTranspose(C, MAT_INITIAL_MATRIX, &B)); 1593 } else { 1594 PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATHERMITIANTRANSPOSEVIRTUAL, &flg)); 1595 if (flg) { 1596 PetscCall(MatHermitianTransposeGetMat(A10, &C)); 1597 PetscCall(MatHermitianTranspose(C, MAT_INITIAL_MATRIX, &B)); 1598 } 1599 } 1600 if (!B) { 1601 B = A10; 1602 PetscCall(PetscObjectReference((PetscObject)B)); 1603 } else if (!data->is) { 1604 PetscCall(PetscObjectTypeCompareAny((PetscObject)A01, &flg, MATTRANSPOSEVIRTUAL, MATHERMITIANTRANSPOSEVIRTUAL, "")); 1605 if (!flg) C = A01; 1606 } 1607 PetscCall(ISCreateStride(PETSC_COMM_SELF, B->rmap->N, 0, 1, &uis)); 1608 PetscCall(ISSetIdentity(uis)); 1609 if (!data->is) { 1610 if (C) PetscCall(PetscObjectReference((PetscObject)C)); 1611 else PetscCall(MatTranspose(B, MAT_INITIAL_MATRIX, &C)); 1612 PetscCall(ISDuplicate(data_00->is, is)); 1613 PetscCall(MatIncreaseOverlap(A, 1, is, 1)); 1614 PetscCall(MatSetOption(C, MAT_SUBMAT_SINGLEIS, PETSC_TRUE)); 1615 PetscCall(MatCreateSubMatrices(C, 1, is, &uis, MAT_INITIAL_MATRIX, &sub)); 1616 PetscCall(MatDestroy(&C)); 1617 PetscCall(MatTranspose(sub[0], MAT_INITIAL_MATRIX, &C)); 1618 PetscCall(MatDestroySubMatrices(1, &sub)); 1619 PetscCall(MatFindNonzeroRows(C, &data->is)); 1620 PetscCall(MatDestroy(&C)); 1621 PetscCall(ISDestroy(is)); 1622 } 1623 if (PetscDefined(USE_DEBUG)) { 1624 PetscCall(PCHPDDMCheckSymmetry_Private(pc, A01, A10)); 1625 PetscCall(MatCreateSubMatrices(B, 1, &uis, &data_00->is, MAT_INITIAL_MATRIX, &sub)); /* expensive check since all processes fetch all rows (but only some columns) of the constraint matrix */ 1626 PetscCall(ISDestroy(&uis)); 1627 PetscCall(ISDuplicate(data->is, &uis)); 1628 PetscCall(ISSort(uis)); 1629 PetscCall(ISComplement(uis, 0, B->rmap->N, is)); 1630 PetscCall(MatDuplicate(sub[0], MAT_COPY_VALUES, &C)); 1631 PetscCall(MatZeroRowsIS(C, is[0], 0.0, nullptr, nullptr)); 1632 PetscCall(ISDestroy(is)); 1633 PetscCall(MatMultEqual(sub[0], C, 20, &flg)); 1634 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "The image of A_10 (R_i^p)^T from the local primal (e.g., velocity) space to the full dual (e.g., pressure) space is not restricted to the local dual space: A_10 (R_i^p)^T != R_i^d (R_i^d)^T A_10 (R_i^p)^T"); /* cf. eq. (9) of https://hal.science/hal-02343808v6/document */ 1635 PetscCall(MatDestroy(&C)); 1636 PetscCall(MatDestroySubMatrices(1, &sub)); 1637 } 1638 PetscCall(ISDestroy(&uis)); 1639 PetscCall(MatDestroy(&B)); 1640 } 1641 if (data->aux) PetscCall(MatNorm(data->aux, NORM_FROBENIUS, &norm)); 1642 else norm = 0.0; 1643 PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &norm, 1, MPIU_REAL, MPI_MAX, PetscObjectComm((PetscObject)P))); 1644 if (norm < PETSC_MACHINE_EPSILON * static_cast<PetscReal>(10.0)) { /* if A11 is near zero, e.g., Stokes equation, build a diagonal auxiliary (Neumann) Mat which is just a small diagonal weighted by the inverse of the multiplicity */ 1645 VecScatter scatter; 1646 Vec x; 1647 const PetscScalar *read; 1648 PetscScalar *write; 1649 1650 PetscCall(MatDestroy(&data->aux)); 1651 PetscCall(ISGetLocalSize(data->is, &n)); 1652 PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)P), n, PETSC_DECIDE, &x)); 1653 PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)P), n, PETSC_DECIDE, &v)); 1654 PetscCall(VecScatterCreate(x, data->is, v, nullptr, &scatter)); 1655 PetscCall(VecSet(v, 1.0)); 1656 PetscCall(VecSet(x, 1.0)); 1657 PetscCall(VecScatterBegin(scatter, v, x, ADD_VALUES, SCATTER_REVERSE)); 1658 PetscCall(VecScatterEnd(scatter, v, x, ADD_VALUES, SCATTER_REVERSE)); /* v has the multiplicity of all unknowns on the overlap */ 1659 PetscCall(VecScatterDestroy(&scatter)); 1660 PetscCall(VecDestroy(&v)); 1661 PetscCall(VecCreateSeq(PETSC_COMM_SELF, n, &v)); 1662 PetscCall(VecGetArrayRead(x, &read)); 1663 PetscCall(VecGetArrayWrite(v, &write)); 1664 PetscCallCXX(std::transform(read, read + n, write, [](const PetscScalar &m) { return PETSC_SMALL / (static_cast<PetscReal>(1000.0) * m); })); 1665 PetscCall(VecRestoreArrayRead(x, &read)); 1666 PetscCall(VecRestoreArrayWrite(v, &write)); 1667 PetscCall(VecDestroy(&x)); 1668 PetscCall(MatCreateDiagonal(v, &data->aux)); 1669 PetscCall(VecDestroy(&v)); 1670 } 1671 uis = data->is; 1672 uaux = data->aux; 1673 PetscCall(PetscObjectReference((PetscObject)uis)); 1674 PetscCall(PetscObjectReference((PetscObject)uaux)); 1675 PetscCall(PetscStrallocpy(pcpre, &prefix)); 1676 PetscCall(PCSetOptionsPrefix(pc, nullptr)); 1677 PetscCall(PCSetType(pc, PCKSP)); /* replace the PC associated to the Schur complement by PCKSP */ 1678 PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &inner_ksp)); /* new KSP that will be attached to the previously set PC */ 1679 PetscCall(PetscObjectGetTabLevel((PetscObject)pc, &n)); 1680 PetscCall(PetscObjectSetTabLevel((PetscObject)inner_ksp, n + 2)); 1681 PetscCall(KSPSetOperators(inner_ksp, pc->mat, pc->pmat)); 1682 PetscCall(KSPSetOptionsPrefix(inner_ksp, std::string(std::string(prefix) + "pc_hpddm_").c_str())); 1683 PetscCall(KSPSetSkipPCSetFromOptions(inner_ksp, PETSC_TRUE)); 1684 PetscCall(KSPSetFromOptions(inner_ksp)); 1685 PetscCall(KSPGetPC(inner_ksp, &inner)); 1686 PetscCall(PCSetOptionsPrefix(inner, nullptr)); 1687 PetscCall(PCSetType(inner, PCNONE)); /* no preconditioner since the action of M^-1 A or A M^-1 will be computed by the Amat */ 1688 PetscCall(PCKSPSetKSP(pc, inner_ksp)); 1689 PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)pc), &container)); 1690 PetscCall(PetscNew(&ctx)); /* context to pass data around for the inner-most PC, which will be a proper PCHPDDM */ 1691 PetscCall(PetscContainerSetPointer(container, ctx)); 1692 std::get<0>(*ctx)[0] = pc_00; /* for coarse correction on the primal (e.g., velocity) space */ 1693 PetscCall(PCCreate(PetscObjectComm((PetscObject)pc), &std::get<0>(*ctx)[1])); 1694 PetscCall(PCSetOptionsPrefix(std::get<0>(*ctx)[1], prefix)); 1695 PetscCall(PetscFree(prefix)); 1696 PetscCall(PCSetOperators(std::get<0>(*ctx)[1], pc->mat, pc->pmat)); 1697 PetscCall(PCSetType(std::get<0>(*ctx)[1], PCHPDDM)); 1698 PetscCall(PCHPDDMSetAuxiliaryMat(std::get<0>(*ctx)[1], uis, uaux, nullptr, nullptr)); /* transfer ownership of the auxiliary inputs from the inner (PCKSP) to the inner-most (PCHPDDM) PC */ 1699 PetscCall(PCSetFromOptions(std::get<0>(*ctx)[1])); 1700 PetscCall(PetscObjectDereference((PetscObject)uis)); 1701 PetscCall(PetscObjectDereference((PetscObject)uaux)); 1702 PetscCall(MatCreateShell(PetscObjectComm((PetscObject)pc), inner->mat->rmap->n, inner->mat->cmap->n, inner->mat->rmap->N, inner->mat->cmap->N, ctx, &S)); /* MatShell computing the action of M^-1 A or A M^-1 */ 1703 PetscCall(MatShellSetOperation(S, MATOP_MULT, (void (*)(void))MatMult_SchurCorrection)); 1704 PetscCall(MatShellSetOperation(S, MATOP_VIEW, (void (*)(void))MatView_SchurCorrection)); 1705 PetscCall(MatShellSetOperation(S, MATOP_DESTROY, (void (*)(void))MatDestroy_SchurCorrection)); 1706 PetscCall(KSPGetPCSide(inner_ksp, &(std::get<2>(*ctx)))); 1707 if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) { 1708 PetscCall(KSPSetPreSolve(inner_ksp, KSPPreSolve_SchurCorrection, ctx)); 1709 } else { /* no support for PC_SYMMETRIC */ 1710 PetscCheck(std::get<2>(*ctx) == PC_RIGHT, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "PCSide %s (!= %s or %s or %s)", PCSides[std::get<2>(*ctx)], PCSides[PC_SIDE_DEFAULT], PCSides[PC_LEFT], PCSides[PC_RIGHT]); 1711 } 1712 PetscCall(KSPSetPostSolve(inner_ksp, KSPPostSolve_SchurCorrection, ctx)); 1713 PetscCall(PetscObjectCompose((PetscObject)(std::get<0>(*ctx)[1]), "_PCHPDDM_Schur", (PetscObject)container)); 1714 PetscCall(PetscObjectDereference((PetscObject)container)); 1715 PetscCall(PCSetUp(std::get<0>(*ctx)[1])); 1716 PetscCall(PCSetUp(pc)); 1717 PetscCall(KSPSetOperators(inner_ksp, S, S)); 1718 PetscCall(MatCreateVecs(std::get<1>(*ctx)[0], std::get<3>(*ctx), std::get<3>(*ctx) + 1)); 1719 PetscCall(VecDuplicate(std::get<3>(*ctx)[0], std::get<3>(*ctx) + 2)); 1720 PetscCall(PetscObjectDereference((PetscObject)inner_ksp)); 1721 PetscCall(PetscObjectDereference((PetscObject)S)); 1722 PetscFunctionReturn(PETSC_SUCCESS); 1723 } else { /* second call to PCSetUp() on the PC associated to the Schur complement, retrieve previously set context */ 1724 PetscCall(PetscContainerGetPointer(container, (void **)&ctx)); 1725 } 1726 } 1727 } 1728 } 1729 if (!data->is && data->N > 1) { 1730 char type[256] = {}; /* same size as in src/ksp/pc/interface/pcset.c */ 1731 PetscCall(PetscObjectTypeCompareAny((PetscObject)P, &flg, MATNORMAL, MATNORMALHERMITIAN, "")); 1732 if (flg || (A->rmap->N != A->cmap->N && P->rmap->N == P->cmap->N && P->rmap->N == A->cmap->N)) { 1733 Mat B; 1734 PetscCall(PCHPDDMSetAuxiliaryMatNormal_Private(pc, A, P, &B, pcpre)); 1735 if (data->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED) data->correction = PC_HPDDM_COARSE_CORRECTION_BALANCED; 1736 PetscCall(MatDestroy(&B)); 1737 } else { 1738 PetscCall(PetscObjectTypeCompare((PetscObject)P, MATSCHURCOMPLEMENT, &flg)); 1739 if (flg) { 1740 Mat A00, P00, A01, A10, A11, B, N; 1741 PCHPDDMSchurPreType type = PC_HPDDM_SCHUR_PRE_LEAST_SQUARES; 1742 1743 PetscCall(MatSchurComplementGetSubMatrices(P, &A00, &P00, &A01, &A10, &A11)); 1744 if (PetscDefined(USE_DEBUG)) PetscCall(PCHPDDMCheckSymmetry_Private(pc, A01, A10)); 1745 PetscCall(PetscOptionsGetEnum(nullptr, pcpre, "-pc_hpddm_schur_precondition", PCHPDDMSchurPreTypes, (PetscEnum *)&type, &flg)); 1746 if (type == PC_HPDDM_SCHUR_PRE_LEAST_SQUARES) { 1747 Vec diagonal = nullptr; 1748 const PetscScalar *array; 1749 MatSchurComplementAinvType type; 1750 1751 if (A11) { 1752 PetscCall(MatCreateVecs(A11, &diagonal, nullptr)); 1753 PetscCall(MatGetDiagonal(A11, diagonal)); 1754 } 1755 PetscCall(MatCreateVecs(P00, &v, nullptr)); 1756 PetscCall(MatSchurComplementGetAinvType(P, &type)); 1757 PetscCheck(type == MAT_SCHUR_COMPLEMENT_AINV_DIAG || type == MAT_SCHUR_COMPLEMENT_AINV_LUMP, PetscObjectComm((PetscObject)P), PETSC_ERR_SUP, "-%smat_schur_complement_ainv_type %s", ((PetscObject)P)->prefix ? ((PetscObject)P)->prefix : "", MatSchurComplementAinvTypes[type]); 1758 if (type == MAT_SCHUR_COMPLEMENT_AINV_LUMP) { 1759 PetscCall(MatGetRowSum(P00, v)); 1760 if (A00 == P00) PetscCall(PetscObjectReference((PetscObject)A00)); 1761 PetscCall(MatDestroy(&P00)); 1762 PetscCall(VecGetArrayRead(v, &array)); 1763 PetscCall(MatCreateAIJ(PetscObjectComm((PetscObject)A00), A00->rmap->n, A00->cmap->n, A00->rmap->N, A00->cmap->N, 1, nullptr, 0, nullptr, &P00)); 1764 PetscCall(MatSetOption(P00, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE)); 1765 for (n = A00->rmap->rstart; n < A00->rmap->rend; ++n) PetscCall(MatSetValue(P00, n, n, array[n - A00->rmap->rstart], INSERT_VALUES)); 1766 PetscCall(MatAssemblyBegin(P00, MAT_FINAL_ASSEMBLY)); 1767 PetscCall(MatAssemblyEnd(P00, MAT_FINAL_ASSEMBLY)); 1768 PetscCall(VecRestoreArrayRead(v, &array)); 1769 PetscCall(MatSchurComplementUpdateSubMatrices(P, A00, P00, A01, A10, A11)); /* replace P00 by diag(sum of each row of P00) */ 1770 PetscCall(MatDestroy(&P00)); 1771 } else PetscCall(MatGetDiagonal(P00, v)); 1772 PetscCall(VecReciprocal(v)); /* inv(diag(P00)) */ 1773 PetscCall(VecSqrtAbs(v)); /* sqrt(inv(diag(P00))) */ 1774 PetscCall(MatDuplicate(A01, MAT_COPY_VALUES, &B)); 1775 PetscCall(MatDiagonalScale(B, v, nullptr)); 1776 PetscCall(VecDestroy(&v)); 1777 PetscCall(MatCreateNormalHermitian(B, &N)); 1778 PetscCall(PCHPDDMSetAuxiliaryMatNormal_Private(pc, B, N, &P, pcpre, &diagonal)); 1779 PetscCall(PetscObjectTypeCompare((PetscObject)data->aux, MATSEQAIJ, &flg)); 1780 if (!flg) { 1781 PetscCall(MatDestroy(&P)); 1782 P = N; 1783 PetscCall(PetscObjectReference((PetscObject)P)); 1784 } else PetscCall(MatScale(P, -1.0)); 1785 if (diagonal) { 1786 PetscCall(MatDiagonalSet(P, diagonal, ADD_VALUES)); 1787 PetscCall(PCSetOperators(pc, P, P)); /* replace P by diag(P11) - A01^T inv(diag(P00)) A01 */ 1788 PetscCall(VecDestroy(&diagonal)); 1789 } else { 1790 PetscCall(MatScale(N, -1.0)); 1791 PetscCall(PCSetOperators(pc, N, P)); /* replace P by - A01^T inv(diag(P00)) A01 */ 1792 } 1793 PetscCall(MatDestroy(&N)); 1794 PetscCall(MatDestroy(&P)); 1795 PetscCall(MatDestroy(&B)); 1796 } else 1797 PetscCheck(type != PC_HPDDM_SCHUR_PRE_GENEO, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo without a prior call to PCHPDDMSetAuxiliaryMat() on the A11 block%s%s", pcpre ? pcpre : "", pcpre ? " -" : "", pcpre ? pcpre : ""); 1798 PetscFunctionReturn(PETSC_SUCCESS); 1799 } else { 1800 PetscCall(PetscOptionsGetString(nullptr, pcpre, "-pc_hpddm_levels_1_st_pc_type", type, sizeof(type), nullptr)); 1801 PetscCall(PetscStrcmp(type, PCMAT, &algebraic)); 1802 PetscCheck(!algebraic || !block, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_levels_1_st_pc_type mat and -pc_hpddm_block_splitting"); 1803 if (overlap != -1) { 1804 PetscCheck(!block && !algebraic, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_%s and -pc_hpddm_harmonic_overlap", block ? "block_splitting" : "levels_1_st_pc_type mat"); 1805 PetscCheck(overlap >= 1, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_WRONG, "-pc_hpddm_harmonic_overlap %" PetscInt_FMT " < 1", overlap); 1806 } 1807 if (block || overlap != -1) algebraic = PETSC_TRUE; 1808 if (algebraic) { 1809 PetscCall(ISCreateStride(PETSC_COMM_SELF, P->rmap->n, P->rmap->rstart, 1, &data->is)); 1810 PetscCall(MatIncreaseOverlap(P, 1, &data->is, 1)); 1811 PetscCall(ISSort(data->is)); 1812 } else 1813 PetscCall(PetscInfo(pc, "Cannot assemble a fully-algebraic coarse operator with an assembled Pmat and -%spc_hpddm_levels_1_st_pc_type != mat and -%spc_hpddm_block_splitting != true and -%spc_hpddm_harmonic_overlap < 1\n", pcpre ? pcpre : "", pcpre ? pcpre : "", pcpre ? pcpre : "")); 1814 } 1815 } 1816 } 1817 } 1818 #if PetscDefined(USE_DEBUG) 1819 if (data->is) PetscCall(ISDuplicate(data->is, &dis)); 1820 if (data->aux) PetscCall(MatDuplicate(data->aux, MAT_COPY_VALUES, &daux)); 1821 #endif 1822 if (data->is || (ismatis && data->N > 1)) { 1823 if (ismatis) { 1824 std::initializer_list<std::string> list = {MATSEQBAIJ, MATSEQSBAIJ}; 1825 PetscCall(MatISGetLocalMat(P, &N)); 1826 std::initializer_list<std::string>::const_iterator it = std::find(list.begin(), list.end(), ((PetscObject)N)->type_name); 1827 PetscCall(MatISRestoreLocalMat(P, &N)); 1828 switch (std::distance(list.begin(), it)) { 1829 case 0: 1830 PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &C)); 1831 break; 1832 case 1: 1833 /* MatCreateSubMatrices() does not work with MATSBAIJ and unsorted ISes, so convert to MPIBAIJ */ 1834 PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &C)); 1835 PetscCall(MatSetOption(C, MAT_SYMMETRIC, PETSC_TRUE)); 1836 break; 1837 default: 1838 PetscCall(MatConvert(P, MATMPIAIJ, MAT_INITIAL_MATRIX, &C)); 1839 } 1840 PetscCall(MatISGetLocalToGlobalMapping(P, &l2g, nullptr)); 1841 PetscCall(PetscObjectReference((PetscObject)P)); 1842 PetscCall(KSPSetOperators(data->levels[0]->ksp, A, C)); 1843 std::swap(C, P); 1844 PetscCall(ISLocalToGlobalMappingGetSize(l2g, &n)); 1845 PetscCall(ISCreateStride(PETSC_COMM_SELF, n, 0, 1, &loc)); 1846 PetscCall(ISLocalToGlobalMappingApplyIS(l2g, loc, &is[0])); 1847 PetscCall(ISDestroy(&loc)); 1848 /* the auxiliary Mat is _not_ the local Neumann matrix */ 1849 /* it is the local Neumann matrix augmented (with zeros) through MatIncreaseOverlap() */ 1850 data->Neumann = PETSC_BOOL3_FALSE; 1851 structure = SAME_NONZERO_PATTERN; 1852 } else { 1853 is[0] = data->is; 1854 if (algebraic || ctx) subdomains = PETSC_TRUE; 1855 PetscCall(PetscOptionsGetBool(nullptr, pcpre, "-pc_hpddm_define_subdomains", &subdomains, nullptr)); 1856 if (ctx) PetscCheck(subdomains, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_hpddm_define_subdomains false", pcpre, pcpre); 1857 if (PetscBool3ToBool(data->Neumann)) { 1858 PetscCheck(!block, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_block_splitting and -pc_hpddm_has_neumann"); 1859 PetscCheck(overlap == -1, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_harmonic_overlap %" PetscInt_FMT " and -pc_hpddm_has_neumann", overlap); 1860 PetscCheck(!algebraic, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_levels_1_st_pc_type mat and -pc_hpddm_has_neumann"); 1861 } 1862 if (PetscBool3ToBool(data->Neumann) || block) structure = SAME_NONZERO_PATTERN; 1863 PetscCall(ISCreateStride(PetscObjectComm((PetscObject)data->is), P->rmap->n, P->rmap->rstart, 1, &loc)); 1864 } 1865 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_", pcpre ? pcpre : "")); 1866 PetscCall(PetscOptionsGetEnum(nullptr, prefix, "-st_matstructure", MatStructures, (PetscEnum *)&structure, &flg)); /* if not user-provided, force its value when possible */ 1867 if (!flg && structure == SAME_NONZERO_PATTERN) { /* cannot call STSetMatStructure() yet, insert the appropriate option in the database, parsed by STSetFromOptions() */ 1868 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-%spc_hpddm_levels_1_st_matstructure", pcpre ? pcpre : "")); 1869 PetscCall(PetscOptionsSetValue(nullptr, prefix, MatStructures[structure])); 1870 } 1871 flg = PETSC_FALSE; 1872 if (data->share) { 1873 data->share = PETSC_FALSE; /* will be reset to PETSC_TRUE if none of the conditions below are true */ 1874 if (!subdomains) PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since -%spc_hpddm_define_subdomains is not true\n", pcpre ? pcpre : "")); 1875 else if (data->deflation) PetscCall(PetscInfo(pc, "Nothing to share since PCHPDDMSetDeflationMat() has been called\n")); 1876 else if (ismatis) PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc with a Pmat of type MATIS\n")); 1877 else if (!algebraic && structure != SAME_NONZERO_PATTERN) 1878 PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since -%spc_hpddm_levels_1_st_matstructure %s (!= %s)\n", pcpre ? pcpre : "", MatStructures[structure], MatStructures[SAME_NONZERO_PATTERN])); 1879 else data->share = PETSC_TRUE; 1880 } 1881 if (!ismatis) { 1882 if (data->share || (!PetscBool3ToBool(data->Neumann) && subdomains)) PetscCall(ISDuplicate(is[0], &unsorted)); 1883 else unsorted = is[0]; 1884 } 1885 if (data->N > 1 && (data->aux || ismatis || algebraic)) { 1886 PetscCheck(loadedSym, PETSC_COMM_SELF, PETSC_ERR_PLIB, "HPDDM library not loaded, cannot use more than one level"); 1887 PetscCall(MatSetOption(P, MAT_SUBMAT_SINGLEIS, PETSC_TRUE)); 1888 if (ismatis) { 1889 /* needed by HPDDM (currently) so that the partition of unity is 0 on subdomain interfaces */ 1890 PetscCall(MatIncreaseOverlap(P, 1, is, 1)); 1891 PetscCall(ISDestroy(&data->is)); 1892 data->is = is[0]; 1893 } else { 1894 if (PetscDefined(USE_DEBUG)) { 1895 PetscBool equal; 1896 IS intersect; 1897 1898 PetscCall(ISIntersect(data->is, loc, &intersect)); 1899 PetscCall(ISEqualUnsorted(loc, intersect, &equal)); 1900 PetscCall(ISDestroy(&intersect)); 1901 PetscCheck(equal, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "IS of the auxiliary Mat does not include all local rows of A"); 1902 } 1903 if (overlap == -1) PetscCall(PetscObjectComposeFunction((PetscObject)pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_Private_C", PCHPDDMAlgebraicAuxiliaryMat_Private)); 1904 if (!PetscBool3ToBool(data->Neumann) && (!algebraic || overlap != -1)) { 1905 PetscCall(PetscObjectTypeCompare((PetscObject)P, MATMPISBAIJ, &flg)); 1906 if (flg) { 1907 /* maybe better to ISSort(is[0]), MatCreateSubMatrices(), and then MatPermute() */ 1908 /* but there is no MatPermute_SeqSBAIJ(), so as before, just use MATMPIBAIJ */ 1909 PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &uaux)); 1910 flg = PETSC_FALSE; 1911 } 1912 } 1913 } 1914 if (algebraic && overlap == -1) { 1915 PetscUseMethod(pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_Private_C", (Mat, IS *, Mat *[], PetscBool), (P, is, &sub, block)); 1916 if (block) { 1917 PetscCall(PetscObjectQuery((PetscObject)sub[0], "_PCHPDDM_Neumann_Mat", (PetscObject *)&data->aux)); 1918 PetscCall(PetscObjectCompose((PetscObject)sub[0], "_PCHPDDM_Neumann_Mat", nullptr)); 1919 } 1920 } else if (!uaux || overlap != -1) { 1921 if (!ctx) { 1922 if (PetscBool3ToBool(data->Neumann)) sub = &data->aux; 1923 else { 1924 if (overlap != -1) { 1925 Harmonic h; 1926 Mat A0, *a; /* with an SVD: [ A_00 A_01 ] */ 1927 IS ov[2], rows, cols, stride; /* [ A_10 A_11 A_12 ] */ 1928 const PetscInt *i[2], bs = PetscAbs(P->cmap->bs); /* with a GEVP: [ A_00 A_01 ] */ 1929 PetscInt n[2]; /* [ A_10 A_11 A_12 ] */ 1930 std::vector<PetscInt> v[2]; /* [ A_21 A_22 ] */ 1931 PetscBool flg; 1932 1933 PetscCall(ISDuplicate(data->is, ov)); 1934 if (overlap > 1) PetscCall(MatIncreaseOverlap(P, 1, ov, overlap - 1)); 1935 PetscCall(ISDuplicate(ov[0], ov + 1)); 1936 PetscCall(MatIncreaseOverlap(P, 1, ov + 1, 1)); 1937 PetscCall(PetscNew(&h)); 1938 h->ksp = nullptr; 1939 PetscCall(PetscCalloc1(2, &h->A)); 1940 PetscCall(PetscOptionsHasName(nullptr, pcpre, "-svd_nsv", &flg)); 1941 if (!flg) PetscCall(PetscOptionsHasName(nullptr, prefix, "-svd_relative_threshold", &flg)); 1942 PetscCall(ISSort(ov[0])); 1943 if (!flg) PetscCall(ISSort(ov[1])); 1944 PetscCall(PetscMalloc1(!flg ? 5 : 3, &h->is)); 1945 PetscCall(MatCreateSubMatrices(uaux ? uaux : P, 1, ov + !flg, ov + 1, MAT_INITIAL_MATRIX, &a)); /* submatrix from above, either square (!flg) or rectangular (flg) */ 1946 for (PetscInt j = 0; j < 2; ++j) { 1947 PetscCall(ISGetIndices(ov[j], i + j)); 1948 PetscCall(ISGetLocalSize(ov[j], n + j)); 1949 } 1950 v[1].reserve((n[1] - n[0]) / bs); 1951 for (PetscInt j = 0; j < n[1]; j += bs) { /* indices of the (2,2) block */ 1952 PetscInt location; 1953 PetscCall(ISLocate(ov[0], i[1][j], &location)); 1954 if (location < 0) v[1].emplace_back(j / bs); 1955 } 1956 if (!flg) { 1957 h->A[1] = a[0]; 1958 PetscCall(PetscObjectReference((PetscObject)h->A[1])); 1959 v[0].reserve((n[0] - P->rmap->n) / bs); 1960 for (PetscInt j = 0; j < n[1]; j += bs) { /* row indices of the (1,2) block */ 1961 PetscInt location; 1962 PetscCall(ISLocate(loc, i[1][j], &location)); 1963 if (location < 0) { 1964 PetscCall(ISLocate(ov[0], i[1][j], &location)); 1965 if (location >= 0) v[0].emplace_back(j / bs); 1966 } 1967 } 1968 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[0].size(), v[0].data(), PETSC_USE_POINTER, &rows)); 1969 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[1].size(), v[1].data(), PETSC_COPY_VALUES, h->is + 4)); 1970 PetscCall(MatCreateSubMatrix(a[0], rows, h->is[4], MAT_INITIAL_MATRIX, h->A)); /* A_12 submatrix from above */ 1971 PetscCall(ISDestroy(&rows)); 1972 if (uaux) PetscCall(MatConvert(a[0], MATSEQSBAIJ, MAT_INPLACE_MATRIX, a)); /* initial Pmat was MATSBAIJ, convert back to the same format since the rectangular A_12 submatrix has been created */ 1973 PetscCall(ISEmbed(ov[0], ov[1], PETSC_TRUE, &rows)); 1974 PetscCall(MatCreateSubMatrix(a[0], rows, cols = rows, MAT_INITIAL_MATRIX, &A0)); /* [ A_00 A_01 ; A_10 A_11 ] submatrix from above */ 1975 PetscCall(ISDestroy(&rows)); 1976 v[0].clear(); 1977 PetscCall(ISEmbed(loc, ov[1], PETSC_TRUE, h->is + 3)); 1978 PetscCall(ISEmbed(data->is, ov[1], PETSC_TRUE, h->is + 2)); 1979 } 1980 v[0].reserve((n[0] - P->rmap->n) / bs); 1981 for (PetscInt j = 0; j < n[0]; j += bs) { 1982 PetscInt location; 1983 PetscCall(ISLocate(loc, i[0][j], &location)); 1984 if (location < 0) v[0].emplace_back(j / bs); 1985 } 1986 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[0].size(), v[0].data(), PETSC_USE_POINTER, &rows)); 1987 for (PetscInt j = 0; j < 2; ++j) PetscCall(ISRestoreIndices(ov[j], i + j)); 1988 if (flg) { 1989 IS is; 1990 PetscCall(ISCreateStride(PETSC_COMM_SELF, a[0]->rmap->n, 0, 1, &is)); 1991 PetscCall(ISEmbed(ov[0], ov[1], PETSC_TRUE, &cols)); 1992 PetscCall(MatCreateSubMatrix(a[0], is, cols, MAT_INITIAL_MATRIX, &A0)); /* [ A_00 A_01 ; A_10 A_11 ] submatrix from above */ 1993 PetscCall(ISDestroy(&cols)); 1994 PetscCall(ISDestroy(&is)); 1995 if (uaux) PetscCall(MatConvert(A0, MATSEQSBAIJ, MAT_INPLACE_MATRIX, &A0)); /* initial Pmat was MATSBAIJ, convert back to the same format since this submatrix is square */ 1996 PetscCall(ISEmbed(loc, data->is, PETSC_TRUE, h->is + 2)); 1997 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[1].size(), v[1].data(), PETSC_USE_POINTER, &cols)); 1998 PetscCall(MatCreateSubMatrix(a[0], rows, cols, MAT_INITIAL_MATRIX, h->A)); /* A_12 submatrix from above */ 1999 PetscCall(ISDestroy(&cols)); 2000 } 2001 PetscCall(ISCreateStride(PETSC_COMM_SELF, A0->rmap->n, 0, 1, &stride)); 2002 PetscCall(ISEmbed(rows, stride, PETSC_TRUE, h->is)); 2003 PetscCall(ISDestroy(&stride)); 2004 PetscCall(ISDestroy(&rows)); 2005 PetscCall(ISEmbed(loc, ov[0], PETSC_TRUE, h->is + 1)); 2006 if (subdomains) { 2007 if (!data->levels[0]->pc) { 2008 PetscCall(PCCreate(PetscObjectComm((PetscObject)pc), &data->levels[0]->pc)); 2009 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_", pcpre ? pcpre : "")); 2010 PetscCall(PCSetOptionsPrefix(data->levels[0]->pc, prefix)); 2011 PetscCall(PCSetOperators(data->levels[0]->pc, A, P)); 2012 } 2013 PetscCall(PCSetType(data->levels[0]->pc, PCASM)); 2014 if (!data->levels[0]->pc->setupcalled) PetscCall(PCASMSetLocalSubdomains(data->levels[0]->pc, 1, ov + !flg, &loc)); 2015 PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(data->levels[0]->pc, flg ? A0 : a[0], PETSC_TRUE)); 2016 if (!flg) ++overlap; 2017 if (data->share) { 2018 PetscInt n = -1; 2019 PetscTryMethod(data->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data->levels[0]->pc, &n, nullptr, &ksp)); 2020 PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", n); 2021 if (flg) { 2022 h->ksp = ksp[0]; 2023 PetscCall(PetscObjectReference((PetscObject)h->ksp)); 2024 } 2025 } 2026 } 2027 if (!h->ksp) { 2028 PetscBool share = data->share; 2029 PetscCall(KSPCreate(PETSC_COMM_SELF, &h->ksp)); 2030 PetscCall(KSPSetType(h->ksp, KSPPREONLY)); 2031 PetscCall(KSPSetOperators(h->ksp, A0, A0)); 2032 do { 2033 if (!data->share) { 2034 share = PETSC_FALSE; 2035 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_%s", pcpre ? pcpre : "", flg ? "svd_" : "eps_")); 2036 PetscCall(KSPSetOptionsPrefix(h->ksp, prefix)); 2037 PetscCall(KSPSetFromOptions(h->ksp)); 2038 } else { 2039 MatSolverType type; 2040 PetscCall(KSPGetPC(ksp[0], &pc)); 2041 PetscCall(PetscObjectTypeCompareAny((PetscObject)pc, &data->share, PCLU, PCCHOLESKY, "")); 2042 if (data->share) { 2043 PetscCall(PCFactorGetMatSolverType(pc, &type)); 2044 if (!type) { 2045 if (PetscDefined(HAVE_MUMPS)) PetscCall(PCFactorSetMatSolverType(pc, MATSOLVERMUMPS)); 2046 else if (PetscDefined(HAVE_MKL_PARDISO)) PetscCall(PCFactorSetMatSolverType(pc, MATSOLVERMKL_PARDISO)); 2047 else data->share = PETSC_FALSE; 2048 if (data->share) PetscCall(PCSetFromOptions(pc)); 2049 } else { 2050 PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &data->share)); 2051 if (!data->share) PetscCall(PetscStrcmp(type, MATSOLVERMKL_PARDISO, &data->share)); 2052 } 2053 if (data->share) { 2054 std::tuple<KSP, IS, Vec[2]> *p; 2055 PetscCall(PCFactorGetMatrix(pc, &A)); 2056 PetscCall(MatFactorSetSchurIS(A, h->is[4])); 2057 PetscCall(KSPSetUp(ksp[0])); 2058 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_eps_shell_", pcpre ? pcpre : "")); 2059 PetscCall(KSPSetOptionsPrefix(h->ksp, prefix)); 2060 PetscCall(KSPSetFromOptions(h->ksp)); 2061 PetscCall(KSPGetPC(h->ksp, &pc)); 2062 PetscCall(PCSetType(pc, PCSHELL)); 2063 PetscCall(PetscNew(&p)); 2064 std::get<0>(*p) = ksp[0]; 2065 PetscCall(ISEmbed(ov[0], ov[1], PETSC_TRUE, &std::get<1>(*p))); 2066 PetscCall(MatCreateVecs(A, std::get<2>(*p), std::get<2>(*p) + 1)); 2067 PetscCall(PCShellSetContext(pc, p)); 2068 PetscCall(PCShellSetApply(pc, PCApply_Schur)); 2069 PetscCall(PCShellSetApplyTranspose(pc, PCApply_Schur<Vec, true>)); 2070 PetscCall(PCShellSetMatApply(pc, PCApply_Schur<Mat>)); 2071 PetscCall(PCShellSetDestroy(pc, PCDestroy_Schur)); 2072 } 2073 } 2074 if (!data->share) PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since neither MUMPS nor MKL PARDISO is used\n")); 2075 } 2076 } while (!share != !data->share); /* if data->share is initially PETSC_TRUE, but then reset to PETSC_FALSE, then go back to the beginning of the do loop */ 2077 } 2078 PetscCall(ISDestroy(ov)); 2079 PetscCall(ISDestroy(ov + 1)); 2080 if (overlap == 1 && subdomains && flg) { 2081 *subA = A0; 2082 sub = subA; 2083 if (uaux) PetscCall(MatDestroy(&uaux)); 2084 } else PetscCall(MatDestroy(&A0)); 2085 PetscCall(MatCreateShell(PETSC_COMM_SELF, P->rmap->n, n[1] - n[0], P->rmap->n, n[1] - n[0], h, &data->aux)); 2086 PetscCall(MatCreateVecs(h->ksp->pc->pmat, &h->v, nullptr)); 2087 PetscCall(MatShellSetOperation(data->aux, MATOP_MULT, (void (*)(void))MatMult_Harmonic)); 2088 PetscCall(MatShellSetOperation(data->aux, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMultTranspose_Harmonic)); 2089 PetscCall(MatShellSetMatProductOperation(data->aux, MATPRODUCT_AB, nullptr, MatProduct_AB_Harmonic, nullptr, MATDENSE, MATDENSE)); 2090 PetscCall(MatShellSetOperation(data->aux, MATOP_DESTROY, (void (*)(void))MatDestroy_Harmonic)); 2091 PetscCall(MatDestroySubMatrices(1, &a)); 2092 } 2093 if (overlap != 1 || !subdomains) PetscCall(MatCreateSubMatrices(uaux ? uaux : P, 1, is, is, MAT_INITIAL_MATRIX, &sub)); 2094 if (uaux) { 2095 PetscCall(MatDestroy(&uaux)); 2096 PetscCall(MatConvert(sub[0], MATSEQSBAIJ, MAT_INPLACE_MATRIX, sub)); 2097 } 2098 } 2099 } 2100 } else { 2101 PetscCall(MatCreateSubMatrices(uaux, 1, is, is, MAT_INITIAL_MATRIX, &sub)); 2102 PetscCall(MatDestroy(&uaux)); 2103 PetscCall(MatConvert(sub[0], MATSEQSBAIJ, MAT_INPLACE_MATRIX, sub)); 2104 } 2105 /* Vec holding the partition of unity */ 2106 if (!data->levels[0]->D) { 2107 PetscCall(ISGetLocalSize(data->is, &n)); 2108 PetscCall(VecCreateMPI(PETSC_COMM_SELF, n, PETSC_DETERMINE, &data->levels[0]->D)); 2109 } 2110 if (data->share && overlap == -1) { 2111 Mat D; 2112 IS perm = nullptr; 2113 PetscInt size = -1; 2114 if (!data->levels[0]->pc) { 2115 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_", pcpre ? pcpre : "")); 2116 PetscCall(PCCreate(PetscObjectComm((PetscObject)pc), &data->levels[0]->pc)); 2117 PetscCall(PCSetOptionsPrefix(data->levels[0]->pc, prefix)); 2118 PetscCall(PCSetOperators(data->levels[0]->pc, A, P)); 2119 } 2120 PetscCall(PCSetType(data->levels[0]->pc, PCASM)); 2121 if (!ctx) { 2122 if (!data->levels[0]->pc->setupcalled) { 2123 IS sorted; /* PCASM will sort the input IS, duplicate it to return an unmodified (PCHPDDM) input IS */ 2124 PetscCall(ISDuplicate(is[0], &sorted)); 2125 PetscCall(PCASMSetLocalSubdomains(data->levels[0]->pc, 1, &sorted, &loc)); 2126 PetscCall(PetscObjectDereference((PetscObject)sorted)); 2127 } 2128 PetscCall(PCSetFromOptions(data->levels[0]->pc)); 2129 if (block) { 2130 PetscCall(PCHPDDMPermute_Private(unsorted, data->is, &uis, sub[0], &C, &perm)); 2131 PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(data->levels[0]->pc, C, algebraic)); 2132 } else PetscCall(PCSetUp(data->levels[0]->pc)); 2133 PetscTryMethod(data->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data->levels[0]->pc, &size, nullptr, &ksp)); 2134 if (size != 1) { 2135 data->share = PETSC_FALSE; 2136 PetscCheck(size == -1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", size); 2137 PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since PCASMGetSubKSP() not found in fine-level PC\n")); 2138 PetscCall(ISDestroy(&unsorted)); 2139 unsorted = is[0]; 2140 } else { 2141 if (!block && !ctx) PetscCall(PCHPDDMPermute_Private(unsorted, data->is, &uis, PetscBool3ToBool(data->Neumann) ? sub[0] : data->aux, &C, &perm)); 2142 if (!PetscBool3ToBool(data->Neumann) && !block) { 2143 PetscCall(MatPermute(sub[0], perm, perm, &D)); /* permute since PCASM will call ISSort() */ 2144 PetscCall(MatHeaderReplace(sub[0], &D)); 2145 } 2146 if (data->B) { /* see PCHPDDMSetRHSMat() */ 2147 PetscCall(MatPermute(data->B, perm, perm, &D)); 2148 PetscCall(MatHeaderReplace(data->B, &D)); 2149 } 2150 PetscCall(ISDestroy(&perm)); 2151 const char *matpre; 2152 PetscBool cmp[4]; 2153 PetscCall(KSPGetOperators(ksp[0], subA, subA + 1)); 2154 PetscCall(MatDuplicate(subA[1], MAT_SHARE_NONZERO_PATTERN, &D)); 2155 PetscCall(MatGetOptionsPrefix(subA[1], &matpre)); 2156 PetscCall(MatSetOptionsPrefix(D, matpre)); 2157 PetscCall(PetscObjectTypeCompare((PetscObject)D, MATNORMAL, cmp)); 2158 PetscCall(PetscObjectTypeCompare((PetscObject)C, MATNORMAL, cmp + 1)); 2159 if (!cmp[0]) PetscCall(PetscObjectTypeCompare((PetscObject)D, MATNORMALHERMITIAN, cmp + 2)); 2160 else cmp[2] = PETSC_FALSE; 2161 if (!cmp[1]) PetscCall(PetscObjectTypeCompare((PetscObject)C, MATNORMALHERMITIAN, cmp + 3)); 2162 else cmp[3] = PETSC_FALSE; 2163 PetscCheck(cmp[0] == cmp[1] && cmp[2] == cmp[3], PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_levels_1_pc_asm_sub_mat_type %s and auxiliary Mat of type %s", pcpre ? pcpre : "", ((PetscObject)D)->type_name, ((PetscObject)C)->type_name); 2164 if (!cmp[0] && !cmp[2]) { 2165 if (!block) PetscCall(MatAXPY(D, 1.0, C, SUBSET_NONZERO_PATTERN)); 2166 else { 2167 PetscCall(MatMissingDiagonal(D, cmp, nullptr)); 2168 if (cmp[0]) structure = DIFFERENT_NONZERO_PATTERN; /* data->aux has no missing diagonal entry */ 2169 PetscCall(MatAXPY(D, 1.0, data->aux, structure)); 2170 } 2171 } else { 2172 Mat mat[2]; 2173 if (cmp[0]) { 2174 PetscCall(MatNormalGetMat(D, mat)); 2175 PetscCall(MatNormalGetMat(C, mat + 1)); 2176 } else { 2177 PetscCall(MatNormalHermitianGetMat(D, mat)); 2178 PetscCall(MatNormalHermitianGetMat(C, mat + 1)); 2179 } 2180 PetscCall(MatAXPY(mat[0], 1.0, mat[1], SUBSET_NONZERO_PATTERN)); 2181 } 2182 PetscCall(MatPropagateSymmetryOptions(C, D)); 2183 PetscCall(MatDestroy(&C)); 2184 C = D; 2185 /* swap pointers so that variables stay consistent throughout PCSetUp() */ 2186 std::swap(C, data->aux); 2187 std::swap(uis, data->is); 2188 swap = PETSC_TRUE; 2189 } 2190 } 2191 } 2192 if (ctx) { 2193 PC_HPDDM *data_00 = (PC_HPDDM *)std::get<0>(*ctx)[0]->data; 2194 PC s; 2195 Mat A00, P00, A01 = nullptr, A10, A11, N, b[4]; 2196 IS sorted, is[2]; 2197 MatSolverType type; 2198 std::pair<PC, Vec[2]> *p; 2199 2200 PetscCall(PCHPDDMPermute_Private(unsorted, data->is, &uis, data->aux, &C, nullptr)); /* permute since PCASM works with a sorted IS */ 2201 std::swap(C, data->aux); 2202 std::swap(uis, data->is); 2203 swap = PETSC_TRUE; 2204 PetscCall(PCASMSetType(data->levels[0]->pc, PC_ASM_NONE)); /* "Neumann--Neumann" preconditioning with overlap and a Boolean partition of unity */ 2205 PetscCall(PCASMSetLocalSubdomains(data->levels[0]->pc, 1, &data->is, &loc)); 2206 PetscCall(PCSetFromOptions(data->levels[0]->pc)); /* action of eq. (15) of https://hal.science/hal-02343808v6/document (with a sign flip) */ 2207 PetscCall(MatSchurComplementGetSubMatrices(P, &A00, &P00, std::get<1>(*ctx), &A10, &A11)); 2208 std::get<1>(*ctx)[1] = A10; 2209 PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATTRANSPOSEVIRTUAL, &flg)); 2210 if (flg) PetscCall(MatTransposeGetMat(A10, &A01)); 2211 else { 2212 PetscBool flg; 2213 2214 PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATHERMITIANTRANSPOSEVIRTUAL, &flg)); 2215 if (flg) PetscCall(MatHermitianTransposeGetMat(A10, &A01)); 2216 } 2217 PetscCall(ISDuplicate(data_00->is, &sorted)); /* during setup of the PC associated to the A00 block, this IS has already been sorted, but it's put back to its original state at the end of PCSetUp_HPDDM(), which may be unsorted */ 2218 PetscCall(ISSort(sorted)); /* this is to avoid changing users inputs, but it requires a new call to ISSort() here */ 2219 if (!A01) { 2220 PetscCall(MatSetOption(A10, MAT_SUBMAT_SINGLEIS, PETSC_TRUE)); 2221 PetscCall(MatCreateSubMatrices(A10, 1, &data->is, &sorted, MAT_INITIAL_MATRIX, &sub)); 2222 b[2] = sub[0]; 2223 PetscCall(PetscObjectReference((PetscObject)sub[0])); 2224 PetscCall(MatDestroySubMatrices(1, &sub)); 2225 PetscCall(PetscObjectTypeCompare((PetscObject)std::get<1>(*ctx)[0], MATTRANSPOSEVIRTUAL, &flg)); 2226 A10 = nullptr; 2227 if (flg) PetscCall(MatTransposeGetMat(std::get<1>(*ctx)[0], &A10)); 2228 else { 2229 PetscBool flg; 2230 2231 PetscCall(PetscObjectTypeCompare((PetscObject)(PetscObject)std::get<1>(*ctx)[0], MATHERMITIANTRANSPOSEVIRTUAL, &flg)); 2232 if (flg) PetscCall(MatHermitianTransposeGetMat(std::get<1>(*ctx)[0], &A10)); 2233 } 2234 if (!A10) { 2235 PetscCall(MatCreateSubMatrices(std::get<1>(*ctx)[0], 1, &sorted, &data->is, MAT_INITIAL_MATRIX, &sub)); 2236 b[1] = sub[0]; 2237 PetscCall(PetscObjectReference((PetscObject)sub[0])); 2238 } 2239 } else { 2240 PetscCall(MatSetOption(A01, MAT_SUBMAT_SINGLEIS, PETSC_TRUE)); 2241 PetscCall(MatCreateSubMatrices(A01, 1, &sorted, &data->is, MAT_INITIAL_MATRIX, &sub)); 2242 if (flg) PetscCall(MatTranspose(*sub, MAT_INITIAL_MATRIX, b + 2)); 2243 else PetscCall(MatHermitianTranspose(*sub, MAT_INITIAL_MATRIX, b + 2)); 2244 } 2245 PetscCall(MatDestroySubMatrices(1, &sub)); 2246 PetscCall(ISDestroy(&sorted)); 2247 n = -1; 2248 PetscTryMethod(data_00->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data_00->levels[0]->pc, &n, nullptr, &ksp)); 2249 PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", n); 2250 PetscCall(KSPGetOperators(ksp[0], subA, subA + 1)); 2251 PetscCall(ISGetLocalSize(data_00->is, &n)); 2252 PetscCheck(n == subA[0]->rmap->n && n == subA[0]->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_hpddm_define_subdomains false", pcpre ? pcpre : "", ((PetscObject)pc)->prefix); 2253 if (A01 || A10) { 2254 if (flg) PetscCall(MatTranspose(b[2], MAT_INITIAL_MATRIX, b + 1)); 2255 else PetscCall(MatHermitianTranspose(b[2], MAT_INITIAL_MATRIX, b + 1)); 2256 } 2257 PetscCall(MatCreateSchurComplement(subA[0], subA[1], b[1], b[2], data->aux, &S)); 2258 PetscCall(MatSchurComplementSetKSP(S, ksp[0])); 2259 PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(data->levels[0]->pc, S, PETSC_TRUE)); /* the subdomain Mat is already known and the input IS of PCASMSetLocalSubdomains() is already sorted */ 2260 PetscTryMethod(data->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data->levels[0]->pc, &n, nullptr, &ksp)); 2261 PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", n); 2262 PetscCall(KSPGetPC(ksp[0], &inner)); 2263 PetscCall(PCSetType(inner, PCSHELL)); /* compute the action of the inverse of the local Schur complement with a PCSHELL */ 2264 b[0] = subA[0]; 2265 b[3] = data->aux; 2266 PetscCall(MatCreateNest(PETSC_COMM_SELF, 2, nullptr, 2, nullptr, b, &N)); /* instead of computing inv(A11 - A10 inv(A00) A01), compute inv([A00, A01; A10, A11]) followed by a partial solution associated to the A11 block */ 2267 PetscCall(PetscObjectDereference((PetscObject)b[1])); 2268 PetscCall(PetscObjectDereference((PetscObject)b[2])); 2269 PetscCall(PCCreate(PETSC_COMM_SELF, &s)); 2270 PetscCall(PCSetOptionsPrefix(s, ((PetscObject)inner)->prefix)); 2271 PetscCall(PCSetOptionsPrefix(inner, nullptr)); 2272 PetscCall(KSPSetSkipPCSetFromOptions(ksp[0], PETSC_TRUE)); 2273 PetscCall(PCSetType(s, PCLU)); 2274 if (PetscDefined(HAVE_MUMPS)) { /* only MATSOLVERMUMPS handles MATNEST, so for the others, e.g., MATSOLVERPETSC or MATSOLVERMKL_PARDISO, convert to plain MATAIJ */ 2275 PetscCall(PCFactorSetMatSolverType(s, MATSOLVERMUMPS)); 2276 } 2277 PetscCall(PCSetFromOptions(s)); 2278 PetscCall(PCFactorGetMatSolverType(s, &type)); 2279 PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg)); 2280 if (flg) { 2281 PetscCall(PCSetOperators(s, N, N)); 2282 PetscCall(PCFactorGetMatrix(s, b)); 2283 PetscCall(MatSetOptionsPrefix(*b, ((PetscObject)s)->prefix)); 2284 n = -1; 2285 PetscCall(PetscOptionsGetInt(nullptr, ((PetscObject)s)->prefix, "-mat_mumps_icntl_26", &n, nullptr)); 2286 if (n == 1) { 2287 PetscCall(MatNestGetISs(N, is, nullptr)); /* allocates a square MatDense of size is[1]->map->n, so one */ 2288 PetscCall(MatFactorSetSchurIS(*b, is[1])); /* needs to be able to deactivate this path when dealing */ 2289 } /* with a large constraint space in order to avoid OOM */ 2290 } else { 2291 PetscCall(MatConvert(N, MATAIJ, MAT_INITIAL_MATRIX, b)); 2292 PetscCall(PCSetOperators(s, N, *b)); 2293 PetscCall(PetscObjectDereference((PetscObject)*b)); 2294 PetscCall(PCFactorGetMatrix(s, b)); /* MATSOLVERMKL_PARDISO cannot compute in PETSc (yet) a partial solution associated to the A11 block, only partial solution associated to the A00 block or full solution */ 2295 } 2296 PetscCall(PetscNew(&p)); 2297 p->first = s; 2298 PetscCall(MatCreateVecs(*b, p->second, p->second + 1)); 2299 PetscCall(PCShellSetContext(inner, p)); 2300 PetscCall(PCShellSetApply(inner, PCApply_Nest)); 2301 PetscCall(PCShellSetView(inner, PCView_Nest)); 2302 PetscCall(PCShellSetDestroy(inner, PCDestroy_Nest)); 2303 PetscCall(PetscObjectDereference((PetscObject)N)); 2304 } 2305 if (!data->levels[0]->scatter) { 2306 PetscCall(MatCreateVecs(P, &xin, nullptr)); 2307 if (ismatis) PetscCall(MatDestroy(&P)); 2308 PetscCall(VecScatterCreate(xin, data->is, data->levels[0]->D, nullptr, &data->levels[0]->scatter)); 2309 PetscCall(VecDestroy(&xin)); 2310 } 2311 if (data->levels[0]->P) { 2312 /* if the pattern is the same and PCSetUp() has previously succeeded, reuse HPDDM buffers and connectivity */ 2313 PetscCall(HPDDM::Schwarz<PetscScalar>::destroy(data->levels[0], pc->setupcalled < 1 || pc->flag == DIFFERENT_NONZERO_PATTERN ? PETSC_TRUE : PETSC_FALSE)); 2314 } 2315 if (!data->levels[0]->P) data->levels[0]->P = new HPDDM::Schwarz<PetscScalar>(); 2316 if (data->log_separate) PetscCall(PetscLogEventBegin(PC_HPDDM_SetUp[0], data->levels[0]->ksp, nullptr, nullptr, nullptr)); 2317 else PetscCall(PetscLogEventBegin(PC_HPDDM_Strc, data->levels[0]->ksp, nullptr, nullptr, nullptr)); 2318 /* HPDDM internal data structure */ 2319 PetscCall(data->levels[0]->P->structure(loc, data->is, !ctx ? sub[0] : nullptr, ismatis ? C : data->aux, data->levels)); 2320 if (!data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_Strc, data->levels[0]->ksp, nullptr, nullptr, nullptr)); 2321 /* matrix pencil of the generalized eigenvalue problem on the overlap (GenEO) */ 2322 if (!ctx) { 2323 if (data->deflation || overlap != -1) weighted = data->aux; 2324 else if (!data->B) { 2325 PetscBool cmp[2]; 2326 PetscCall(MatDuplicate(sub[0], MAT_COPY_VALUES, &weighted)); 2327 PetscCall(PetscObjectTypeCompare((PetscObject)weighted, MATNORMAL, cmp)); 2328 if (!cmp[0]) PetscCall(PetscObjectTypeCompare((PetscObject)weighted, MATNORMALHERMITIAN, cmp + 1)); 2329 else cmp[1] = PETSC_FALSE; 2330 if (!cmp[0] && !cmp[1]) PetscCall(MatDiagonalScale(weighted, data->levels[0]->D, data->levels[0]->D)); 2331 else { /* MATNORMAL applies MatDiagonalScale() in a matrix-free fashion, not what is needed since this won't be passed to SLEPc during the eigensolve */ 2332 if (cmp[0]) PetscCall(MatNormalGetMat(weighted, &data->B)); 2333 else PetscCall(MatNormalHermitianGetMat(weighted, &data->B)); 2334 PetscCall(MatDiagonalScale(data->B, nullptr, data->levels[0]->D)); 2335 data->B = nullptr; 2336 flg = PETSC_FALSE; 2337 } 2338 /* neither MatDuplicate() nor MatDiagonaleScale() handles the symmetry options, so propagate the options explicitly */ 2339 /* only useful for -mat_type baij -pc_hpddm_levels_1_st_pc_type cholesky (no problem with MATAIJ or MATSBAIJ) */ 2340 PetscCall(MatPropagateSymmetryOptions(sub[0], weighted)); 2341 } else weighted = data->B; 2342 } else weighted = nullptr; 2343 /* SLEPc is used inside the loaded symbol */ 2344 PetscCall((*loadedSym)(data->levels[0]->P, data->is, ismatis ? C : (algebraic && !block && overlap == -1 ? sub[0] : (!ctx ? data->aux : S)), weighted, data->B, initial, data->levels)); 2345 if (!ctx && data->share && overlap == -1) { 2346 Mat st[2]; 2347 PetscCall(KSPGetOperators(ksp[0], st, st + 1)); 2348 PetscCall(MatCopy(subA[0], st[0], structure)); 2349 if (subA[1] != subA[0] || st[1] != st[0]) PetscCall(MatCopy(subA[1], st[1], SAME_NONZERO_PATTERN)); 2350 } 2351 if (data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_SetUp[0], data->levels[0]->ksp, nullptr, nullptr, nullptr)); 2352 if (ismatis) PetscCall(MatISGetLocalMat(C, &N)); 2353 else N = data->aux; 2354 if (!ctx) P = sub[0]; 2355 else P = S; 2356 /* going through the grid hierarchy */ 2357 for (n = 1; n < data->N; ++n) { 2358 if (data->log_separate) PetscCall(PetscLogEventBegin(PC_HPDDM_SetUp[n], data->levels[n]->ksp, nullptr, nullptr, nullptr)); 2359 /* method composed in the loaded symbol since there, SLEPc is used as well */ 2360 PetscTryMethod(data->levels[0]->ksp, "PCHPDDMSetUp_Private_C", (Mat *, Mat *, PetscInt, PetscInt *const, PC_HPDDM_Level **const), (&P, &N, n, &data->N, data->levels)); 2361 if (data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_SetUp[n], data->levels[n]->ksp, nullptr, nullptr, nullptr)); 2362 } 2363 /* reset to NULL to avoid any faulty use */ 2364 PetscCall(PetscObjectComposeFunction((PetscObject)data->levels[0]->ksp, "PCHPDDMSetUp_Private_C", nullptr)); 2365 if (!ismatis) PetscCall(PetscObjectComposeFunction((PetscObject)pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_C", nullptr)); 2366 else PetscCall(PetscObjectDereference((PetscObject)C)); /* matching PetscObjectReference() above */ 2367 for (n = 0; n < data->N - 1; ++n) 2368 if (data->levels[n]->P) { 2369 /* HPDDM internal work buffers */ 2370 data->levels[n]->P->setBuffer(); 2371 data->levels[n]->P->super::start(); 2372 } 2373 if (ismatis || !subdomains) PetscCall(PCHPDDMDestroySubMatrices_Private(PetscBool3ToBool(data->Neumann), PetscBool(algebraic && !block && overlap == -1), sub)); 2374 if (ismatis) data->is = nullptr; 2375 for (n = 0; n < data->N - 1 + (reused > 0); ++n) { 2376 if (data->levels[n]->P) { 2377 PC spc; 2378 2379 /* force the PC to be PCSHELL to do the coarse grid corrections */ 2380 PetscCall(KSPSetSkipPCSetFromOptions(data->levels[n]->ksp, PETSC_TRUE)); 2381 PetscCall(KSPGetPC(data->levels[n]->ksp, &spc)); 2382 PetscCall(PCSetType(spc, PCSHELL)); 2383 PetscCall(PCShellSetContext(spc, data->levels[n])); 2384 PetscCall(PCShellSetSetUp(spc, PCSetUp_HPDDMShell)); 2385 PetscCall(PCShellSetApply(spc, PCApply_HPDDMShell)); 2386 PetscCall(PCShellSetMatApply(spc, PCMatApply_HPDDMShell)); 2387 if (ctx && n == 0) { 2388 Mat Amat, Pmat; 2389 PetscInt m, M; 2390 std::tuple<Mat, VecScatter, Vec[2]> *ctx; 2391 2392 PetscCall(KSPGetOperators(data->levels[n]->ksp, nullptr, &Pmat)); 2393 PetscCall(MatGetLocalSize(Pmat, &m, nullptr)); 2394 PetscCall(MatGetSize(Pmat, &M, nullptr)); 2395 PetscCall(PetscNew(&ctx)); 2396 std::get<0>(*ctx) = S; 2397 std::get<1>(*ctx) = data->levels[n]->scatter; 2398 PetscCall(MatCreateShell(PetscObjectComm((PetscObject)data->levels[n]->ksp), m, m, M, M, ctx, &Amat)); 2399 PetscCall(MatShellSetOperation(Amat, MATOP_MULT, (void (*)(void))MatMult_Schur<false>)); 2400 PetscCall(MatShellSetOperation(Amat, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMult_Schur<true>)); 2401 PetscCall(MatShellSetOperation(Amat, MATOP_DESTROY, (void (*)(void))MatDestroy_Schur)); 2402 PetscCall(MatCreateVecs(S, std::get<2>(*ctx), std::get<2>(*ctx) + 1)); 2403 PetscCall(KSPSetOperators(data->levels[n]->ksp, Amat, Pmat)); 2404 PetscCall(PetscObjectDereference((PetscObject)Amat)); 2405 } 2406 PetscCall(PCShellSetDestroy(spc, PCDestroy_HPDDMShell)); 2407 if (!data->levels[n]->pc) PetscCall(PCCreate(PetscObjectComm((PetscObject)data->levels[n]->ksp), &data->levels[n]->pc)); 2408 if (n < reused) { 2409 PetscCall(PCSetReusePreconditioner(spc, PETSC_TRUE)); 2410 PetscCall(PCSetReusePreconditioner(data->levels[n]->pc, PETSC_TRUE)); 2411 } 2412 PetscCall(PCSetUp(spc)); 2413 } 2414 } 2415 if (ctx) PetscCall(MatDestroy(&S)); 2416 if (overlap == -1) PetscCall(PetscObjectComposeFunction((PetscObject)pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_Private_C", nullptr)); 2417 } else flg = reused ? PETSC_FALSE : PETSC_TRUE; 2418 if (!ismatis && subdomains) { 2419 if (flg) PetscCall(KSPGetPC(data->levels[0]->ksp, &inner)); 2420 else inner = data->levels[0]->pc; 2421 if (inner) { 2422 if (!inner->setupcalled) PetscCall(PCSetType(inner, PCASM)); 2423 PetscCall(PCSetFromOptions(inner)); 2424 PetscCall(PetscStrcmp(((PetscObject)inner)->type_name, PCASM, &flg)); 2425 if (flg) { 2426 if (!inner->setupcalled) { /* evaluates to PETSC_FALSE when -pc_hpddm_block_splitting */ 2427 IS sorted; /* PCASM will sort the input IS, duplicate it to return an unmodified (PCHPDDM) input IS */ 2428 PetscCall(ISDuplicate(is[0], &sorted)); 2429 PetscCall(PCASMSetLocalSubdomains(inner, 1, &sorted, &loc)); 2430 PetscCall(PetscObjectDereference((PetscObject)sorted)); 2431 } 2432 if (!PetscBool3ToBool(data->Neumann) && data->N > 1) { /* subdomain matrices are already created for the eigenproblem, reuse them for the fine-level PC */ 2433 PetscCall(PCHPDDMPermute_Private(*is, nullptr, nullptr, sub[0], &P, nullptr)); 2434 PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(inner, P, algebraic)); 2435 PetscCall(PetscObjectDereference((PetscObject)P)); 2436 } 2437 } 2438 } 2439 if (data->N > 1) { 2440 if (overlap != 1) PetscCall(PCHPDDMDestroySubMatrices_Private(PetscBool3ToBool(data->Neumann), PetscBool(algebraic && !block && overlap == -1), sub)); 2441 if (overlap == 1) PetscCall(MatDestroy(subA)); 2442 } 2443 } 2444 PetscCall(ISDestroy(&loc)); 2445 } else data->N = 1 + reused; /* enforce this value to 1 + reused if there is no way to build another level */ 2446 if (requested != data->N + reused) { 2447 PetscCall(PetscInfo(pc, "%" PetscInt_FMT " levels requested, only %" PetscInt_FMT " built + %" PetscInt_FMT " reused. Options for level(s) > %" PetscInt_FMT ", including -%spc_hpddm_coarse_ will not be taken into account\n", requested, data->N, reused, 2448 data->N, pcpre ? pcpre : "")); 2449 PetscCall(PetscInfo(pc, "It is best to tune parameters, e.g., a higher value for -%spc_hpddm_levels_%" PetscInt_FMT "_eps_threshold so that at least one local deflation vector will be selected\n", pcpre ? pcpre : "", data->N)); 2450 /* cannot use PCDestroy_HPDDMShell() because PCSHELL not set for unassembled levels */ 2451 for (n = data->N - 1; n < requested - 1; ++n) { 2452 if (data->levels[n]->P) { 2453 PetscCall(HPDDM::Schwarz<PetscScalar>::destroy(data->levels[n], PETSC_TRUE)); 2454 PetscCall(VecDestroyVecs(1, &data->levels[n]->v[0])); 2455 PetscCall(VecDestroyVecs(2, &data->levels[n]->v[1])); 2456 PetscCall(MatDestroy(data->levels[n]->V)); 2457 PetscCall(MatDestroy(data->levels[n]->V + 1)); 2458 PetscCall(MatDestroy(data->levels[n]->V + 2)); 2459 PetscCall(VecDestroy(&data->levels[n]->D)); 2460 PetscCall(VecScatterDestroy(&data->levels[n]->scatter)); 2461 } 2462 } 2463 if (reused) { 2464 for (n = reused; n < PETSC_PCHPDDM_MAXLEVELS && data->levels[n]; ++n) { 2465 PetscCall(KSPDestroy(&data->levels[n]->ksp)); 2466 PetscCall(PCDestroy(&data->levels[n]->pc)); 2467 } 2468 } 2469 PetscCheck(!PetscDefined(USE_DEBUG), PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "%" PetscInt_FMT " levels requested, only %" PetscInt_FMT " built + %" PetscInt_FMT " reused. Options for level(s) > %" PetscInt_FMT ", including -%spc_hpddm_coarse_ will not be taken into account. It is best to tune parameters, e.g., a higher value for -%spc_hpddm_levels_%" PetscInt_FMT "_eps_threshold so that at least one local deflation vector will be selected. If you don't want this to error out, compile --with-debugging=0", requested, 2470 data->N, reused, data->N, pcpre ? pcpre : "", pcpre ? pcpre : "", data->N); 2471 } 2472 /* these solvers are created after PCSetFromOptions() is called */ 2473 if (pc->setfromoptionscalled) { 2474 for (n = 0; n < data->N; ++n) { 2475 if (data->levels[n]->ksp) PetscCall(KSPSetFromOptions(data->levels[n]->ksp)); 2476 if (data->levels[n]->pc) PetscCall(PCSetFromOptions(data->levels[n]->pc)); 2477 } 2478 pc->setfromoptionscalled = 0; 2479 } 2480 data->N += reused; 2481 if (data->share && swap) { 2482 /* swap back pointers so that variables follow the user-provided numbering */ 2483 std::swap(C, data->aux); 2484 std::swap(uis, data->is); 2485 PetscCall(MatDestroy(&C)); 2486 PetscCall(ISDestroy(&uis)); 2487 } 2488 if (algebraic) PetscCall(MatDestroy(&data->aux)); 2489 if (unsorted && unsorted != is[0]) { 2490 PetscCall(ISCopy(unsorted, data->is)); 2491 PetscCall(ISDestroy(&unsorted)); 2492 } 2493 #if PetscDefined(USE_DEBUG) 2494 PetscCheck((data->is && dis) || (!data->is && !dis), PETSC_COMM_SELF, PETSC_ERR_PLIB, "An IS pointer is NULL but not the other: input IS pointer (%p) v. output IS pointer (%p)", (void *)dis, (void *)data->is); 2495 if (data->is) { 2496 PetscCall(ISEqualUnsorted(data->is, dis, &flg)); 2497 PetscCall(ISDestroy(&dis)); 2498 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Input IS and output IS are not equal"); 2499 } 2500 PetscCheck((data->aux && daux) || (!data->aux && !daux), PETSC_COMM_SELF, PETSC_ERR_PLIB, "A Mat pointer is NULL but not the other: input Mat pointer (%p) v. output Mat pointer (%p)", (void *)daux, (void *)data->aux); 2501 if (data->aux) { 2502 PetscCall(MatMultEqual(data->aux, daux, 20, &flg)); 2503 PetscCall(MatDestroy(&daux)); 2504 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Input Mat and output Mat are not equal"); 2505 } 2506 #endif 2507 PetscFunctionReturn(PETSC_SUCCESS); 2508 } 2509 2510 /*@ 2511 PCHPDDMSetCoarseCorrectionType - Sets the coarse correction type. 2512 2513 Collective 2514 2515 Input Parameters: 2516 + pc - preconditioner context 2517 - type - `PC_HPDDM_COARSE_CORRECTION_DEFLATED`, `PC_HPDDM_COARSE_CORRECTION_ADDITIVE`, or `PC_HPDDM_COARSE_CORRECTION_BALANCED` 2518 2519 Options Database Key: 2520 . -pc_hpddm_coarse_correction <deflated, additive, balanced> - type of coarse correction to apply 2521 2522 Level: intermediate 2523 2524 .seealso: `PCHPDDMGetCoarseCorrectionType()`, `PCHPDDM`, `PCHPDDMCoarseCorrectionType` 2525 @*/ 2526 PetscErrorCode PCHPDDMSetCoarseCorrectionType(PC pc, PCHPDDMCoarseCorrectionType type) 2527 { 2528 PetscFunctionBegin; 2529 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 2530 PetscValidLogicalCollectiveEnum(pc, type, 2); 2531 PetscTryMethod(pc, "PCHPDDMSetCoarseCorrectionType_C", (PC, PCHPDDMCoarseCorrectionType), (pc, type)); 2532 PetscFunctionReturn(PETSC_SUCCESS); 2533 } 2534 2535 /*@ 2536 PCHPDDMGetCoarseCorrectionType - Gets the coarse correction type. 2537 2538 Input Parameter: 2539 . pc - preconditioner context 2540 2541 Output Parameter: 2542 . type - `PC_HPDDM_COARSE_CORRECTION_DEFLATED`, `PC_HPDDM_COARSE_CORRECTION_ADDITIVE`, or `PC_HPDDM_COARSE_CORRECTION_BALANCED` 2543 2544 Level: intermediate 2545 2546 .seealso: `PCHPDDMSetCoarseCorrectionType()`, `PCHPDDM`, `PCHPDDMCoarseCorrectionType` 2547 @*/ 2548 PetscErrorCode PCHPDDMGetCoarseCorrectionType(PC pc, PCHPDDMCoarseCorrectionType *type) 2549 { 2550 PetscFunctionBegin; 2551 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 2552 if (type) { 2553 PetscAssertPointer(type, 2); 2554 PetscUseMethod(pc, "PCHPDDMGetCoarseCorrectionType_C", (PC, PCHPDDMCoarseCorrectionType *), (pc, type)); 2555 } 2556 PetscFunctionReturn(PETSC_SUCCESS); 2557 } 2558 2559 static PetscErrorCode PCHPDDMSetCoarseCorrectionType_HPDDM(PC pc, PCHPDDMCoarseCorrectionType type) 2560 { 2561 PC_HPDDM *data = (PC_HPDDM *)pc->data; 2562 2563 PetscFunctionBegin; 2564 PetscCheck(type >= 0 && type <= 2, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown PCHPDDMCoarseCorrectionType %d", type); 2565 data->correction = type; 2566 PetscFunctionReturn(PETSC_SUCCESS); 2567 } 2568 2569 static PetscErrorCode PCHPDDMGetCoarseCorrectionType_HPDDM(PC pc, PCHPDDMCoarseCorrectionType *type) 2570 { 2571 PC_HPDDM *data = (PC_HPDDM *)pc->data; 2572 2573 PetscFunctionBegin; 2574 *type = data->correction; 2575 PetscFunctionReturn(PETSC_SUCCESS); 2576 } 2577 2578 /*@ 2579 PCHPDDMSetSTShareSubKSP - Sets whether the `KSP` in SLEPc `ST` and the fine-level subdomain solver should be shared. 2580 2581 Input Parameters: 2582 + pc - preconditioner context 2583 - share - whether the `KSP` should be shared or not 2584 2585 Note: 2586 This is not the same as `PCSetReusePreconditioner()`. Given certain conditions (visible using -info), a symbolic factorization can be skipped 2587 when using a subdomain `PCType` such as `PCLU` or `PCCHOLESKY`. 2588 2589 Level: advanced 2590 2591 .seealso: `PCHPDDM`, `PCHPDDMGetSTShareSubKSP()` 2592 @*/ 2593 PetscErrorCode PCHPDDMSetSTShareSubKSP(PC pc, PetscBool share) 2594 { 2595 PetscFunctionBegin; 2596 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 2597 PetscTryMethod(pc, "PCHPDDMSetSTShareSubKSP_C", (PC, PetscBool), (pc, share)); 2598 PetscFunctionReturn(PETSC_SUCCESS); 2599 } 2600 2601 /*@ 2602 PCHPDDMGetSTShareSubKSP - Gets whether the `KSP` in SLEPc `ST` and the fine-level subdomain solver is shared. 2603 2604 Input Parameter: 2605 . pc - preconditioner context 2606 2607 Output Parameter: 2608 . share - whether the `KSP` is shared or not 2609 2610 Note: 2611 This is not the same as `PCGetReusePreconditioner()`. The return value is unlikely to be true, but when it is, a symbolic factorization can be skipped 2612 when using a subdomain `PCType` such as `PCLU` or `PCCHOLESKY`. 2613 2614 Level: advanced 2615 2616 .seealso: `PCHPDDM`, `PCHPDDMSetSTShareSubKSP()` 2617 @*/ 2618 PetscErrorCode PCHPDDMGetSTShareSubKSP(PC pc, PetscBool *share) 2619 { 2620 PetscFunctionBegin; 2621 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 2622 if (share) { 2623 PetscAssertPointer(share, 2); 2624 PetscUseMethod(pc, "PCHPDDMGetSTShareSubKSP_C", (PC, PetscBool *), (pc, share)); 2625 } 2626 PetscFunctionReturn(PETSC_SUCCESS); 2627 } 2628 2629 static PetscErrorCode PCHPDDMSetSTShareSubKSP_HPDDM(PC pc, PetscBool share) 2630 { 2631 PC_HPDDM *data = (PC_HPDDM *)pc->data; 2632 2633 PetscFunctionBegin; 2634 data->share = share; 2635 PetscFunctionReturn(PETSC_SUCCESS); 2636 } 2637 2638 static PetscErrorCode PCHPDDMGetSTShareSubKSP_HPDDM(PC pc, PetscBool *share) 2639 { 2640 PC_HPDDM *data = (PC_HPDDM *)pc->data; 2641 2642 PetscFunctionBegin; 2643 *share = data->share; 2644 PetscFunctionReturn(PETSC_SUCCESS); 2645 } 2646 2647 /*@ 2648 PCHPDDMSetDeflationMat - Sets the deflation space used to assemble a coarser operator. 2649 2650 Input Parameters: 2651 + pc - preconditioner context 2652 . is - index set of the local deflation matrix 2653 - U - deflation sequential matrix stored as a `MATSEQDENSE` 2654 2655 Level: advanced 2656 2657 .seealso: `PCHPDDM`, `PCDeflationSetSpace()`, `PCMGSetRestriction()` 2658 @*/ 2659 PetscErrorCode PCHPDDMSetDeflationMat(PC pc, IS is, Mat U) 2660 { 2661 PetscFunctionBegin; 2662 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 2663 PetscValidHeaderSpecific(is, IS_CLASSID, 2); 2664 PetscValidHeaderSpecific(U, MAT_CLASSID, 3); 2665 PetscTryMethod(pc, "PCHPDDMSetDeflationMat_C", (PC, IS, Mat), (pc, is, U)); 2666 PetscFunctionReturn(PETSC_SUCCESS); 2667 } 2668 2669 static PetscErrorCode PCHPDDMSetDeflationMat_HPDDM(PC pc, IS is, Mat U) 2670 { 2671 PetscFunctionBegin; 2672 PetscCall(PCHPDDMSetAuxiliaryMat_Private(pc, is, U, PETSC_TRUE)); 2673 PetscFunctionReturn(PETSC_SUCCESS); 2674 } 2675 2676 PetscErrorCode HPDDMLoadDL_Private(PetscBool *found) 2677 { 2678 PetscBool flg; 2679 char lib[PETSC_MAX_PATH_LEN], dlib[PETSC_MAX_PATH_LEN], dir[PETSC_MAX_PATH_LEN]; 2680 2681 PetscFunctionBegin; 2682 PetscAssertPointer(found, 1); 2683 PetscCall(PetscStrncpy(dir, "${PETSC_LIB_DIR}", sizeof(dir))); 2684 PetscCall(PetscOptionsGetString(nullptr, nullptr, "-hpddm_dir", dir, sizeof(dir), nullptr)); 2685 PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/libhpddm_petsc", dir)); 2686 PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found)); 2687 #if defined(SLEPC_LIB_DIR) /* this variable is passed during SLEPc ./configure when PETSc has not been configured */ 2688 if (!*found) { /* with --download-hpddm since slepcconf.h is not yet built (and thus can't be included) */ 2689 PetscCall(PetscStrncpy(dir, HPDDM_STR(SLEPC_LIB_DIR), sizeof(dir))); 2690 PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/libhpddm_petsc", dir)); 2691 PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found)); 2692 } 2693 #endif 2694 if (!*found) { /* probable options for this to evaluate to PETSC_TRUE: system inconsistency (libhpddm_petsc moved by user?) or PETSc configured without --download-slepc */ 2695 PetscCall(PetscOptionsGetenv(PETSC_COMM_SELF, "SLEPC_DIR", dir, sizeof(dir), &flg)); 2696 if (flg) { /* if both PETSc and SLEPc are configured with --download-hpddm but PETSc has been configured without --download-slepc, one must ensure that libslepc is loaded before libhpddm_petsc */ 2697 PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/lib/libslepc", dir)); 2698 PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found)); 2699 PetscCheck(*found, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s not found but SLEPC_DIR=%s", lib, dir); 2700 PetscCall(PetscDLLibraryAppend(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, dlib)); 2701 PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/lib/libhpddm_petsc", dir)); /* libhpddm_petsc is always in the same directory as libslepc */ 2702 PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found)); 2703 } 2704 } 2705 PetscCheck(*found, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s not found", lib); 2706 PetscCall(PetscDLLibraryAppend(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, dlib)); 2707 PetscFunctionReturn(PETSC_SUCCESS); 2708 } 2709 2710 /*MC 2711 PCHPDDM - Interface with the HPDDM library. 2712 2713 This `PC` may be used to build multilevel spectral domain decomposition methods based on the GenEO framework [2011, 2019]. It may be viewed as an alternative to spectral 2714 AMGe or `PCBDDC` with adaptive selection of constraints. The interface is explained in details in [2021] (see references below) 2715 2716 The matrix to be preconditioned (Pmat) may be unassembled (`MATIS`), assembled (`MATAIJ`, `MATBAIJ`, or `MATSBAIJ`), hierarchical (`MATHTOOL`), or `MATNORMAL`. 2717 2718 For multilevel preconditioning, when using an assembled or hierarchical Pmat, one must provide an auxiliary local `Mat` (unassembled local operator for GenEO) using 2719 `PCHPDDMSetAuxiliaryMat()`. Calling this routine is not needed when using a `MATIS` Pmat, assembly is done internally using `MatConvert()`. 2720 2721 Options Database Keys: 2722 + -pc_hpddm_define_subdomains <true, default=false> - on the finest level, calls `PCASMSetLocalSubdomains()` with the `IS` supplied in `PCHPDDMSetAuxiliaryMat()` 2723 (not relevant with an unassembled Pmat) 2724 . -pc_hpddm_has_neumann <true, default=false> - on the finest level, informs the `PC` that the local Neumann matrix is supplied in `PCHPDDMSetAuxiliaryMat()` 2725 - -pc_hpddm_coarse_correction <type, default=deflated> - determines the `PCHPDDMCoarseCorrectionType` when calling `PCApply()` 2726 2727 Options for subdomain solvers, subdomain eigensolvers (for computing deflation vectors), and the coarse solver can be set using the following options database prefixes. 2728 .vb 2729 -pc_hpddm_levels_%d_pc_ 2730 -pc_hpddm_levels_%d_ksp_ 2731 -pc_hpddm_levels_%d_eps_ 2732 -pc_hpddm_levels_%d_p 2733 -pc_hpddm_levels_%d_mat_type 2734 -pc_hpddm_coarse_ 2735 -pc_hpddm_coarse_p 2736 -pc_hpddm_coarse_mat_type 2737 -pc_hpddm_coarse_mat_filter 2738 .ve 2739 2740 E.g., -pc_hpddm_levels_1_sub_pc_type lu -pc_hpddm_levels_1_eps_nev 10 -pc_hpddm_levels_2_p 4 -pc_hpddm_levels_2_sub_pc_type lu -pc_hpddm_levels_2_eps_nev 10 2741 -pc_hpddm_coarse_p 2 -pc_hpddm_coarse_mat_type baij will use 10 deflation vectors per subdomain on the fine "level 1", 2742 aggregate the fine subdomains into 4 "level 2" subdomains, then use 10 deflation vectors per subdomain on "level 2", 2743 and assemble the coarse matrix (of dimension 4 x 10 = 40) on two processes as a `MATBAIJ` (default is `MATSBAIJ`). 2744 2745 In order to activate a "level N+1" coarse correction, it is mandatory to call -pc_hpddm_levels_N_eps_nev <nu> or -pc_hpddm_levels_N_eps_threshold <val>. The default -pc_hpddm_coarse_p value is 1, meaning that the coarse operator is aggregated on a single process. 2746 2747 This preconditioner requires that you build PETSc with SLEPc (``--download-slepc``). By default, the underlying concurrent eigenproblems 2748 are solved using SLEPc shift-and-invert spectral transformation. This is usually what gives the best performance for GenEO, cf. [2011, 2013]. As 2749 stated above, SLEPc options are available through -pc_hpddm_levels_%d_, e.g., -pc_hpddm_levels_1_eps_type arpack -pc_hpddm_levels_1_eps_nev 10 2750 -pc_hpddm_levels_1_st_type sinvert. There are furthermore three options related to the (subdomain-wise local) eigensolver that are not described in 2751 SLEPc documentation since they are specific to `PCHPDDM`. 2752 .vb 2753 -pc_hpddm_levels_1_st_share_sub_ksp 2754 -pc_hpddm_levels_%d_eps_threshold 2755 -pc_hpddm_levels_1_eps_use_inertia 2756 .ve 2757 2758 The first option from the list only applies to the fine-level eigensolver, see `PCHPDDMSetSTShareSubKSP()`. The second option from the list is 2759 used to filter eigenmodes retrieved after convergence of `EPSSolve()` at "level N" such that eigenvectors used to define a "level N+1" coarse 2760 correction are associated to eigenvalues whose magnitude are lower or equal than -pc_hpddm_levels_N_eps_threshold. When using an `EPS` which cannot 2761 determine a priori the proper -pc_hpddm_levels_N_eps_nev such that all wanted eigenmodes are retrieved, it is possible to get an estimation of the 2762 correct value using the third option from the list, -pc_hpddm_levels_1_eps_use_inertia, see `MatGetInertia()`. In that case, there is no need 2763 to supply -pc_hpddm_levels_1_eps_nev. This last option also only applies to the fine-level (N = 1) eigensolver. 2764 2765 References: 2766 + 2011 - A robust two-level domain decomposition preconditioner for systems of PDEs. Spillane, Dolean, Hauret, Nataf, Pechstein, and Scheichl. Comptes Rendus Mathematique. 2767 . 2013 - Scalable domain decomposition preconditioners for heterogeneous elliptic problems. Jolivet, Hecht, Nataf, and Prud'homme. SC13. 2768 . 2015 - An introduction to domain decomposition methods: algorithms, theory, and parallel implementation. Dolean, Jolivet, and Nataf. SIAM. 2769 . 2019 - A multilevel Schwarz preconditioner based on a hierarchy of robust coarse spaces. Al Daas, Grigori, Jolivet, and Tournier. SIAM Journal on Scientific Computing. 2770 . 2021 - KSPHPDDM and PCHPDDM: extending PETSc with advanced Krylov methods and robust multilevel overlapping Schwarz preconditioners. Jolivet, Roman, and Zampini. Computer & Mathematics with Applications. 2771 . 2022a - A robust algebraic domain decomposition preconditioner for sparse normal equations. Al Daas, Jolivet, and Scott. SIAM Journal on Scientific Computing. 2772 . 2022b - A robust algebraic multilevel domain decomposition preconditioner for sparse symmetric positive definite matrices. Al Daas and Jolivet. 2773 - 2023 - Recent advances in domain decomposition methods for large-scale saddle point problems. Nataf and Tournier. Comptes Rendus Mecanique. 2774 2775 Level: intermediate 2776 2777 .seealso: `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCHPDDMSetAuxiliaryMat()`, `MATIS`, `PCBDDC`, `PCDEFLATION`, `PCTELESCOPE`, `PCASM`, 2778 `PCHPDDMSetCoarseCorrectionType()`, `PCHPDDMHasNeumannMat()`, `PCHPDDMSetRHSMat()`, `PCHPDDMSetDeflationMat()`, `PCHPDDMSetSTShareSubKSP()`, 2779 `PCHPDDMGetSTShareSubKSP()`, `PCHPDDMGetCoarseCorrectionType()`, `PCHPDDMGetComplexities()` 2780 M*/ 2781 PETSC_EXTERN PetscErrorCode PCCreate_HPDDM(PC pc) 2782 { 2783 PC_HPDDM *data; 2784 PetscBool found; 2785 2786 PetscFunctionBegin; 2787 if (!loadedSym) { 2788 PetscCall(HPDDMLoadDL_Private(&found)); 2789 if (found) PetscCall(PetscDLLibrarySym(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, nullptr, "PCHPDDM_Internal", (void **)&loadedSym)); 2790 } 2791 PetscCheck(loadedSym, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCHPDDM_Internal symbol not found in loaded libhpddm_petsc"); 2792 PetscCall(PetscNew(&data)); 2793 pc->data = data; 2794 data->Neumann = PETSC_BOOL3_UNKNOWN; 2795 pc->ops->reset = PCReset_HPDDM; 2796 pc->ops->destroy = PCDestroy_HPDDM; 2797 pc->ops->setfromoptions = PCSetFromOptions_HPDDM; 2798 pc->ops->setup = PCSetUp_HPDDM; 2799 pc->ops->apply = PCApply_HPDDM; 2800 pc->ops->matapply = PCMatApply_HPDDM; 2801 pc->ops->view = PCView_HPDDM; 2802 pc->ops->presolve = PCPreSolve_HPDDM; 2803 2804 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetAuxiliaryMat_C", PCHPDDMSetAuxiliaryMat_HPDDM)); 2805 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMHasNeumannMat_C", PCHPDDMHasNeumannMat_HPDDM)); 2806 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetRHSMat_C", PCHPDDMSetRHSMat_HPDDM)); 2807 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetCoarseCorrectionType_C", PCHPDDMSetCoarseCorrectionType_HPDDM)); 2808 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetCoarseCorrectionType_C", PCHPDDMGetCoarseCorrectionType_HPDDM)); 2809 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetSTShareSubKSP_C", PCHPDDMSetSTShareSubKSP_HPDDM)); 2810 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetSTShareSubKSP_C", PCHPDDMGetSTShareSubKSP_HPDDM)); 2811 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetDeflationMat_C", PCHPDDMSetDeflationMat_HPDDM)); 2812 PetscFunctionReturn(PETSC_SUCCESS); 2813 } 2814 2815 /*@C 2816 PCHPDDMInitializePackage - This function initializes everything in the `PCHPDDM` package. It is called from `PCInitializePackage()`. 2817 2818 Level: developer 2819 2820 .seealso: `PetscInitialize()` 2821 @*/ 2822 PetscErrorCode PCHPDDMInitializePackage(void) 2823 { 2824 char ename[32]; 2825 PetscInt i; 2826 2827 PetscFunctionBegin; 2828 if (PCHPDDMPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS); 2829 PCHPDDMPackageInitialized = PETSC_TRUE; 2830 PetscCall(PetscRegisterFinalize(PCHPDDMFinalizePackage)); 2831 /* general events registered once during package initialization */ 2832 /* some of these events are not triggered in libpetsc, */ 2833 /* but rather directly in libhpddm_petsc, */ 2834 /* which is in charge of performing the following operations */ 2835 2836 /* domain decomposition structure from Pmat sparsity pattern */ 2837 PetscCall(PetscLogEventRegister("PCHPDDMStrc", PC_CLASSID, &PC_HPDDM_Strc)); 2838 /* Galerkin product, redistribution, and setup (not triggered in libpetsc) */ 2839 PetscCall(PetscLogEventRegister("PCHPDDMPtAP", PC_CLASSID, &PC_HPDDM_PtAP)); 2840 /* Galerkin product with summation, redistribution, and setup (not triggered in libpetsc) */ 2841 PetscCall(PetscLogEventRegister("PCHPDDMPtBP", PC_CLASSID, &PC_HPDDM_PtBP)); 2842 /* next level construction using PtAP and PtBP (not triggered in libpetsc) */ 2843 PetscCall(PetscLogEventRegister("PCHPDDMNext", PC_CLASSID, &PC_HPDDM_Next)); 2844 static_assert(PETSC_PCHPDDM_MAXLEVELS <= 9, "PETSC_PCHPDDM_MAXLEVELS value is too high"); 2845 for (i = 1; i < PETSC_PCHPDDM_MAXLEVELS; ++i) { 2846 PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCHPDDMSetUp L%1" PetscInt_FMT, i)); 2847 /* events during a PCSetUp() at level #i _except_ the assembly */ 2848 /* of the Galerkin operator of the coarser level #(i + 1) */ 2849 PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &PC_HPDDM_SetUp[i - 1])); 2850 PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCHPDDMSolve L%1" PetscInt_FMT, i)); 2851 /* events during a PCApply() at level #i _except_ */ 2852 /* the KSPSolve() of the coarser level #(i + 1) */ 2853 PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &PC_HPDDM_Solve[i - 1])); 2854 } 2855 PetscFunctionReturn(PETSC_SUCCESS); 2856 } 2857 2858 /*@C 2859 PCHPDDMFinalizePackage - This function frees everything from the `PCHPDDM` package. It is called from `PetscFinalize()`. 2860 2861 Level: developer 2862 2863 .seealso: `PetscFinalize()` 2864 @*/ 2865 PetscErrorCode PCHPDDMFinalizePackage(void) 2866 { 2867 PetscFunctionBegin; 2868 PCHPDDMPackageInitialized = PETSC_FALSE; 2869 PetscFunctionReturn(PETSC_SUCCESS); 2870 } 2871 2872 static PetscErrorCode MatMult_Harmonic(Mat A, Vec x, Vec y) 2873 { 2874 Harmonic h; /* [ A_00 A_01 ], furthermore, A_00 = [ A_loc,loc A_loc,ovl ], thus, A_01 = [ ] */ 2875 /* [ A_10 A_11 A_12 ] [ A_ovl,loc A_ovl,ovl ] [ A_ovl,1 ] */ 2876 Vec sub; /* y = A x = R_loc R_0 [ A_00 A_01 ]^-1 R_loc = [ I_loc ] */ 2877 /* [ A_10 A_11 ] R_1^T A_12 x [ ] */ 2878 PetscFunctionBegin; 2879 PetscCall(MatShellGetContext(A, &h)); 2880 PetscCall(VecSet(h->v, 0.0)); 2881 PetscCall(VecGetSubVector(h->v, h->is[0], &sub)); 2882 PetscCall(MatMult(h->A[0], x, sub)); 2883 PetscCall(VecRestoreSubVector(h->v, h->is[0], &sub)); 2884 PetscCall(KSPSolve(h->ksp, h->v, h->v)); 2885 PetscCall(VecISCopy(h->v, h->is[1], SCATTER_REVERSE, y)); 2886 PetscFunctionReturn(PETSC_SUCCESS); 2887 } 2888 2889 static PetscErrorCode MatMultTranspose_Harmonic(Mat A, Vec y, Vec x) 2890 { 2891 Harmonic h; /* x = A^T y = [ A_00 A_01 ]^-T R_0^T R_loc^T y */ 2892 Vec sub; /* A_12^T R_1 [ A_10 A_11 ] */ 2893 2894 PetscFunctionBegin; 2895 PetscCall(MatShellGetContext(A, &h)); 2896 PetscCall(VecSet(h->v, 0.0)); 2897 PetscCall(VecISCopy(h->v, h->is[1], SCATTER_FORWARD, y)); 2898 PetscCall(KSPSolveTranspose(h->ksp, h->v, h->v)); 2899 PetscCall(VecGetSubVector(h->v, h->is[0], &sub)); 2900 PetscCall(MatMultTranspose(h->A[0], sub, x)); 2901 PetscCall(VecRestoreSubVector(h->v, h->is[0], &sub)); 2902 PetscFunctionReturn(PETSC_SUCCESS); 2903 } 2904 2905 static PetscErrorCode MatProduct_AB_Harmonic(Mat S, Mat X, Mat Y, void *) 2906 { 2907 Harmonic h; 2908 Mat A, B; 2909 Vec a, b; 2910 2911 PetscFunctionBegin; 2912 PetscCall(MatShellGetContext(S, &h)); 2913 PetscCall(MatMatMult(h->A[0], X, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &A)); 2914 PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, h->ksp->pc->mat->rmap->n, A->cmap->n, nullptr, &B)); 2915 for (PetscInt i = 0; i < A->cmap->n; ++i) { 2916 PetscCall(MatDenseGetColumnVecRead(A, i, &a)); 2917 PetscCall(MatDenseGetColumnVecWrite(B, i, &b)); 2918 PetscCall(VecISCopy(b, h->is[0], SCATTER_FORWARD, a)); 2919 PetscCall(MatDenseRestoreColumnVecWrite(B, i, &b)); 2920 PetscCall(MatDenseRestoreColumnVecRead(A, i, &a)); 2921 } 2922 PetscCall(MatDestroy(&A)); 2923 PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, h->ksp->pc->mat->rmap->n, B->cmap->n, nullptr, &A)); 2924 PetscCall(KSPMatSolve(h->ksp, B, A)); 2925 PetscCall(MatDestroy(&B)); 2926 for (PetscInt i = 0; i < A->cmap->n; ++i) { 2927 PetscCall(MatDenseGetColumnVecRead(A, i, &a)); 2928 PetscCall(MatDenseGetColumnVecWrite(Y, i, &b)); 2929 PetscCall(VecISCopy(a, h->is[1], SCATTER_REVERSE, b)); 2930 PetscCall(MatDenseRestoreColumnVecWrite(Y, i, &b)); 2931 PetscCall(MatDenseRestoreColumnVecRead(A, i, &a)); 2932 } 2933 PetscCall(MatDestroy(&A)); 2934 PetscFunctionReturn(PETSC_SUCCESS); 2935 } 2936 2937 static PetscErrorCode MatDestroy_Harmonic(Mat A) 2938 { 2939 Harmonic h; 2940 2941 PetscFunctionBegin; 2942 PetscCall(MatShellGetContext(A, &h)); 2943 for (PetscInt i = 0; i < (h->A[1] ? 5 : 3); ++i) PetscCall(ISDestroy(h->is + i)); 2944 PetscCall(PetscFree(h->is)); 2945 PetscCall(VecDestroy(&h->v)); 2946 for (PetscInt i = 0; i < 2; ++i) PetscCall(MatDestroy(h->A + i)); 2947 PetscCall(PetscFree(h->A)); 2948 PetscCall(KSPDestroy(&h->ksp)); 2949 PetscCall(PetscFree(h)); 2950 PetscFunctionReturn(PETSC_SUCCESS); 2951 } 2952