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