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