Actual source code: hpddm.cxx

petsc-master 2020-09-23
Report Typos and Errors
  1: #include <petsc/private/petschpddm.h>

  3: /* static array length */
  4: #define ALEN(a) (sizeof(a)/sizeof((a)[0]))

  6: const char *const KSPHPDDMTypes[]           = { KSPGMRES, "bgmres", KSPCG, "bcg", "gcrodr", "bgcrodr", "bfbcg", KSPPREONLY };
  7: static const char *HPDDMOrthogonalization[] = { "cgs", "mgs" };
  8: static const char *HPDDMQR[]                = { "cholqr", "cgs", "mgs" };
  9: static const char *HPDDMVariant[]           = { "left", "right", "flexible" };
 10: static const char *HPDDMRecycleTarget[]     = { "SM", "LM", "SR", "LR", "SI", "LI" };
 11: static const char *HPDDMRecycleStrategy[]   = { "A", "B" };

 13: static PetscBool citeKSP = PETSC_FALSE;
 14: static const char hpddmCitationKSP[] = "@inproceedings{jolivet2016block,\n\tTitle = {{Block Iterative Methods and Recycling for Improved Scalability of Linear Solvers}},\n\tAuthor = {Jolivet, Pierre and Tournier, Pierre-Henri},\n\tOrganization = {IEEE},\n\tYear = {2016},\n\tSeries = {SC16},\n\tBooktitle = {Proceedings of the 2016 International Conference for High Performance Computing, Networking, Storage and Analysis}\n}\n";

 16: #if defined(PETSC_HAVE_SLEPC) && defined(PETSC_USE_SHARED_LIBRARIES)
 17: static PetscBool loadedDL = PETSC_FALSE;
 18: #endif

 20: static PetscErrorCode KSPSetFromOptions_HPDDM(PetscOptionItems *PetscOptionsObject, KSP ksp)
 21: {
 22:   KSP_HPDDM      *data = (KSP_HPDDM*)ksp->data;
 23:   PetscInt       i, j;
 24:   PetscMPIInt    size;

 28:   PetscOptionsHead(PetscOptionsObject, "KSPHPDDM options, cf. https://github.com/hpddm/hpddm");
 29:   i = (data->cntl[0] == static_cast<char>(PETSC_DECIDE) ? HPDDM_KRYLOV_METHOD_GMRES : data->cntl[0]);
 30:   PetscOptionsEList("-ksp_hpddm_type", "Type of Krylov method", "KSPHPDDM", KSPHPDDMTypes, ALEN(KSPHPDDMTypes), KSPHPDDMTypes[HPDDM_KRYLOV_METHOD_GMRES], &i, NULL);
 31:   if (i == ALEN(KSPHPDDMTypes) - 1)
 32:     i = HPDDM_KRYLOV_METHOD_NONE; /* need to shift the value since HPDDM_KRYLOV_METHOD_RICHARDSON is not registered in PETSc */
 33:   data->cntl[0] = i;
 34:   if (data->cntl[0] != HPDDM_KRYLOV_METHOD_NONE) {
 35:     if (data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG && data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG) {
 36:       i = (data->cntl[1] == static_cast<char>(PETSC_DECIDE) ? HPDDM_VARIANT_LEFT : data->cntl[1]);
 37:       if (ksp->pc_side_set == PC_SIDE_DEFAULT) {
 38:         PetscOptionsEList("-ksp_hpddm_variant", "Left, right, or variable preconditioning", "KSPHPDDM", HPDDMVariant, ALEN(HPDDMVariant), HPDDMVariant[HPDDM_VARIANT_LEFT], &i, NULL);
 39:       } else if (ksp->pc_side_set == PC_RIGHT) i = HPDDM_VARIANT_RIGHT;
 40:       else if (ksp->pc_side_set == PC_SYMMETRIC) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Symmetric preconditioning not implemented");
 41:       data->cntl[1] = i;
 42:       if (i > 0) {
 43:         KSPSetPCSide(ksp, PC_RIGHT);
 44:       }
 45:     }
 46:     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
 47:       data->rcntl[0] = (std::abs(data->rcntl[0] - static_cast<PetscReal>(PETSC_DECIDE)) < PETSC_SMALL ? -1.0 : data->rcntl[0]);
 48:       PetscOptionsReal("-ksp_hpddm_deflation_tol", "Tolerance when deflating right-hand sides inside block methods", "KSPHPDDM", data->rcntl[0], data->rcntl, NULL);
 49:       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]));
 50:       PetscOptionsRangeInt("-ksp_hpddm_enlarge_krylov_subspace", "Split the initial right-hand side into multiple vectors", "KSPHPDDM", i, &i, NULL, 1, std::numeric_limits<unsigned short>::max() - 1);
 51:       data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG] = i;
 52:     } else data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG] = 0;
 53:     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) {
 54:       i = (data->cntl[2] == static_cast<char>(PETSC_DECIDE) ? HPDDM_ORTHOGONALIZATION_CGS : data->cntl[2] & 3);
 55:       PetscOptionsEList("-ksp_hpddm_orthogonalization", "Classical (faster) or Modified (more robust) Gram--Schmidt process", "KSPHPDDM", HPDDMOrthogonalization, ALEN(HPDDMOrthogonalization), HPDDMOrthogonalization[HPDDM_ORTHOGONALIZATION_CGS], &i, NULL);
 56:       j = (data->cntl[2] == static_cast<char>(PETSC_DECIDE) ? HPDDM_QR_CHOLQR : ((data->cntl[2] >> 2) & 7));
 57:       PetscOptionsEList("-ksp_hpddm_qr", "Distributed QR factorizations computed with Cholesky QR, Classical or Modified Gram--Schmidt process", "KSPHPDDM", HPDDMQR, ALEN(HPDDMQR), HPDDMQR[HPDDM_QR_CHOLQR], &j, NULL);
 58:       data->cntl[2] = static_cast<char>(i) + (static_cast<char>(j) << 2);
 59:       i = (data->scntl[0] == static_cast<unsigned short>(PETSC_DECIDE) ? PetscMin(30, ksp->max_it) : data->scntl[0]);
 60:       PetscOptionsRangeInt("-ksp_gmres_restart", "Maximum number of Arnoldi vectors generated per cycle", "KSPHPDDM", i, &i, NULL, PetscMin(1, ksp->max_it), PetscMin(ksp->max_it, std::numeric_limits<unsigned short>::max() - 1));
 61:       data->scntl[0] = i;
 62:     }
 63:     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BCG || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
 64:       j = (data->cntl[1] == static_cast<char>(PETSC_DECIDE) ? HPDDM_QR_CHOLQR : data->cntl[1]);
 65:       PetscOptionsEList("-ksp_hpddm_qr", "Distributed QR factorizations computed with Cholesky QR, Classical or Modified Gram--Schmidt process", "KSPHPDDM", HPDDMQR, ALEN(HPDDMQR), HPDDMQR[HPDDM_QR_CHOLQR], &j, NULL);
 66:       data->cntl[1] = j;
 67:     }
 68:     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
 69:       i = (data->icntl[0] == static_cast<int>(PETSC_DECIDE) ? PetscMin(20, data->scntl[0] - 1) : data->icntl[0]);
 70:       PetscOptionsRangeInt("-ksp_hpddm_recycle", "Number of harmonic Ritz vectors to compute", "KSPHPDDM", i, &i, NULL, 1, data->scntl[0] - 1);
 71:       data->icntl[0] = i;
 72:       if (!PetscDefined(HAVE_SLEPC) || !PetscDefined(USE_SHARED_LIBRARIES) || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR) {
 73:         i = (data->cntl[3] == static_cast<char>(PETSC_DECIDE) ? HPDDM_RECYCLE_TARGET_SM : data->cntl[3]);
 74:         PetscOptionsEList("-ksp_hpddm_recycle_target", "Criterion to select harmonic Ritz vectors", "KSPHPDDM", HPDDMRecycleTarget, ALEN(HPDDMRecycleTarget), HPDDMRecycleTarget[HPDDM_RECYCLE_TARGET_SM], &i, NULL);
 75:         data->cntl[3] = i;
 76:       } else {
 77:         MPI_Comm_size(PetscObjectComm((PetscObject)ksp), &size);
 78:         i = (data->cntl[3] == static_cast<char>(PETSC_DECIDE) ? 1 : data->cntl[3]);
 79:         PetscOptionsRangeInt("-ksp_hpddm_recycle_redistribute", "Number of processes used to solve eigenvalue problems when recycling in BGCRODR", "KSPHPDDM", i, &i, NULL, 1, PetscMin(size, 192));
 80:         data->cntl[3] = i;
 81:       }
 82:       i = (data->cntl[4] == static_cast<char>(PETSC_DECIDE) ? HPDDM_RECYCLE_STRATEGY_A : data->cntl[4]);
 83:       PetscOptionsEList("-ksp_hpddm_recycle_strategy", "Generalized eigenvalue problem to solve for recycling", "KSPHPDDM", HPDDMRecycleStrategy, ALEN(HPDDMRecycleStrategy), HPDDMRecycleStrategy[HPDDM_RECYCLE_STRATEGY_A], &i, NULL);
 84:       data->cntl[4] = i;
 85:     }
 86:   } else {
 87:     data->cntl[0] = HPDDM_KRYLOV_METHOD_NONE;
 88:     data->scntl[1] = 1;
 89:   }
 90:   PetscOptionsTail();
 91:   return(0);
 92: }

 94: static PetscErrorCode KSPView_HPDDM(KSP ksp, PetscViewer viewer)
 95: {
 96:   KSP_HPDDM            *data = (KSP_HPDDM*)ksp->data;
 97:   HPDDM::PETScOperator *op = data->op;
 98:   const PetscScalar    *array = op ? op->storage() : NULL;
 99:   PetscBool            ascii;
100:   PetscErrorCode       ierr;

103:   PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &ascii);
104:   if (op && ascii) {
105:     PetscViewerASCIIPrintf(viewer, "HPDDM type: %s\n", KSPHPDDMTypes[std::min(static_cast<PetscInt>(data->cntl[0]), static_cast<PetscInt>(ALEN(KSPHPDDMTypes) - 1))]);
106:     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
107:       if (std::abs(data->rcntl[0] - static_cast<PetscReal>(PETSC_DECIDE)) < PETSC_SMALL) {
108:         PetscViewerASCIIPrintf(viewer, "no deflation at restarts\n", PetscBools[array ? PETSC_TRUE : PETSC_FALSE]);
109:       } else {
110:         PetscViewerASCIIPrintf(viewer, "deflation tolerance: %g\n", data->rcntl[0]);
111:       }
112:     }
113:     if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
114:       PetscViewerASCIIPrintf(viewer, "deflation subspace attached? %s\n", PetscBools[array ? PETSC_TRUE : PETSC_FALSE]);
115:       if (!PetscDefined(HAVE_SLEPC) || !PetscDefined(USE_SHARED_LIBRARIES) || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR) {
116:         PetscViewerASCIIPrintf(viewer, "deflation target: %s\n", HPDDMRecycleTarget[static_cast<PetscInt>(data->cntl[3])]);
117:       } else {
118:         PetscViewerASCIIPrintf(viewer, "redistribution size: %D\n", static_cast<PetscInt>(data->cntl[3]));
119:       }
120:     }
121:     if (data->icntl[1] != static_cast<int>(PETSC_DECIDE)) {
122:       PetscViewerASCIIPrintf(viewer, "  block size is %d\n", data->icntl[1]);
123:     }
124:   }
125:   return(0);
126: }

