xref: /petsc/src/ksp/ksp/impls/hpddm/hpddm.cxx (revision 92683e95eee7934b252f434bd80477b3f5ea99c4)
1 #define HPDDM_MIXED_PRECISION 1
2 #include <petsc/private/petschpddm.h> /*I "petscksp.h" I*/
3 
4 const char *const KSPHPDDMTypes[]          = {KSPGMRES, "bgmres", KSPCG, "bcg", "gcrodr", "bgcrodr", "bfbcg", KSPPREONLY};
5 const char *const HPDDMOrthogonalization[] = {"cgs", "mgs"};
6 const char *const HPDDMQR[]                = {"cholqr", "cgs", "mgs"};
7 const char *const HPDDMVariant[]           = {"left", "right", "flexible"};
8 const char *const HPDDMRecycleTarget[]     = {"SM", "LM", "SR", "LR", "SI", "LI"};
9 const char *const HPDDMRecycleStrategy[]   = {"A", "B"};
10 
11 PetscBool  HPDDMCite       = PETSC_FALSE;
12 const char HPDDMCitation[] = "@article{jolivet2020petsc,\n"
13                              "  Author = {Jolivet, Pierre and Roman, Jose E. and Zampini, Stefano},\n"
14                              "  Title = {{KSPHPDDM} and {PCHPDDM}: Extending {PETSc} with Robust Overlapping {Schwarz} Preconditioners and Advanced {Krylov} Methods},\n"
15                              "  Year = {2021},\n"
16                              "  Publisher = {Elsevier},\n"
17                              "  Journal = {Computer \\& Mathematics with Applications},\n"
18                              "  Volume = {84},\n"
19                              "  Pages = {277--295},\n"
20                              "  Url = {https://github.com/prj-/jolivet2020petsc}\n"
21                              "}\n";
22 
23 #if PetscDefined(HAVE_SLEPC) && PetscDefined(HAVE_DYNAMIC_LIBRARIES) && PetscDefined(USE_SHARED_LIBRARIES)
24 static PetscBool loadedDL = PETSC_FALSE;
25 #endif
26 
KSPSetFromOptions_HPDDM(KSP ksp,PetscOptionItems PetscOptionsObject)27 static PetscErrorCode KSPSetFromOptions_HPDDM(KSP ksp, PetscOptionItems PetscOptionsObject)
28 {
29   KSP_HPDDM  *data = (KSP_HPDDM *)ksp->data;
30   PetscInt    i, j;
31   PetscMPIInt size;
32 
33   PetscFunctionBegin;
34   PetscOptionsHeadBegin(PetscOptionsObject, "KSPHPDDM options, cf. https://github.com/hpddm/hpddm");
35   i = (data->cntl[0] == static_cast<char>(PETSC_DECIDE) ? HPDDM_KRYLOV_METHOD_GMRES : data->cntl[0]);
36   PetscCall(PetscOptionsEList("-ksp_hpddm_type", "Type of Krylov method", "KSPHPDDMGetType", KSPHPDDMTypes, PETSC_STATIC_ARRAY_LENGTH(KSPHPDDMTypes), KSPHPDDMTypes[HPDDM_KRYLOV_METHOD_GMRES], &i, nullptr));
37   if (i == PETSC_STATIC_ARRAY_LENGTH(KSPHPDDMTypes) - 1) i = HPDDM_KRYLOV_METHOD_NONE; /* need to shift the value since HPDDM_KRYLOV_METHOD_RICHARDSON is not registered in PETSc */
38   data->cntl[0] = i;
39   PetscCall(PetscOptionsEnum("-ksp_hpddm_precision", "Precision in which Krylov bases are stored", "KSPHPDDM", PetscPrecisionTypes, (PetscEnum)data->precision, (PetscEnum *)&data->precision, nullptr));
40   PetscCheck(data->precision != PETSC_PRECISION___FLOAT128 || PetscDefined(HAVE_REAL___FLOAT128), PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP_SYS, "Unsupported %s precision", PetscPrecisionTypes[data->precision]);
41   PetscCheck(std::abs(data->precision - PETSC_SCALAR_PRECISION) <= 1, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Unsupported mixed %s and %s precisions", PetscPrecisionTypes[data->precision], PetscPrecisionTypes[PETSC_SCALAR_PRECISION]);
42   PetscCheck(data->precision != PETSC_PRECISION_INVALID && data->precision != PETSC_PRECISION_BFLOAT16, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Unsupported PetscPrecision %s", PetscPrecisionTypes[data->precision]);
43   static_assert(PETSC_PRECISION___FP16 == PETSC_PRECISION_SINGLE - 1 && PETSC_PRECISION_SINGLE == PETSC_PRECISION_DOUBLE - 1 && PETSC_PRECISION_DOUBLE == PETSC_PRECISION___FLOAT128 - 1, "");
44   if (data->cntl[0] != HPDDM_KRYLOV_METHOD_NONE) {
45     if (data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG && data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG) {
46       i = (data->cntl[1] == static_cast<char>(PETSC_DECIDE) ? HPDDM_VARIANT_LEFT : data->cntl[1]);
47       if (ksp->pc_side_set == PC_SIDE_DEFAULT)
48         PetscCall(PetscOptionsEList("-ksp_hpddm_variant", "Left, right, or variable preconditioning", "KSPHPDDM", HPDDMVariant, PETSC_STATIC_ARRAY_LENGTH(HPDDMVariant), HPDDMVariant[HPDDM_VARIANT_LEFT], &i, nullptr));
49       else if (ksp->pc_side_set == PC_RIGHT) i = HPDDM_VARIANT_RIGHT;
50       data->cntl[1] = i;
51       if (i > 0) PetscCall(KSPSetPCSide(ksp, PC_RIGHT));
52     }
53     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
54       data->rcntl[0] = (PetscAbsReal(data->rcntl[0] - static_cast<PetscReal>(PETSC_DECIDE)) < PETSC_SMALL ? -1.0 : data->rcntl[0]);
55       PetscCall(PetscOptionsReal("-ksp_hpddm_deflation_tol", "Tolerance when deflating right-hand sides inside block methods", "KSPHPDDM", data->rcntl[0], data->rcntl, nullptr));
56       i = (data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG] == static_cast<unsigned short>(PETSC_DECIDE) ? 1 : PetscMax(1, data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG]));
57       PetscCall(PetscOptionsRangeInt("-ksp_hpddm_enlarge_krylov_subspace", "Split the initial right-hand side into multiple vectors", "KSPHPDDM", i, &i, nullptr, 1, std::numeric_limits<unsigned short>::max() - 1));
58       data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG] = i;
59     } else data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG] = 0;
60     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
61       i = (data->cntl[2] == static_cast<char>(PETSC_DECIDE) ? HPDDM_ORTHOGONALIZATION_CGS : data->cntl[2] & 3);
62       PetscCall(PetscOptionsEList("-ksp_hpddm_orthogonalization", "Classical (faster) or Modified (more robust) Gram--Schmidt process", "KSPHPDDM", HPDDMOrthogonalization, PETSC_STATIC_ARRAY_LENGTH(HPDDMOrthogonalization), HPDDMOrthogonalization[HPDDM_ORTHOGONALIZATION_CGS], &i, nullptr));
63       j = (data->cntl[2] == static_cast<char>(PETSC_DECIDE) ? HPDDM_QR_CHOLQR : ((data->cntl[2] >> 2) & 7));
64       PetscCall(PetscOptionsEList("-ksp_hpddm_qr", "Distributed QR factorizations computed with Cholesky QR, Classical or Modified Gram--Schmidt process", "KSPHPDDM", HPDDMQR, PETSC_STATIC_ARRAY_LENGTH(HPDDMQR), HPDDMQR[HPDDM_QR_CHOLQR], &j, nullptr));
65       data->cntl[2] = static_cast<char>(i) + (static_cast<char>(j) << 2);
66       i             = (data->scntl[0] == static_cast<unsigned short>(PETSC_DECIDE) ? PetscMin(30, ksp->max_it) : data->scntl[0]);
67       PetscCall(PetscOptionsRangeInt("-ksp_gmres_restart", "Maximum number of Arnoldi vectors generated per cycle", "KSPHPDDM", i, &i, nullptr, PetscMin(1, ksp->max_it), PetscMin(ksp->max_it, std::numeric_limits<unsigned short>::max() - 1)));
68       data->scntl[0] = i;
69     }
70     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BCG || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
71       j = (data->cntl[1] == static_cast<char>(PETSC_DECIDE) ? HPDDM_QR_CHOLQR : data->cntl[1]);
72       PetscCall(PetscOptionsEList("-ksp_hpddm_qr", "Distributed QR factorizations computed with Cholesky QR, Classical or Modified Gram--Schmidt process", "KSPHPDDM", HPDDMQR, PETSC_STATIC_ARRAY_LENGTH(HPDDMQR), HPDDMQR[HPDDM_QR_CHOLQR], &j, nullptr));
73       data->cntl[1] = j;
74     }
75     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
76       i = (data->icntl[0] == static_cast<int>(PETSC_DECIDE) ? PetscMin(20, data->scntl[0] - 1) : data->icntl[0]);
77       PetscCall(PetscOptionsRangeInt("-ksp_hpddm_recycle", "Number of harmonic Ritz vectors to compute", "KSPHPDDM", i, &i, nullptr, 1, data->scntl[0] - 1));
78       data->icntl[0] = i;
79       if (!PetscDefined(HAVE_SLEPC) || !PetscDefined(USE_SHARED_LIBRARIES) || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR) {
80         i = (data->cntl[3] == static_cast<char>(PETSC_DECIDE) ? HPDDM_RECYCLE_TARGET_SM : data->cntl[3]);
81         PetscCall(PetscOptionsEList("-ksp_hpddm_recycle_target", "Criterion to select harmonic Ritz vectors", "KSPHPDDM", HPDDMRecycleTarget, PETSC_STATIC_ARRAY_LENGTH(HPDDMRecycleTarget), HPDDMRecycleTarget[HPDDM_RECYCLE_TARGET_SM], &i, nullptr));
82         data->cntl[3] = i;
83       } else {
84         PetscCheck(data->precision == PETSC_SCALAR_PRECISION, PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_INCOMP, "Cannot use SLEPc with a different precision than PETSc for harmonic Ritz eigensolves");
85         PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)ksp), &size));
86         i = (data->cntl[3] == static_cast<char>(PETSC_DECIDE) ? 1 : data->cntl[3]);
87         PetscCall(PetscOptionsRangeInt("-ksp_hpddm_recycle_redistribute", "Number of processes used to solve eigenvalue problems when recycling in BGCRODR", "KSPHPDDM", i, &i, nullptr, 1, PetscMin(size, 192)));
88         data->cntl[3] = i;
89       }
90       i = (data->cntl[4] == static_cast<char>(PETSC_DECIDE) ? HPDDM_RECYCLE_STRATEGY_A : data->cntl[4]);
91       PetscCall(PetscOptionsEList("-ksp_hpddm_recycle_strategy", "Generalized eigenvalue problem to solve for recycling", "KSPHPDDM", HPDDMRecycleStrategy, PETSC_STATIC_ARRAY_LENGTH(HPDDMRecycleStrategy), HPDDMRecycleStrategy[HPDDM_RECYCLE_STRATEGY_A], &i, nullptr));
92       data->cntl[4] = i;
93     }
94   } else {
95     data->cntl[0]  = HPDDM_KRYLOV_METHOD_NONE;
96     data->scntl[1] = 1;
97   }
98   PetscCheck(ksp->nmax >= std::numeric_limits<int>::min() && ksp->nmax <= std::numeric_limits<int>::max(), PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "KSPMatSolve() block size %" PetscInt_FMT " not representable by an integer, which is not handled by KSPHPDDM",
99              ksp->nmax);
100   data->icntl[1] = static_cast<int>(ksp->nmax);
101   PetscOptionsHeadEnd();
102   PetscFunctionReturn(PETSC_SUCCESS);
103 }
104 
KSPView_HPDDM(KSP ksp,PetscViewer viewer)105 static PetscErrorCode KSPView_HPDDM(KSP ksp, PetscViewer viewer)
106 {
107   KSP_HPDDM            *data  = (KSP_HPDDM *)ksp->data;
108   HPDDM::PETScOperator *op    = data->op;
109   const PetscScalar    *array = op ? op->storage() : nullptr;
110   PetscBool             ascii;
111 
112   PetscFunctionBegin;
113   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &ascii));
114   if (op && ascii) {
115     PetscCall(PetscViewerASCIIPrintf(viewer, "HPDDM type: %s%s\n", KSPHPDDMTypes[std::min(static_cast<PetscInt>(data->cntl[0]), static_cast<PetscInt>(PETSC_STATIC_ARRAY_LENGTH(KSPHPDDMTypes) - 1))], data->cntl[1] == HPDDM_VARIANT_FLEXIBLE ? " (with support for variable preconditioning)" : ""));
116     PetscCall(PetscViewerASCIIPrintf(viewer, "precision: %s\n", PetscPrecisionTypes[data->precision]));
117     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
118       if (PetscAbsReal(data->rcntl[0] - static_cast<PetscReal>(PETSC_DECIDE)) < PETSC_SMALL) PetscCall(PetscViewerASCIIPrintf(viewer, "no deflation at restarts\n"));
119       else PetscCall(PetscViewerASCIIPrintf(viewer, "deflation tolerance: %g\n", static_cast<double>(data->rcntl[0])));
120     }
121     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
122       PetscCall(PetscViewerASCIIPrintf(viewer, "deflation subspace attached? %s\n", PetscBools[array ? PETSC_TRUE : PETSC_FALSE]));
123       if (!PetscDefined(HAVE_SLEPC) || !PetscDefined(USE_SHARED_LIBRARIES) || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR) PetscCall(PetscViewerASCIIPrintf(viewer, "deflation target: %s\n", HPDDMRecycleTarget[static_cast<PetscInt>(data->cntl[3])]));
124       else PetscCall(PetscViewerASCIIPrintf(viewer, "redistribution size: %d\n", static_cast<PetscMPIInt>(data->cntl[3])));
125     }
126     if (data->icntl[1] != static_cast<int>(PETSC_DECIDE)) PetscCall(PetscViewerASCIIPrintf(viewer, "  block size is %d\n", data->icntl[1]));
127   }
128   PetscFunctionReturn(PETSC_SUCCESS);
129 }
130 
KSPSetUp_HPDDM(KSP ksp)131 static PetscErrorCode KSPSetUp_HPDDM(KSP ksp)
132 {
133   KSP_HPDDM *data = (KSP_HPDDM *)ksp->data;
134   Mat        A;
135   PetscInt   n, bs;
136   PetscBool  match;
137 
138   PetscFunctionBegin;
139   PetscCall(KSPGetOperators(ksp, &A, nullptr));
140   PetscCall(MatGetLocalSize(A, &n, nullptr));
141   PetscCall(MatGetBlockSize(A, &bs));
142   PetscCall(PetscObjectTypeCompareAny((PetscObject)A, &match, MATSEQKAIJ, MATMPIKAIJ, ""));
143   if (match) n /= bs;
144   data->op = new HPDDM::PETScOperator(ksp, n);
145   if (PetscUnlikely(!ksp->setfromoptionscalled || data->cntl[0] == static_cast<char>(PETSC_DECIDE))) { /* what follows is basically a copy/paste of KSPSetFromOptions_HPDDM, with no call to PetscOptions() */
146     PetscCall(PetscInfo(ksp, "KSPSetFromOptions() not called or uninitialized internal structure, hardwiring default KSPHPDDM options\n"));
147     if (data->cntl[0] == static_cast<char>(PETSC_DECIDE)) data->cntl[0] = 0; /* GMRES by default */
148     if (data->cntl[0] != HPDDM_KRYLOV_METHOD_NONE) {                         /* following options do not matter with PREONLY */
149       if (data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG && data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG) {
150         data->cntl[1] = HPDDM_VARIANT_LEFT; /* left preconditioning by default */
151         if (ksp->pc_side_set == PC_RIGHT) data->cntl[1] = HPDDM_VARIANT_RIGHT;
152         if (data->cntl[1] > 0) PetscCall(KSPSetPCSide(ksp, PC_RIGHT));
153       }
154       if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
155         data->rcntl[0]                                          = -1.0; /* no deflation by default */
156         data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG] = 1;    /* Krylov subspace not enlarged by default */
157       } else data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG] = 0;
158       if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
159         data->cntl[2]  = static_cast<char>(HPDDM_ORTHOGONALIZATION_CGS) + (static_cast<char>(HPDDM_QR_CHOLQR) << 2); /* CGS and CholQR by default */
160         data->scntl[0] = PetscMin(30, ksp->max_it);                                                                  /* restart parameter of 30 by default */
161       }
162       if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BCG || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) data->cntl[1] = HPDDM_QR_CHOLQR; /* CholQR by default */
163       if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
164         data->icntl[0] = PetscMin(20, data->scntl[0] - 1); /* recycled subspace of size 20 by default */
165         if (!PetscDefined(HAVE_SLEPC) || !PetscDefined(USE_SHARED_LIBRARIES) || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR) {
166           data->cntl[3] = HPDDM_RECYCLE_TARGET_SM; /* default recycling target */
167         } else {
168           data->cntl[3] = 1; /* redistribution parameter of 1 by default */
169         }
170         data->cntl[4] = HPDDM_RECYCLE_STRATEGY_A; /* default recycling strategy */
171       }
172     } else data->scntl[1] = 1;
173   }
174   PetscCheck(ksp->nmax >= std::numeric_limits<int>::min() && ksp->nmax <= std::numeric_limits<int>::max(), PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "KSPMatSolve() block size %" PetscInt_FMT " not representable by an integer, which is not handled by KSPHPDDM",
175              ksp->nmax);
176   data->icntl[1] = static_cast<int>(ksp->nmax);
177   PetscFunctionReturn(PETSC_SUCCESS);
178 }
179 
KSPReset_HPDDM_Private(KSP ksp)180 static inline PetscErrorCode KSPReset_HPDDM_Private(KSP ksp)
181 {
182   KSP_HPDDM *data = (KSP_HPDDM *)ksp->data;
183 
184   PetscFunctionBegin;
185   /* cast PETSC_DECIDE into the appropriate types to avoid compiler warnings */
186   std::fill_n(data->rcntl, PETSC_STATIC_ARRAY_LENGTH(data->rcntl), static_cast<PetscReal>(PETSC_DECIDE));
187   std::fill_n(data->icntl, PETSC_STATIC_ARRAY_LENGTH(data->icntl), static_cast<int>(PETSC_DECIDE));
188   std::fill_n(data->scntl, PETSC_STATIC_ARRAY_LENGTH(data->scntl), static_cast<unsigned short>(PETSC_DECIDE));
189   std::fill_n(data->cntl, PETSC_STATIC_ARRAY_LENGTH(data->cntl), static_cast<char>(PETSC_DECIDE));
190   data->precision = PETSC_SCALAR_PRECISION;
191   PetscFunctionReturn(PETSC_SUCCESS);
192 }
193 
KSPReset_HPDDM(KSP ksp)194 static PetscErrorCode KSPReset_HPDDM(KSP ksp)
195 {
196   KSP_HPDDM *data = (KSP_HPDDM *)ksp->data;
197 
198   PetscFunctionBegin;
199   delete data->op;
200   data->op = nullptr;
201   PetscCall(KSPReset_HPDDM_Private(ksp));
202   PetscFunctionReturn(PETSC_SUCCESS);
203 }
204 
KSPDestroy_HPDDM(KSP ksp)205 static PetscErrorCode KSPDestroy_HPDDM(KSP ksp)
206 {
207   PetscFunctionBegin;
208   PetscCall(KSPReset_HPDDM(ksp));
209   PetscCall(KSPDestroyDefault(ksp));
210   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetDeflationMat_C", nullptr));
211   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetDeflationMat_C", nullptr));
212   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetType_C", nullptr));
213   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetType_C", nullptr));
214   PetscFunctionReturn(PETSC_SUCCESS);
215 }
216 
217 template <PetscMemType type = PETSC_MEMTYPE_HOST>
KSPSolve_HPDDM_Private(KSP ksp,const PetscScalar * b,PetscScalar * x,PetscInt n)218 static inline PetscErrorCode KSPSolve_HPDDM_Private(KSP ksp, const PetscScalar *b, PetscScalar *x, PetscInt n)
219 {
220   KSP_HPDDM              *data = (KSP_HPDDM *)ksp->data;
221   KSPConvergedDefaultCtx *ctx  = (KSPConvergedDefaultCtx *)ksp->cnvP;
222   const PetscInt          N    = data->op->getDof() * n;
223   PetscBool               flg;
224 #if !PetscDefined(USE_REAL_DOUBLE) || PetscDefined(HAVE_F2CBLASLAPACK___FLOAT128_BINDINGS)
225   HPDDM::upscaled_type<PetscScalar> *high[2];
226 #endif
227 #if !PetscDefined(USE_REAL_SINGLE) || PetscDefined(HAVE_F2CBLASLAPACK___FP16_BINDINGS)
228   typedef HPDDM::downscaled_type<PetscReal> PetscDownscaledReal PETSC_ATTRIBUTE_MAY_ALIAS;
229   #if !PetscDefined(USE_COMPLEX)
230   PetscDownscaledReal *low[2];
231   #else
232   typedef PetscReal PetscAliasedReal   PETSC_ATTRIBUTE_MAY_ALIAS;
233   HPDDM::downscaled_type<PetscScalar> *low[2];
234   PetscAliasedReal                    *x_r;
235   PetscDownscaledReal                 *low_r;
236   #endif
237 #endif
238 #if PetscDefined(HAVE_CUDA)
239   Mat     A;
240   VecType vtype;
241 #endif
242 
243   PetscFunctionBegin;
244 #if PetscDefined(HAVE_CUDA)
245   PetscCall(KSPGetOperators(ksp, &A, nullptr));
246   PetscCall(MatGetVecType(A, &vtype));
247   std::initializer_list<std::string>                 list = {VECCUDA, VECSEQCUDA, VECMPICUDA};
248   std::initializer_list<std::string>::const_iterator it   = std::find(list.begin(), list.end(), std::string(vtype));
249   PetscCheck(type != PETSC_MEMTYPE_HOST || it == list.end(), PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "MatGetVecType() must return a Vec with the same PetscMemType as the right-hand side and solution, PetscMemType(%s) != %s", vtype, PetscMemTypeToString(type));
250 #endif
251   PetscCall(PCGetDiagonalScale(ksp->pc, &flg));
252   PetscCheck(!flg, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ((PetscObject)ksp)->type_name);
253   if (n > 1) {
254     if (ksp->converged == KSPConvergedDefault) {
255       PetscCheck(!ctx->mininitialrtol, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support KSPConvergedDefaultSetUMIRNorm()", ((PetscObject)ksp)->type_name);
256       if (!ctx->initialrtol) {
257         PetscCall(PetscInfo(ksp, "Forcing KSPConvergedDefaultSetUIRNorm() since KSPConvergedDefault() cannot handle multiple norms\n"));
258         ctx->initialrtol = PETSC_TRUE;
259       }
260     } else PetscCall(PetscInfo(ksp, "Using a special \"converged\" callback, be careful, it is used in KSPHPDDM to track blocks of residuals\n"));
261   }
262   /* initial guess is always nonzero with recycling methods if there is a deflation subspace available */
263   if ((data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) && data->op->storage()) ksp->guess_zero = PETSC_FALSE;
264   ksp->its    = 0;
265   ksp->reason = KSP_CONVERGED_ITERATING;
266   if (data->precision > PETSC_SCALAR_PRECISION) { /* Krylov basis stored in higher precision than PetscScalar */
267 #if !PetscDefined(USE_REAL_DOUBLE) || PetscDefined(HAVE_F2CBLASLAPACK___FLOAT128_BINDINGS)
268     if (type == PETSC_MEMTYPE_HOST) {
269       PetscCall(PetscMalloc2(N, high, N, high + 1));
270       HPDDM::copy_n(b, N, high[0]);
271       HPDDM::copy_n(x, N, high[1]);
272       PetscCall(HPDDM::IterativeMethod::solve(*data->op, high[0], high[1], n, PetscObjectComm((PetscObject)ksp)));
273       HPDDM::copy_n(high[1], N, x);
274       PetscCall(PetscFree2(high[0], high[1]));
275     } else {
276       PetscCheck(PetscDefined(HAVE_CUDA) && PetscDefined(USE_REAL_SINGLE), PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "CUDA in PETSc has no support for precisions other than single or double");
277   #if PetscDefined(HAVE_CUDA)
278     #if PetscDefined(HAVE_HPDDM)
279       PetscCall(KSPSolve_HPDDM_CUDA_Private(data, b, x, n, PetscObjectComm((PetscObject)ksp)));
280     #else
281       SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "No CUDA support with --download-hpddm from SLEPc");
282     #endif
283   #endif
284     }
285 #else
286     PetscCheck(data->precision != PETSC_PRECISION___FLOAT128, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Reconfigure with --download-f2cblaslapack --with-f2cblaslapack-float128-bindings");
287 #endif
288   } else if (data->precision < PETSC_SCALAR_PRECISION) { /* Krylov basis stored in lower precision than PetscScalar */
289 #if !PetscDefined(USE_REAL_SINGLE) || PetscDefined(HAVE_F2CBLASLAPACK___FP16_BINDINGS)
290     if (type == PETSC_MEMTYPE_HOST) {
291       PetscCall(PetscMalloc1(N, low));
292   #if !PetscDefined(USE_COMPLEX)
293       low[1] = reinterpret_cast<PetscDownscaledReal *>(x);
294   #else
295       low[1] = reinterpret_cast<HPDDM::downscaled_type<PetscScalar> *>(x);
296   #endif
297       std::copy_n(b, N, low[0]);
298       for (PetscInt i = 0; i < N; ++i) low[1][i] = x[i];
299       PetscCall(HPDDM::IterativeMethod::solve(*data->op, low[0], low[1], n, PetscObjectComm((PetscObject)ksp)));
300   #if !PetscDefined(USE_COMPLEX)
301       for (PetscInt i = N; i-- > 0;) x[i] = static_cast<PetscScalar>(low[1][i]);
302   #else
303       x_r = reinterpret_cast<PetscAliasedReal *>(x), low_r = reinterpret_cast<PetscDownscaledReal *>(x_r);
304       for (PetscInt i = 2 * N; i-- > 0;) x_r[i] = static_cast<PetscReal>(low_r[i]);
305   #endif
306       PetscCall(PetscFree(low[0]));
307     } else {
308       PetscCheck(PetscDefined(HAVE_CUDA) && PetscDefined(USE_REAL_DOUBLE), PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "CUDA in PETSc has no support for precisions other than single or double");
309   #if PetscDefined(HAVE_CUDA)
310     #if PetscDefined(HAVE_HPDDM)
311       PetscCall(KSPSolve_HPDDM_CUDA_Private(data, b, x, n, PetscObjectComm((PetscObject)ksp)));
312     #else
313       SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "No CUDA support with --download-hpddm from SLEPc");
314     #endif
315   #endif
316     }
317 #else
318     PetscCheck(data->precision != PETSC_PRECISION___FP16, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Reconfigure with --download-f2cblaslapack --with-f2cblaslapack-fp16-bindings");
319 #endif
320   } else { /* Krylov basis stored in the same precision as PetscScalar */
321     if (type == PETSC_MEMTYPE_HOST) PetscCall(HPDDM::IterativeMethod::solve(*data->op, b, x, n, PetscObjectComm((PetscObject)ksp)));
322     else {
323       PetscCheck(PetscDefined(USE_REAL_SINGLE) || PetscDefined(USE_REAL_DOUBLE), PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "CUDA in PETSc has no support for precisions other than single or double");
324 #if PetscDefined(HAVE_CUDA)
325   #if PetscDefined(HAVE_HPDDM)
326       PetscCall(KSPSolve_HPDDM_CUDA_Private(data, b, x, n, PetscObjectComm((PetscObject)ksp)));
327   #else
328       SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "No CUDA support with --download-hpddm from SLEPc");
329   #endif
330 #endif
331     }
332   }
333   if (!ksp->reason) { /* KSPConvergedDefault() is still returning 0 (= KSP_CONVERGED_ITERATING) */
334     if (ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
335     else ksp->reason = KSP_CONVERGED_RTOL; /* early exit by HPDDM, which only happens on breakdowns or convergence */
336   }
337   ksp->its = PetscMin(ksp->its, ksp->max_it);
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
KSPSolve_HPDDM(KSP ksp)341 static PetscErrorCode KSPSolve_HPDDM(KSP ksp)
342 {
343   KSP_HPDDM         *data = (KSP_HPDDM *)ksp->data;
344   Mat                A, B;
345   PetscScalar       *x, *bt = nullptr, **ptr;
346   const PetscScalar *b;
347   PetscInt           i, j, n;
348   PetscBool          flg;
349   PetscMemType       type[2];
350 
351   PetscFunctionBegin;
352   PetscCall(PetscCitationsRegister(HPDDMCitation, &HPDDMCite));
353   PetscCall(KSPGetOperators(ksp, &A, nullptr));
354   PetscCall(PetscObjectTypeCompareAny((PetscObject)A, &flg, MATSEQKAIJ, MATMPIKAIJ, ""));
355   PetscCall(VecGetArrayWriteAndMemType(ksp->vec_sol, &x, type));
356   PetscCall(VecGetArrayReadAndMemType(ksp->vec_rhs, &b, type + 1));
357   PetscCheck(type[0] == type[1], PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_INCOMP, "Right-hand side and solution vectors must have the same PetscMemType, %s != %s", PetscMemTypeToString(type[0]), PetscMemTypeToString(type[1]));
358   if (!flg) {
359     if (PetscMemTypeCUDA(type[0])) PetscCall(KSPSolve_HPDDM_Private<PETSC_MEMTYPE_CUDA>(ksp, b, x, 1));
360     else {
361       PetscCheck(PetscMemTypeHost(type[0]), PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "PetscMemType (%s) is neither PETSC_MEMTYPE_HOST nor PETSC_MEMTYPE_CUDA", PetscMemTypeToString(type[0]));
362       PetscCall(KSPSolve_HPDDM_Private(ksp, b, x, 1));
363     }
364   } else {
365     PetscCheck(PetscMemTypeHost(type[0]), PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "PetscMemType (%s) is not PETSC_MEMTYPE_HOST", PetscMemTypeToString(type[0]));
366     PetscCall(MatKAIJGetScaledIdentity(A, &flg));
367     PetscCall(MatKAIJGetAIJ(A, &B));
368     PetscCall(MatGetBlockSize(A, &n));
369     PetscCall(MatGetLocalSize(B, &i, nullptr));
370     j = data->op->getDof();
371     if (!flg) i *= n; /* S and T are not scaled identities, cannot use block methods */
372     if (i != j) {     /* switching between block and standard methods */
373       delete data->op;
374       data->op = new HPDDM::PETScOperator(ksp, i);
375     }
376     if (flg && n > 1) {
377       PetscCall(PetscMalloc1(i * n, &bt));
378       /* from row- to column-major to be consistent with HPDDM */
379       HPDDM::Wrapper<PetscScalar>::omatcopy<'T'>(i, n, b, n, bt, i);
380       ptr = const_cast<PetscScalar **>(&b);
381       std::swap(*ptr, bt);
382       HPDDM::Wrapper<PetscScalar>::imatcopy<'T'>(i, n, x, n, i);
383     }
384     PetscCall(KSPSolve_HPDDM_Private(ksp, b, x, flg ? n : 1));
385     if (flg && n > 1) {
386       std::swap(*ptr, bt);
387       PetscCall(PetscFree(bt));
388       /* from column- to row-major to be consistent with MatKAIJ format */
389       HPDDM::Wrapper<PetscScalar>::imatcopy<'T'>(n, i, x, i, n);
390     }
391   }
392   PetscCall(VecRestoreArrayReadAndMemType(ksp->vec_rhs, &b));
393   PetscCall(VecRestoreArrayWriteAndMemType(ksp->vec_sol, &x));
394   PetscFunctionReturn(PETSC_SUCCESS);
395 }
396 
397 /*@
398   KSPHPDDMSetDeflationMat - Sets the deflation space used by Krylov methods in `KSPHPDDM` with recycling. This space is viewed as a set of vectors stored in
399   a `MATDENSE` (column major).
400 
401   Input Parameters:
402 + ksp - iterative context
403 - U   - deflation space to be used during `KSPSolve()`
404 
405   Level: intermediate
406 
407 .seealso: [](ch_ksp), `KSPHPDDM`, `KSPCreate()`, `KSPType`, `KSPHPDDMGetDeflationMat()`
408 @*/
KSPHPDDMSetDeflationMat(KSP ksp,Mat U)409 PetscErrorCode KSPHPDDMSetDeflationMat(KSP ksp, Mat U)
410 {
411   PetscFunctionBegin;
412   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
413   PetscValidHeaderSpecific(U, MAT_CLASSID, 2);
414   PetscCheckSameComm(ksp, 1, U, 2);
415   PetscUseMethod(ksp, "KSPHPDDMSetDeflationMat_C", (KSP, Mat), (ksp, U));
416   PetscFunctionReturn(PETSC_SUCCESS);
417 }
418 
419 /*@
420   KSPHPDDMGetDeflationMat - Gets the deflation space computed by Krylov methods in `KSPHPDDM`  with recycling or `NULL` if `KSPSolve()` has not been called yet.
421 
422   Input Parameter:
423 . ksp - iterative context
424 
425   Output Parameter:
426 . U - deflation space generated during `KSPSolve()`
427 
428   Level: intermediate
429 
430   Note:
431   This space is viewed as a set of vectors stored in a `MATDENSE` (column major). It is the responsibility of the user to free the returned `Mat`.
432 
433 .seealso: [](ch_ksp), `KSPHPDDM`, `KSPCreate()`, `KSPType`, `KSPHPDDMSetDeflationMat()`
434 @*/
KSPHPDDMGetDeflationMat(KSP ksp,Mat * U)435 PetscErrorCode KSPHPDDMGetDeflationMat(KSP ksp, Mat *U)
436 {
437   PetscFunctionBegin;
438   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
439   if (U) {
440     PetscAssertPointer(U, 2);
441     PetscUseMethod(ksp, "KSPHPDDMGetDeflationMat_C", (KSP, Mat *), (ksp, U));
442   }
443   PetscFunctionReturn(PETSC_SUCCESS);
444 }
445 
KSPHPDDMSetDeflationMat_HPDDM(KSP ksp,Mat U)446 static PetscErrorCode KSPHPDDMSetDeflationMat_HPDDM(KSP ksp, Mat U)
447 {
448   KSP_HPDDM            *data = (KSP_HPDDM *)ksp->data;
449   HPDDM::PETScOperator *op   = data->op;
450   Mat                   A;
451   const PetscScalar    *array;
452   PetscScalar          *copy;
453   PetscInt              m1, M1, m2, M2, n2, N2, ldu;
454   PetscBool             match;
455 
456   PetscFunctionBegin;
457   if (!op) {
458     PetscCall(KSPSetUp(ksp));
459     op = data->op;
460   }
461   PetscCheck(data->precision == PETSC_SCALAR_PRECISION, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "%s != %s", PetscPrecisionTypes[data->precision], PetscPrecisionTypes[PETSC_SCALAR_PRECISION]);
462   PetscCall(KSPGetOperators(ksp, &A, nullptr));
463   PetscCall(MatGetLocalSize(A, &m1, nullptr));
464   PetscCall(MatGetLocalSize(U, &m2, &n2));
465   PetscCall(MatGetSize(A, &M1, nullptr));
466   PetscCall(MatGetSize(U, &M2, &N2));
467   PetscCheck(m1 == m2 && M1 == M2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Cannot use a deflation space with (m2,M2) = (%" PetscInt_FMT ",%" PetscInt_FMT ") for a linear system with (m1,M1) = (%" PetscInt_FMT ",%" PetscInt_FMT ")", m2, M2, m1, M1);
468   PetscCall(PetscObjectTypeCompareAny((PetscObject)U, &match, MATSEQDENSE, MATMPIDENSE, ""));
469   PetscCheck(match, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Provided deflation space not stored in a dense Mat");
470   PetscCall(MatDenseGetArrayRead(U, &array));
471   copy = op->allocate(m2, 1, N2);
472   PetscCheck(copy, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Memory allocation error");
473   PetscCall(MatDenseGetLDA(U, &ldu));
474   HPDDM::Wrapper<PetscScalar>::omatcopy<'N'>(N2, m2, array, ldu, copy, m2);
475   PetscCall(MatDenseRestoreArrayRead(U, &array));
476   PetscFunctionReturn(PETSC_SUCCESS);
477 }
478 
KSPHPDDMGetDeflationMat_HPDDM(KSP ksp,Mat * U)479 static PetscErrorCode KSPHPDDMGetDeflationMat_HPDDM(KSP ksp, Mat *U)
480 {
481   KSP_HPDDM            *data = (KSP_HPDDM *)ksp->data;
482   HPDDM::PETScOperator *op   = data->op;
483   Mat                   A;
484   const PetscScalar    *array;
485   PetscScalar          *copy;
486   PetscInt              m1, M1, N2;
487 
488   PetscFunctionBegin;
489   if (!op) {
490     PetscCall(KSPSetUp(ksp));
491     op = data->op;
492   }
493   PetscCheck(data->precision == PETSC_SCALAR_PRECISION, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "%s != %s", PetscPrecisionTypes[data->precision], PetscPrecisionTypes[PETSC_SCALAR_PRECISION]);
494   array = op->storage();
495   N2    = op->k().first * op->k().second;
496   if (!array) *U = nullptr;
497   else {
498     PetscCall(KSPGetOperators(ksp, &A, nullptr));
499     PetscCall(MatGetLocalSize(A, &m1, nullptr));
500     PetscCall(MatGetSize(A, &M1, nullptr));
501     PetscCall(MatCreateDense(PetscObjectComm((PetscObject)ksp), m1, PETSC_DECIDE, M1, N2, nullptr, U));
502     PetscCall(MatDenseGetArrayWrite(*U, &copy));
503     PetscCall(PetscArraycpy(copy, array, m1 * N2));
504     PetscCall(MatDenseRestoreArrayWrite(*U, &copy));
505   }
506   PetscFunctionReturn(PETSC_SUCCESS);
507 }
508 
KSPMatSolve_HPDDM(KSP ksp,Mat B,Mat X)509 static PetscErrorCode KSPMatSolve_HPDDM(KSP ksp, Mat B, Mat X)
510 {
511   KSP_HPDDM         *data = (KSP_HPDDM *)ksp->data;
512   Mat                A;
513   const PetscScalar *b;
514   PetscScalar       *x;
515   PetscInt           n, lda;
516   PetscMemType       type[2];
517 
518   PetscFunctionBegin;
519   PetscCall(PetscCitationsRegister(HPDDMCitation, &HPDDMCite));
520   if (!data->op) PetscCall(KSPSetUp(ksp));
521   PetscCall(KSPGetOperators(ksp, &A, nullptr));
522   PetscCall(MatGetLocalSize(B, &n, nullptr));
523   PetscCall(MatDenseGetLDA(B, &lda));
524   PetscCheck(n == lda, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Unsupported leading dimension lda = %" PetscInt_FMT " with n = %" PetscInt_FMT, lda, n);
525   PetscCall(MatGetLocalSize(A, &n, nullptr));
526   PetscCall(MatDenseGetLDA(X, &lda));
527   PetscCheck(n == lda, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Unsupported leading dimension lda = %" PetscInt_FMT " with n = %" PetscInt_FMT, lda, n);
528   PetscCall(MatGetSize(X, nullptr, &n));
529   PetscCall(MatDenseGetArrayWriteAndMemType(X, &x, type));
530   PetscCall(MatDenseGetArrayReadAndMemType(B, &b, type + 1));
531   PetscCheck(type[0] == type[1], PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_INCOMP, "Right-hand side and solution matrices must have the same PetscMemType, %s != %s", PetscMemTypeToString(type[0]), PetscMemTypeToString(type[1]));
532   if (PetscMemTypeCUDA(type[0])) PetscCall(KSPSolve_HPDDM_Private<PETSC_MEMTYPE_CUDA>(ksp, b, x, n));
533   else {
534     PetscCheck(PetscMemTypeHost(type[0]), PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "PetscMemType (%s) is neither PETSC_MEMTYPE_HOST nor PETSC_MEMTYPE_CUDA", PetscMemTypeToString(type[0]));
535     PetscCall(KSPSolve_HPDDM_Private(ksp, b, x, n));
536   }
537   PetscCall(MatDenseRestoreArrayReadAndMemType(B, &b));
538   PetscCall(MatDenseRestoreArrayWriteAndMemType(X, &x));
539   PetscFunctionReturn(PETSC_SUCCESS);
540 }
541 
542 /*@
543   KSPHPDDMSetType - Sets the type of Krylov method used in `KSPHPDDM`.
544 
545   Collective
546 
547   Input Parameters:
548 + ksp  - iterative context
549 - type - any of gmres, bgmres, cg, bcg, gcrodr, bgcrodr, bfbcg, or preonly
550 
551   Level: intermediate
552 
553   Notes:
554   Unlike `KSPReset()`, this function does not destroy any deflation space attached to the `KSP`.
555 
556   As an example, in the following sequence\:
557 .vb
558      KSPHPDDMSetType(ksp, KSPGCRODR);
559      KSPSolve(ksp, b, x);
560      KSPHPDDMSetType(ksp, KSPGMRES);
561      KSPHPDDMSetType(ksp, KSPGCRODR);
562      KSPSolve(ksp, b, x);
563 .ve
564   the recycled space is reused in the second `KSPSolve()`.
565 
566 .seealso: [](ch_ksp), `KSPCreate()`, `KSPType`, `KSPHPDDMType`, `KSPHPDDMGetType()`
567 @*/
KSPHPDDMSetType(KSP ksp,KSPHPDDMType type)568 PetscErrorCode KSPHPDDMSetType(KSP ksp, KSPHPDDMType type)
569 {
570   PetscFunctionBegin;
571   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
572   PetscValidLogicalCollectiveEnum(ksp, type, 2);
573   PetscUseMethod(ksp, "KSPHPDDMSetType_C", (KSP, KSPHPDDMType), (ksp, type));
574   PetscFunctionReturn(PETSC_SUCCESS);
575 }
576 
577 /*@
578   KSPHPDDMGetType - Gets the type of Krylov method used in `KSPHPDDM`.
579 
580   Input Parameter:
581 . ksp - iterative context
582 
583   Output Parameter:
584 . type - any of gmres, bgmres, cg, bcg, gcrodr, bgcrodr, bfbcg, or preonly
585 
586   Level: intermediate
587 
588 .seealso: [](ch_ksp), `KSPCreate()`, `KSPType`, `KSPHPDDMType`, `KSPHPDDMSetType()`
589 @*/
KSPHPDDMGetType(KSP ksp,KSPHPDDMType * type)590 PetscErrorCode KSPHPDDMGetType(KSP ksp, KSPHPDDMType *type)
591 {
592   PetscFunctionBegin;
593   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
594   if (type) {
595     PetscAssertPointer(type, 2);
596     PetscUseMethod(ksp, "KSPHPDDMGetType_C", (KSP, KSPHPDDMType *), (ksp, type));
597   }
598   PetscFunctionReturn(PETSC_SUCCESS);
599 }
600 
KSPHPDDMSetType_HPDDM(KSP ksp,KSPHPDDMType type)601 static PetscErrorCode KSPHPDDMSetType_HPDDM(KSP ksp, KSPHPDDMType type)
602 {
603   KSP_HPDDM *data = (KSP_HPDDM *)ksp->data;
604   PetscInt   i;
605   PetscBool  flg = PETSC_FALSE;
606 
607   PetscFunctionBegin;
608   for (i = 0; i < static_cast<PetscInt>(PETSC_STATIC_ARRAY_LENGTH(KSPHPDDMTypes)); ++i) {
609     PetscCall(PetscStrcmp(KSPHPDDMTypes[type], KSPHPDDMTypes[i], &flg));
610     if (flg) break;
611   }
612   PetscCheck(i != PETSC_STATIC_ARRAY_LENGTH(KSPHPDDMTypes), PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown KSPHPDDMType %d", type);
613   if (data->cntl[0] != static_cast<char>(PETSC_DECIDE) && data->cntl[0] != i) PetscCall(KSPReset_HPDDM_Private(ksp));
614   data->cntl[0] = i;
615   PetscFunctionReturn(PETSC_SUCCESS);
616 }
617 
KSPHPDDMGetType_HPDDM(KSP ksp,KSPHPDDMType * type)618 static PetscErrorCode KSPHPDDMGetType_HPDDM(KSP ksp, KSPHPDDMType *type)
619 {
620   KSP_HPDDM *data = (KSP_HPDDM *)ksp->data;
621 
622   PetscFunctionBegin;
623   PetscCheck(data->cntl[0] != static_cast<char>(PETSC_DECIDE), PETSC_COMM_SELF, PETSC_ERR_ORDER, "KSPHPDDMType not set yet");
624   /* need to shift by -1 for HPDDM_KRYLOV_METHOD_NONE */
625   *type = static_cast<KSPHPDDMType>(PetscMin(data->cntl[0], static_cast<char>(PETSC_STATIC_ARRAY_LENGTH(KSPHPDDMTypes) - 1)));
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 /*MC
630    KSPHPDDM - Interface with the HPDDM library. This `KSP` may be used to further select methods that are currently not implemented natively in PETSc, e.g.,
631    GCRODR {cite}`parks2006recycling`, a recycled Krylov method which is similar to `KSPLGMRES`, see {cite}`jolivet2016block` for a comparison.
632    ex75.c shows how to reproduce the results
633    from the aforementioned paper {cite}`parks2006recycling`. A chronological bibliography of relevant publications linked with `KSP` available in HPDDM through `KSPHPDDM`,
634    and not available directly in PETSc, may be found below. The interface is explained in details in {cite}`jolivetromanzampini2020`.
635    See also {cite}`o1980block`, {cite}`ji2017breakdown` and {cite}`calandra2013modified`
636 
637    Options Database Keys:
638 +   -ksp_gmres_restart <restart, default=30>                  - see `KSPGMRES`
639 .   -ksp_hpddm_type <type, default=gmres>                     - any of gmres, bgmres, cg, bcg, gcrodr, bgcrodr, bfbcg, or preonly, see `KSPHPDDMType`
640 .   -ksp_hpddm_precision <value, default=same as PetscScalar> - any of __fp16, single, double or __float128, see `PetscPrecision`
641 .   -ksp_hpddm_deflation_tol <eps, default=\-1.0>             - tolerance when deflating right-hand sides inside block methods (no deflation by default,
642                                                               only relevant with block methods)
643 .   -ksp_hpddm_enlarge_krylov_subspace <p, default=1>         - split the initial right-hand side into multiple vectors (only relevant with nonblock methods)
644 .   -ksp_hpddm_orthogonalization <type, default=cgs>          - any of cgs or mgs, see KSPGMRES
645 .   -ksp_hpddm_qr <type, default=cholqr>                      - distributed QR factorizations with any of cholqr, cgs, or mgs (only relevant with block methods)
646 .   -ksp_hpddm_variant <type, default=left>                   - any of left, right, or flexible (this option is superseded by `KSPSetPCSide()`)
647 .   -ksp_hpddm_recycle <n, default=0>                         - number of harmonic Ritz vectors to compute (only relevant with GCRODR or BGCRODR)
648 .   -ksp_hpddm_recycle_target <type, default=SM>              - criterion to select harmonic Ritz vectors using either SM, LM, SR, LR, SI, or LI
649                                                               (only relevant with GCRODR or BGCRODR). For BGCRODR, if PETSc is compiled with SLEPc,
650                                                               this option is not relevant, since SLEPc is used instead. Options are set with the prefix -ksp_hpddm_recycle_eps_
651 .   -ksp_hpddm_recycle_strategy <type, default=A>             - generalized eigenvalue problem A or B to solve for recycling (only relevant with flexible GCRODR or BGCRODR)
652 -   -ksp_hpddm_recycle_symmetric <true, default=false>        - symmetric generalized eigenproblems in BGCRODR, useful to switch to distributed solvers like
653                                                               `EPSELEMENTAL` or `EPSSCALAPACK` (only relevant when PETSc is compiled with SLEPc)
654 
655    Level: intermediate
656 
657 .seealso: [](ch_ksp), [](sec_flexibleksp), `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `KSPGMRES`, `KSPCG`, `KSPLGMRES`, `KSPDGMRES`
658 M*/
659 
KSPCreate_HPDDM(KSP ksp)660 PETSC_EXTERN PetscErrorCode KSPCreate_HPDDM(KSP ksp)
661 {
662   KSP_HPDDM  *data;
663   PetscInt    i;
664   const char *common[] = {KSPGMRES, KSPCG, KSPPREONLY};
665   PetscBool   flg      = PETSC_FALSE;
666 
667   PetscFunctionBegin;
668   PetscCall(PetscNew(&data));
669   ksp->data = (void *)data;
670   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 2));
671   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 1));
672   ksp->ops->solve          = KSPSolve_HPDDM;
673   ksp->ops->matsolve       = KSPMatSolve_HPDDM;
674   ksp->ops->setup          = KSPSetUp_HPDDM;
675   ksp->ops->setfromoptions = KSPSetFromOptions_HPDDM;
676   ksp->ops->destroy        = KSPDestroy_HPDDM;
677   ksp->ops->view           = KSPView_HPDDM;
678   ksp->ops->reset          = KSPReset_HPDDM;
679   PetscCall(KSPReset_HPDDM_Private(ksp));
680   for (i = 0; i < static_cast<PetscInt>(PETSC_STATIC_ARRAY_LENGTH(common)); ++i) {
681     PetscCall(PetscStrcmp(((PetscObject)ksp)->type_name, common[i], &flg));
682     if (flg) break;
683   }
684   if (!i) data->cntl[0] = HPDDM_KRYLOV_METHOD_GMRES;
685   else if (i == 1) data->cntl[0] = HPDDM_KRYLOV_METHOD_CG;
686   else if (i == 2) data->cntl[0] = HPDDM_KRYLOV_METHOD_NONE;
687   if (data->cntl[0] != static_cast<char>(PETSC_DECIDE)) PetscCall(PetscInfo(ksp, "Using the previously set KSPType %s\n", common[i]));
688   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetDeflationMat_C", KSPHPDDMSetDeflationMat_HPDDM));
689   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetDeflationMat_C", KSPHPDDMGetDeflationMat_HPDDM));
690   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetType_C", KSPHPDDMSetType_HPDDM));
691   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetType_C", KSPHPDDMGetType_HPDDM));
692 #if PetscDefined(HAVE_SLEPC) && PetscDefined(HAVE_DYNAMIC_LIBRARIES) && PetscDefined(USE_SHARED_LIBRARIES)
693   if (!loadedDL) PetscCall(HPDDMLoadDL_Private(&loadedDL));
694 #endif
695   data->precision = PETSC_SCALAR_PRECISION;
696   PetscFunctionReturn(PETSC_SUCCESS);
697 }
698