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