128: static PetscErrorCode KSPSetUp_HPDDM(KSP ksp)
129: {
130:   KSP_HPDDM      *data = (KSP_HPDDM*)ksp->data;
131:   Mat            A;
132:   PetscInt       n, bs;
133:   PetscBool      match;

137:   KSPGetOperators(ksp, &A, NULL);
138:   MatGetLocalSize(A, &n, NULL);
139:   MatGetBlockSize(A, &bs);
140:   PetscObjectTypeCompareAny((PetscObject)A, &match, MATSEQKAIJ, MATMPIKAIJ, "");
141:   if (match) n /= bs;
142: #if defined(PETSC_PKG_HPDDM_VERSION_MAJOR)
143: #if PETSC_PKG_HPDDM_VERSION_LT(2, 0, 4)
144:   data->op = new HPDDM::PETScOperator(ksp, n, 1);
145: #else
146:   data->op = new HPDDM::PETScOperator(ksp, n);
147: #endif
148: #else
149:   data->op = new HPDDM::PETScOperator(ksp, n, 1);
150: #endif
151:   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() */
152:     PetscInfo(ksp, "KSPSetFromOptions() not called or uninitialized internal structure, hardwiring default KSPHPDDM options\n");
153:     if (data->cntl[0] == static_cast<char>(PETSC_DECIDE))
154:       data->cntl[0] = 0; /* GMRES by default */
155:     if (data->cntl[0] != HPDDM_KRYLOV_METHOD_NONE) { /* following options do not matter with PREONLY */
156:       if (data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG && data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG) {
157:         data->cntl[1] = HPDDM_VARIANT_LEFT; /* left preconditioning by default */
158:         if (ksp->pc_side_set == PC_RIGHT) data->cntl[1] = HPDDM_VARIANT_RIGHT;
159:         else if (ksp->pc_side_set == PC_SYMMETRIC) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Symmetric preconditioning not implemented");
160:         if (data->cntl[1] > 0) {
161:           KSPSetPCSide(ksp, PC_RIGHT);
162:         }
163:       }
164:       if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BGMRES || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
165:         data->rcntl[0] = -1.0; /* no deflation by default */
166:         data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BFBCG] = 1; /* Krylov subspace not enlarged by default */
167:       } else data->scntl[data->cntl[0] != HPDDM_KRYLOV_METHOD_BCG] = 0;
168:       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) {
169:         data->cntl[2] = static_cast<char>(HPDDM_ORTHOGONALIZATION_CGS) + (static_cast<char>(HPDDM_QR_CHOLQR) << 2); /* CGS and CholQR by default */
170:         data->scntl[0] = PetscMin(30, ksp->max_it); /* restart parameter of 30 by default */
171:       }
172:       if (data->cntl[0] == HPDDM_KRYLOV_METHOD_BCG || data->cntl[0] == HPDDM_KRYLOV_METHOD_BFBCG) {
173:         data->cntl[1] = HPDDM_QR_CHOLQR; /* CholQR by default */
174:       }
175:       if (data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) {
176:         data->icntl[0] = PetscMin(20, data->scntl[0] - 1); /* recycled subspace of size 20 by default */
177:         if (!PetscDefined(HAVE_SLEPC) || !PetscDefined(USE_SHARED_LIBRARIES) || data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR) {
178:           data->cntl[3] = HPDDM_RECYCLE_TARGET_SM; /* default recycling target */
179:         } else {
180:           data->cntl[3] = 1; /* redistribution parameter of 1 by default */
181:         }
182:         data->cntl[4] = HPDDM_RECYCLE_STRATEGY_A; /* default recycling strategy */
183:       }
184:     } else data->scntl[1] = 1;
185:   }
186:   return(0);
187: }

