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