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