189: PETSC_STATIC_INLINE PetscErrorCode KSPHPDDMReset_Private(KSP ksp)
190: {
191:   KSP_HPDDM *data = (KSP_HPDDM*)ksp->data;

194:   /* cast PETSC_DECIDE into the appropriate types to avoid compiler warnings */
195:   std::fill_n(data->rcntl, ALEN(data->rcntl), static_cast<PetscReal>(PETSC_DECIDE));
196:   std::fill_n(data->icntl, ALEN(data->icntl), static_cast<int>(PETSC_DECIDE));
197:   std::fill_n(data->scntl, ALEN(data->scntl), static_cast<unsigned short>(PETSC_DECIDE));
198:   std::fill_n(data->cntl , ALEN(data->cntl) , static_cast<char>(PETSC_DECIDE));
199:   return(0);
200: }

202: static PetscErrorCode KSPReset_HPDDM(KSP ksp)
203: {
204:   KSP_HPDDM      *data = (KSP_HPDDM*)ksp->data;

208:   if (data->op) {
209:     delete data->op;
210:     data->op = NULL;
211:   }
212:   KSPHPDDMReset_Private(ksp);
213:   return(0);
214: }

216: static PetscErrorCode KSPDestroy_HPDDM(KSP ksp)
217: {

221:   KSPReset_HPDDM(ksp);
222:   KSPDestroyDefault(ksp);
223:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetDeflationSpace_C", NULL);
224:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetDeflationSpace_C", NULL);
225:   PetscObjectComposeFunction((PetscObject)ksp, "KSPSetMatSolveBlockSize_C", NULL);
226:   PetscObjectComposeFunction((PetscObject)ksp, "KSPGetMatSolveBlockSize_C", NULL);
227:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetType_C", NULL);
228:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetType_C", NULL);
229:   return(0);
230: }

