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