232: PETSC_STATIC_INLINE PetscErrorCode KSPSolve_HPDDM_Private(KSP ksp, const PetscScalar *b, PetscScalar *x, PetscInt n)
233: {
234:   KSP_HPDDM              *data = (KSP_HPDDM*)ksp->data;
235:   KSPConvergedDefaultCtx *ctx = (KSPConvergedDefaultCtx*)ksp->cnvP;
236:   PetscBool              scale;
237:   PetscErrorCode         ierr;

240:   PCGetDiagonalScale(ksp->pc, &scale);
241:   if (scale) SETERRQ1(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ((PetscObject)ksp)->type_name);
242:   if (n > 1) {
243:     if (ksp->converged == KSPConvergedDefault) {
244:       if (ctx->mininitialrtol) SETERRQ1(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support KSPConvergedDefaultSetUMIRNorm()", ((PetscObject)ksp)->type_name);
245:       if (!ctx->initialrtol) {
246:         PetscInfo(ksp, "Forcing KSPConvergedDefaultSetUIRNorm() since KSPConvergedDefault() cannot handle multiple norms\n");
247:         ctx->initialrtol = PETSC_TRUE;
248:       }
249:     } else {
250:       PetscInfo(ksp, "Using a special \"converged\" callback, be careful, it is used in KSPHPDDM to track blocks of residuals\n");
251:     }
252:   }
253:   /* initial guess is always nonzero with recycling methods if there is a deflation subspace available */
254:   if ((data->cntl[0] == HPDDM_KRYLOV_METHOD_GCRODR || data->cntl[0] == HPDDM_KRYLOV_METHOD_BGCRODR) && data->op->storage()) ksp->guess_zero = PETSC_FALSE;
255:   ksp->its = 0;
256:   ksp->reason = KSP_CONVERGED_ITERATING;
257:   static_cast<PetscErrorCode>(HPDDM::IterativeMethod::solve(*data->op, b, x, n, PetscObjectComm((PetscObject)ksp)));
258:   if (!ksp->reason) { /* KSPConvergedDefault() is still returning 0 (= KSP_CONVERGED_ITERATING) */
259:     if (ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
260:     else ksp->reason = KSP_CONVERGED_RTOL; /* early exit by HPDDM, which only happens on breakdowns or convergence */
261:   }
262:   ksp->its = PetscMin(ksp->its, ksp->max_it);
263:   return(0);
264: }

266: static PetscErrorCode KSPSolve_HPDDM(KSP ksp)
267: {
268:   KSP_HPDDM         *data = (KSP_HPDDM*)ksp->data;
269:   Mat               A, B;
270:   PetscScalar       *x, *bt = NULL, **ptr;
271:   const PetscScalar *b;
272:   PetscInt          i, j, n;
273:   PetscBool         flg;
274:   PetscErrorCode    ierr;

277:   PetscCitationsRegister(hpddmCitationKSP, &citeKSP);
278:   KSPGetOperators(ksp, &A, NULL);
279:   PetscObjectTypeCompareAny((PetscObject)A, &flg, MATSEQKAIJ, MATMPIKAIJ, "");
280:   VecGetArray(ksp->vec_sol, &x);
281:   VecGetArrayRead(ksp->vec_rhs, &b);
282:   if (!flg) {
283:     KSPSolve_HPDDM_Private(ksp, b, x, 1);
284:   } else {
285:     MatKAIJGetScaledIdentity(A, &flg);
286:     MatKAIJGetAIJ(A, &B);
287:     MatGetBlockSize(A, &n);
288:     MatGetLocalSize(B, &i, NULL);
289:     j = data->op->getDof();
290:     if (!flg) i *= n; /* S and T are not scaled identities, cannot use block methods */
291:     if (i != j) { /* switching between block and standard methods */
292:       delete data->op;
293: #if defined(PETSC_PKG_HPDDM_VERSION_MAJOR)
294: #if PETSC_PKG_HPDDM_VERSION_LT(2, 0, 4)
295:       data->op = new HPDDM::PETScOperator(ksp, i, 1);
296: #else
297:       data->op = new HPDDM::PETScOperator(ksp, i);
298: #endif
299: #else
300:       data->op = new HPDDM::PETScOperator(ksp, i, 1);
301: #endif
302:     }
303:     if (flg && n > 1) {
304:       PetscMalloc1(i * n, &bt);
305:       /* from row- to column-major to be consistent with HPDDM */
306:       HPDDM::Wrapper<PetscScalar>::omatcopy<'T'>(i, n, b, n, bt, i);
307:       ptr = const_cast<PetscScalar**>(&b);
308:       std::swap(*ptr, bt);
309:       HPDDM::Wrapper<PetscScalar>::imatcopy<'T'>(i, n, x, n, i);
310:     }
311:     KSPSolve_HPDDM_Private(ksp, b, x, flg ? n : 1);
312:     if (flg && n > 1) {
313:       std::swap(*ptr, bt);
314:       PetscFree(bt);
315:       /* from column- to row-major to be consistent with MatKAIJ format */
316:       HPDDM::Wrapper<PetscScalar>::imatcopy<'T'>(n, i, x, i, n);
317:     }
318:   }
319:   VecRestoreArrayRead(ksp->vec_rhs, &b);
320:   VecRestoreArray(ksp->vec_sol, &x);
321:   return(0);
322: }

324: /*@
325:      KSPHPDDMSetDeflationSpace - Sets the deflation space used by Krylov methods with recycling. This space is viewed as a set of vectors stored in a MATDENSE (column major).

327:    Input Parameters:
328: +     ksp - iterative context
329: -     U - deflation space to be used during KSPSolve()

331:    Level: intermediate

333: .seealso:  KSPCreate(), KSPType (for list of available types), KSPHPDDMGetDeflationSpace()
334: @*/
335: PetscErrorCode KSPHPDDMSetDeflationSpace(KSP ksp, Mat U)
336: {

343:   PetscUseMethod(ksp, "KSPHPDDMSetDeflationSpace_C", (KSP, Mat), (ksp, U));
344:   return(0);
345: }

347: /*@
348:      KSPHPDDMGetDeflationSpace - Gets the deflation space computed by Krylov methods with recycling or NULL if KSPSolve() has not been called yet. 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.

350:    Input Parameter:
351: .     ksp - iterative context

353:    Output Parameter:
354: .     U - deflation space generated during KSPSolve()

356:    Level: intermediate

358: .seealso:  KSPCreate(), KSPType (for list of available types), KSPHPDDMSetDeflationSpace()
359: @*/
360: PetscErrorCode KSPHPDDMGetDeflationSpace(KSP ksp, Mat *U)
361: {

366:   PetscUseMethod(ksp, "KSPHPDDMGetDeflationSpace_C", (KSP, Mat*), (ksp, U));
367:   return(0);
368: }

370: static PetscErrorCode KSPHPDDMSetDeflationSpace_HPDDM(KSP ksp, Mat U)
371: {
372:   KSP_HPDDM            *data = (KSP_HPDDM*)ksp->data;
373:   HPDDM::PETScOperator *op = data->op;
374:   Mat                  A;
375:   const PetscScalar    *array;
376:   PetscScalar          *copy;
377:   PetscInt             m1, M1, m2, M2, n2, N2, ldu;
378:   PetscBool            match;
379:   PetscErrorCode       ierr;

382:   if (!op) {
383:     KSPSetUp(ksp);
384:     op = data->op;
385:   }
386:   KSPGetOperators(ksp, &A, NULL);
387:   MatGetLocalSize(A, &m1, NULL);
388:   MatGetLocalSize(U, &m2, &n2);
389:   MatGetSize(A, &M1, NULL);
390:   MatGetSize(U, &M2, &N2);
391:   if (m1 != m2 || M1 != M2) SETERRQ4(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Cannot use a deflation space with (m2,M2) = (%D,%D) for a linear system with (m1,M1) = (%D,%D)", m2, M2, m1, M1);
392:   PetscObjectTypeCompareAny((PetscObject)U, &match, MATSEQDENSE, MATMPIDENSE, "");
393:   if (!match) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Provided deflation space not stored in a dense Mat");
394:   MatDenseGetArrayRead(U, &array);
395:   copy = op->allocate(m2, 1, N2);
396:   if (!copy) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_POINTER, "Memory allocation error");
397:   MatDenseGetLDA(U, &ldu);
398:   HPDDM::Wrapper<PetscScalar>::omatcopy<'N'>(N2, m2, array, ldu, copy, m2);
399:   MatDenseRestoreArrayRead(U, &array);
400:   return(0);
401: }

403: static PetscErrorCode KSPHPDDMGetDeflationSpace_HPDDM(KSP ksp, Mat *U)
404: {
405:   KSP_HPDDM            *data = (KSP_HPDDM*)ksp->data;
406:   HPDDM::PETScOperator *op = data->op;
407:   Mat                  A;
408:   const PetscScalar    *array;
409:   PetscScalar          *copy;
410:   PetscInt             m1, M1, N2;
411:   PetscErrorCode       ierr;

414:   if (!op) {
415:     KSPSetUp(ksp);
416:     op = data->op;
417:   }
418:   array = op->storage();
419:   N2 = op->k().first * op->k().second;
420:   if (!array) *U = NULL;
421:   else {
422:     KSPGetOperators(ksp, &A, NULL);
423:     MatGetLocalSize(A, &m1, NULL);
424:     MatGetSize(A, &M1, NULL);
425:     MatCreateDense(PetscObjectComm((PetscObject)ksp), m1, PETSC_DECIDE, M1, N2, NULL, U);
426:     MatDenseGetArrayWrite(*U, &copy);
427:     PetscArraycpy(copy, array, m1 * N2);
428:     MatDenseRestoreArrayWrite(*U, &copy);
429:   }
430:   return(0);
431: }

433: static PetscErrorCode KSPMatSolve_HPDDM(KSP ksp, Mat B, Mat X)
434: {
435:   KSP_HPDDM            *data = (KSP_HPDDM*)ksp->data;
436:   HPDDM::PETScOperator *op = data->op;
437:   Mat                  A;
438:   const PetscScalar    *b;
439:   PetscScalar          *x;
440:   PetscInt             n, lda;
441:   PetscErrorCode       ierr;

444:   if (!op) {
445:     KSPSetUp(ksp);
446:     op = data->op;
447:   }
448:   KSPGetOperators(ksp, &A, NULL);
449:   MatGetLocalSize(B, &n, NULL);
450:   MatDenseGetLDA(B, &lda);
451:   if (n != lda) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Unhandled leading dimension lda = %D with n = %D", lda, n);
452:   MatGetLocalSize(A, &n, NULL);
453:   MatDenseGetLDA(X, &lda);
454:   if (n != lda) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Unhandled leading dimension lda = %D with n = %D", lda, n);
455:   MatDenseGetArrayRead(B, &b);
456:   MatDenseGetArrayWrite(X, &x);
457:   MatGetSize(X, NULL, &n);
458:   KSPSolve_HPDDM_Private(ksp, b, x, n);
459:   MatDenseRestoreArrayWrite(X, &x);
460:   MatDenseRestoreArrayRead(B, &b);
461:   return(0);
462: }

464: static PetscErrorCode KSPSetMatSolveBlockSize_HPDDM(KSP ksp, PetscInt bs)
465: {
466:   KSP_HPDDM *data = (KSP_HPDDM*)ksp->data;

469:   if (bs > std::numeric_limits<int>::max() || bs < std::numeric_limits<int>::min()) SETERRQ1(PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_OUTOFRANGE, "KSPMatSolve() block size %D not representable by an integer", bs);
470:   else data->icntl[1] = static_cast<int>(bs);
471:   return(0);
472: }

474: static PetscErrorCode KSPGetMatSolveBlockSize_HPDDM(KSP ksp, PetscInt *bs)
475: {
476:   KSP_HPDDM *data = (KSP_HPDDM*)ksp->data;

479:   *bs = static_cast<PetscInt>(data->icntl[1]);
480:   return(0);
481: }

483: /*@
484:      KSPHPDDMSetType - Sets the type of Krylov method used in KSPHPDDM.

486:    Input Parameters:
487: +     ksp - iterative context
488: -     type - any of gmres, bgmres, cg, bcg, gcrodr, bgcrodr, bfbcg, or preonly

490:    Level: intermediate

492:    Notes:
493:      Unlike KSPReset(), this function does not destroy any deflation space attached to the KSP.
494:      As an example, in the following sequence: KSPHPDDMSetType(ksp, KSPGCRODR); KSPSolve(ksp, b, x); KSPHPDDMSetType(ksp, KSPGMRES); KSPHPDDMSetType(ksp, KSPGCRODR); KSPSolve(ksp, b, x); the recycled space is reused in the second KSPSolve().

496: .seealso:  KSPCreate(), KSPType (for list of available types), KSPHPDDMType, KSPHPDDMGetType()
497: @*/
498: PetscErrorCode KSPHPDDMSetType(KSP ksp, KSPHPDDMType type)
499: {

504:   PetscUseMethod(ksp, "KSPHPDDMSetType_C", (KSP, KSPHPDDMType), (ksp, type));
505:   return(0);
506: }

508: /*@
509:      KSPHPDDMGetType - Gets the type of Krylov method used in KSPHPDDM.

511:    Input Parameter:
512: .     ksp - iterative context

514:    Output Parameter:
515: .     type - any of gmres, bgmres, cg, bcg, gcrodr, bgcrodr, bfbcg, or preonly

517:    Level: intermediate

519: .seealso:  KSPCreate(), KSPType (for list of available types), KSPHPDDMType, KSPHPDDMSetType()
520: @*/
521: PetscErrorCode KSPHPDDMGetType(KSP ksp, KSPHPDDMType *type)
522: {

528:   PetscUseMethod(ksp, "KSPHPDDMGetType_C", (KSP, KSPHPDDMType*), (ksp, type));
529:   return(0);
530: }

532: static PetscErrorCode KSPHPDDMSetType_HPDDM(KSP ksp, KSPHPDDMType type)
533: {
534:   KSP_HPDDM      *data = (KSP_HPDDM*)ksp->data;
535:   PetscInt       i;
536:   PetscBool      flg = PETSC_FALSE;

540:   for (i = 0; i < static_cast<PetscInt>(ALEN(KSPHPDDMTypes)); ++i) {
541:     PetscStrcmp(KSPHPDDMTypes[type], KSPHPDDMTypes[i], &flg);
542:     if (flg) break;
543:   }
544:   if (i == ALEN(KSPHPDDMTypes)) SETERRQ1(PetscObjectComm((PetscObject)ksp), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown KSPHPDDMType %s", type);
545:   if (data->cntl[0] != static_cast<char>(PETSC_DECIDE) && data->cntl[0] != i) {
546:     KSPHPDDMReset_Private(ksp);
547:   }
548:   data->cntl[0] = i;
549:   return(0);
550: }

552: static PetscErrorCode KSPHPDDMGetType_HPDDM(KSP ksp, KSPHPDDMType *type)
553: {
554:   KSP_HPDDM *data = (KSP_HPDDM*)ksp->data;

557:   if (data->cntl[0] == static_cast<char>(PETSC_DECIDE)) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "KSPHPDDMType not set yet");
558:   /* need to shift by -1 for HPDDM_KRYLOV_METHOD_NONE */
559:   *type = static_cast<KSPHPDDMType>(PetscMin(data->cntl[0], static_cast<char>(ALEN(KSPHPDDMTypes) - 1)));
560:   return(0);
561: }

563: /*MC
564:      KSPHPDDM - Interface with the HPDDM library.

566:    This KSP may be used to further select methods that are currently not implemented natively in PETSc, e.g., GCRODR [2006], a recycled Krylov method which is similar to KSPLGMRES, see [2016] for a comparison. ex75.c shows how to reproduce the results from the aforementioned paper [2006]. A chronological bibliography of relevant publications linked with KSP available in HPDDM through KSPHPDDM, and not available directly in PETSc, may be found below.

568:    Options Database Keys:
569: +   -ksp_gmres_restart <restart, default=30> - see KSPGMRES
570: .   -ksp_hpddm_type <type, default=gmres> - any of gmres, bgmres, cg, bcg, gcrodr, bgcrodr, bfbcg, or preonly, see KSPHPDDMType
571: .   -ksp_hpddm_deflation_tol <eps, default=\-1.0> - tolerance when deflating right-hand sides inside block methods (no deflation by default, only relevant with block methods)
572: .   -ksp_hpddm_enlarge_krylov_subspace <p, default=1> - split the initial right-hand side into multiple vectors (only relevant with nonblock methods)
573: .   -ksp_hpddm_orthogonalization <type, default=cgs> - any of cgs or mgs, see KSPGMRES
574: .   -ksp_hpddm_qr <type, default=cholqr> - distributed QR factorizations with any of cholqr, cgs, or mgs (only relevant with block methods)
575: .   -ksp_hpddm_variant <type, default=left> - any of left, right, or flexible (this option is superseded by KSPSetPCSide())
576: .   -ksp_hpddm_recycle <n, default=0> - number of harmonic Ritz vectors to compute (only relevant with GCRODR or BGCRODR)
577: .   -ksp_hpddm_recycle_target <type, default=SM> - criterion to select harmonic Ritz vectors using either SM, LM, SR, LR, SI, or LI (only relevant with GCRODR or BGCRODR). For BGCRODR, if PETSc is compiled with SLEPc, this option is not relevant, since SLEPc is used instead. Options are set with the prefix -ksp_hpddm_recycle_eps_
578: .   -ksp_hpddm_recycle_strategy <type, default=A> - generalized eigenvalue problem A or B to solve for recycling (only relevant with flexible GCRODR or BGCRODR)
579: -   -ksp_hpddm_recycle_symmetric <true, default=false> - symmetric generalized eigenproblems in BGCRODR, useful to switch to distributed solvers like EPSELEMENTAL (only relevant when PETSc is compiled with SLEPc)

581:    References:
582: +   1980 - The Block Conjugate Gradient Algorithm and Related Methods. O'Leary. Linear Algebra and its Applications.
583: .   2006 - Recycling Krylov Subspaces for Sequences of Linear Systems. Parks, de Sturler, Mackey, Johnson, and Maiti. SIAM Journal on Scientific Computing
584: .   2013 - A Modified Block Flexible GMRES Method with Deflation at Each Iteration for the Solution of Non-Hermitian Linear Systems with Multiple Right-Hand Sides. Calandra, Gratton, Lago, Vasseur, and Carvalho. SIAM Journal on Scientific Computing.
585: .   2016 - Block Iterative Methods and Recycling for Improved Scalability of Linear Solvers. Jolivet and Tournier. SC16.
586: -   2017 - A breakdown-free block conjugate gradient method. Ji and Li. BIT Numerical Mathematics.

588:    Level: intermediate

590: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPGMRES, KSPCG, KSPLGMRES, KSPDGMRES
591: M*/
592: PETSC_EXTERN PetscErrorCode KSPCreate_HPDDM(KSP ksp)
593: {
594:   KSP_HPDDM      *data;
595:   PetscInt       i;
596:   const char     *common[] = { KSPGMRES, KSPCG, KSPPREONLY };
597:   PetscBool      flg = PETSC_FALSE;

601:   PetscNewLog(ksp, &data);
602:   ksp->data = (void*)data;
603:   KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 2);
604:   KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 1);
605:   ksp->ops->solve          = KSPSolve_HPDDM;
606:   ksp->ops->matsolve       = KSPMatSolve_HPDDM;
607:   ksp->ops->setup          = KSPSetUp_HPDDM;
608:   ksp->ops->setfromoptions = KSPSetFromOptions_HPDDM;
609:   ksp->ops->destroy        = KSPDestroy_HPDDM;
610:   ksp->ops->view           = KSPView_HPDDM;
611:   ksp->ops->reset          = KSPReset_HPDDM;
612:   KSPHPDDMReset_Private(ksp);
613:   for (i = 0; i < static_cast<PetscInt>(ALEN(common)); ++i) {
614:     PetscStrcmp(((PetscObject)ksp)->type_name, common[i], &flg);
615:     if (flg) break;
616:   }
617:   if (!i) data->cntl[0] = HPDDM_KRYLOV_METHOD_GMRES;
618:   else if (i == 1) data->cntl[0] = HPDDM_KRYLOV_METHOD_CG;
619:   else if (i == 2) data->cntl[0] = HPDDM_KRYLOV_METHOD_NONE;
620:   if (data->cntl[0] != static_cast<char>(PETSC_DECIDE)) {
621:     PetscInfo1(ksp, "Using the previously set KSPType %s\n", common[i]);
622:   }
623:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetDeflationSpace_C", KSPHPDDMSetDeflationSpace_HPDDM);
624:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetDeflationSpace_C", KSPHPDDMGetDeflationSpace_HPDDM);
625:   PetscObjectComposeFunction((PetscObject)ksp, "KSPSetMatSolveBlockSize_C", KSPSetMatSolveBlockSize_HPDDM);
626:   PetscObjectComposeFunction((PetscObject)ksp, "KSPGetMatSolveBlockSize_C", KSPGetMatSolveBlockSize_HPDDM);
627:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMSetType_C", KSPHPDDMSetType_HPDDM);
628:   PetscObjectComposeFunction((PetscObject)ksp, "KSPHPDDMGetType_C", KSPHPDDMGetType_HPDDM);
629: #if defined(PETSC_HAVE_SLEPC) && defined(PETSC_USE_SHARED_LIBRARIES)
630:   if (!loadedDL) {
631:     HPDDMLoadDL_Private(&loadedDL);
632:   }
633: #endif
634:   return(0);
635: }