DALinearEqn.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2 
3  DAFoam : Discrete Adjoint with OpenFOAM
4  Version : v3
5 
6 \*---------------------------------------------------------------------------*/
7 
8 #include "DALinearEqn.H"
9 
10 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
11 
12 namespace Foam
13 {
14 
15 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
16 
17 DALinearEqn::DALinearEqn(
18  const fvMesh& mesh,
19  const DAOption& daOption)
20  : mesh_(mesh),
21  daOption_(daOption)
22 {
23 }
24 
25 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
26 
28  const Mat jacMat,
29  const Mat jacPCMat,
30  KSP ksp)
31 {
32  /*
33  Description:
34  This is the main function we need to call to initialize the KSP and set
35  up parameters for solving the linear equations
36 
37  Input:
38  gmresRestart: how many Krylov spaces to keep before resetting them.
39  Usually, this is set to the gmresMaxIters
40 
41  gmresMaxIters: how many GMRES iteration to run at most
42 
43  gmresRelTol: the relative tolerance for GMRES
44 
45  gmresAbsTol: the absolute tolerance for GMRES
46 
47  globalPCIters: globa iteration for PC, usually set it to 0
48 
49  asmOverlap: ASM overlap for solving the linearEqn in parallel.
50  Usually set it to 1. Setting a higher number increases the convergence but
51  significantly increase the memory usage
52 
53  localPCIters: local iteraction for PC. usually set it to 1
54 
55  jacMatReOrdering: re-order the lhs matrix to reduce memory usage.
56  Usually we use nd, rcm, or natural (not re-ordered)
57 
58  pcFillLevel: how many leve fill-in to use for PC. This is a critical
59  parameters for convergence rate. Usually set it to 1. Setting it to a higher
60  number increase the convergence, however, the memory usage generally grows
61  exponetially. We rarely set it more than 2.
62 
63  printInfo: whether to print summary information before solving
64 
65  jacMat: the right-hand-side petsc matrix
66 
67  jacPCMat: the preconditioner matrix from which we constructor our preconditioners
68 
69  Output:
70  genksp: the set KSP object
71  */
72 
73  label gmresRestart =
74  daOption_.getSubDictOption<label>("adjEqnOption", "gmresRestart");
75  label globalPCIters =
76  daOption_.getSubDictOption<label>("adjEqnOption", "globalPCIters");
77  label asmOverlap =
78  daOption_.getSubDictOption<label>("adjEqnOption", "asmOverlap");
79  label localPCIters =
80  daOption_.getSubDictOption<label>("adjEqnOption", "localPCIters");
81  word jacMatReOrdering =
82  daOption_.getSubDictOption<word>("adjEqnOption", "jacMatReOrdering");
83  label pcFillLevel =
84  daOption_.getSubDictOption<label>("adjEqnOption", "pcFillLevel");
85  label gmresMaxIters =
86  daOption_.getSubDictOption<label>("adjEqnOption", "gmresMaxIters");
87  scalar gmresRelTol =
88  daOption_.getSubDictOption<scalar>("adjEqnOption", "gmresRelTol");
89  scalar gmresAbsTol =
90  daOption_.getSubDictOption<scalar>("adjEqnOption", "gmresAbsTol");
91  label useNonZeroInitGuess =
92  daOption_.getSubDictOption<label>("adjEqnOption", "useNonZeroInitGuess");
93  label useMGSO =
94  daOption_.getSubDictOption<label>("adjEqnOption", "useMGSO");
95  label printInfo =
96  daOption_.getSubDictOption<label>("adjEqnOption", "printInfo");
97 
98  PC MLRMasterPC, MLRGlobalPC;
99  PC MLRsubpc;
100  KSP MLRMasterPCKSP;
101  KSP* MLRsubksp;
102  // ASM Preconditioner variables
103  PetscInt MLRoverlap; // width of subdomain overlap
104  PetscInt MLRnlocal, MLRfirst; // number of local subblocks, first local subblock
105 
106  // Create linear solver context
107  //KSPCreate(PETSC_COMM_WORLD, &ksp);
108 
109  // Set operators. Here the matrix that defines the linear
110  // system also serves as the preconditioning matrix.
111  KSPSetOperators(ksp, jacMat, jacPCMat);
112 
113  // This code sets up the supplied kspObject in the following
114  // specific fashion.
115  //
116  // The hierarchy of the setup is:
117  // kspObject --> Supplied KSP object
118  // |
119  // --> master_PC --> Preconditioner type set to KSP
120  // |
121  // --> master_PC_KSP --> KSP type set to Richardson with 'globalPreConIts'
122  // |
123  // --> globalPC --> PC type set to 'globalPCType'
124  // | Usually Additive Schwartz and overlap is set
125  // | with 'ASMOverlap'. Use 0 to get BlockJacobi
126  // |
127  // --> subKSP --> KSP type set to Richardon with 'LocalPreConIts'
128  // |
129  // --> subPC --> PC type set to 'localPCType'.
130  // Usually ILU. 'localFillLevel' is
131  // set and 'localMatrixOrder' is used.
132  //
133  // Note that if globalPreConIts=1 then maser_PC_KSP is NOT created and master_PC=globalPC
134  // and if localPreConIts=1 then subKSP is set to preOnly.
135 
136  // First, KSPSetFromOptions MUST be called
137  KSPSetFromOptions(ksp);
138 
139  // Set GMRES
140  // Set the type of solver to GMRES
141  KSPType kspObjectType = KSPGMRES;
142 
143  KSPSetType(ksp, kspObjectType);
144  // Set the gmres restart
145  PetscInt restartGMRES = gmresRestart;
146 
147  // whether to use non-zero initial guess
148  if (useNonZeroInitGuess)
149  {
150  KSPSetInitialGuessNonzero(ksp, PETSC_TRUE);
151  }
152  else
153  {
154  KSPSetInitialGuessNonzero(ksp, PETSC_FALSE);
155  }
156 
157  KSPGMRESSetRestart(ksp, restartGMRES);
158  // Set the GMRES refinement type
159  KSPGMRESSetCGSRefinementType(ksp, KSP_GMRES_CGS_REFINE_IFNEEDED);
160 
161  // set orthogonalization for the GMRES, useMGSO=1: modified Gram Schmidt
162  // useMGSO=0: classical Gram Schmidt
163  if (useMGSO)
164  {
165  KSPGMRESSetOrthogonalization(ksp, KSPGMRESModifiedGramSchmidtOrthogonalization);
166  }
167 
168  // Set the preconditioner side
169  KSPSetPCSide(ksp, PC_RIGHT);
170 
171  // Set global and local PC iters
172  PetscInt globalPreConIts = globalPCIters;
173 
174  // Since there is an extraneous matMult required when using the
175  // richardson precondtiter with only 1 iteration, only use it when we need
176  // to do more than 1 iteration.
177  if (globalPreConIts > 1)
178  {
179  // Extract preconditioning context for main KSP solver: (MLRMasterPC)
180  KSPGetPC(ksp, &MLRMasterPC);
181 
182  // Set the type of MLRMasterPC to ksp. This lets us do multiple
183  // iterations of preconditioner application
184  PCSetType(MLRMasterPC, PCKSP);
185 
186  // Get the ksp context from MLRMasterPC which is the actual preconditioner:
187  PCKSPGetKSP(MLRMasterPC, &MLRMasterPCKSP);
188 
189  // MLRMasterPCKSP type will always be of type richardson. If the
190  // number of iterations is set to 1, this ksp object is transparent.
191  KSPSetType(MLRMasterPCKSP, KSPRICHARDSON);
192 
193  // Important to set the norm-type to None for efficiency.
194  KSPSetNormType(MLRMasterPCKSP, KSP_NORM_NONE);
195 
196  // Do one iteration of the outer ksp preconditioners. Note the
197  // tolerances are unsued since we have set KSP_NORM_NONE
198  KSPSetTolerances(MLRMasterPCKSP, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, globalPreConIts);
199 
200  // Get the 'preconditioner for MLRMasterPCKSP, called 'MLRGlobalPC'. This
201  // preconditioner is potentially run multiple times.
202  KSPGetPC(MLRMasterPCKSP, &MLRGlobalPC);
203  }
204  else
205  {
206  // Just pull out the pc-object if we are not using kspRichardson
207  KSPGetPC(ksp, &MLRGlobalPC);
208  }
209 
210  // Set the type of 'MLRGlobalPC'. This will almost always be additive schwartz
211  PCSetType(MLRGlobalPC, PCASM);
212 
213  // Set the overlap required
214  MLRoverlap = asmOverlap;
215  PCASMSetOverlap(MLRGlobalPC, MLRoverlap);
216 
217  //label KSPCalcEigen = readLabel(options.lookup("KSPCalcEigen"));
218  //if (KSPCalcEigen)
219  //{
220  // KSPSetComputeEigenvalues(*genksp, PETSC_TRUE);
221  //}
222 
223  //Setup the main ksp context before extracting the subdomains
224  KSPSetUp(ksp);
225 
226  // Extract the ksp objects for each subdomain
227  PCASMGetSubKSP(MLRGlobalPC, &MLRnlocal, &MLRfirst, &MLRsubksp);
228 
229  //Loop over the local blocks, setting various KSP options
230  //for each block.
231  PetscInt localPreConIts = localPCIters;
232  word matOrdering = jacMatReOrdering;
233  PetscInt localFillLevel = pcFillLevel;
234  for (PetscInt i = 0; i < MLRnlocal; i++)
235  {
236  // Since there is an extraneous matMult required when using the
237  // richardson precondtiter with only 1 iteration, only use it we need
238  // to do more than 1 iteration.
239  if (localPreConIts > 1)
240  {
241  // This 'subksp' object will ALSO be of type richardson so we can do
242  // multiple iterations on the sub-domains
243  KSPSetType(MLRsubksp[i], KSPRICHARDSON);
244 
245  // Set the number of iterations to do on local blocks. Tolerances are ignored.
246  KSPSetTolerances(MLRsubksp[i], PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, localPreConIts);
247 
248  // Again, norm_type is NONE since we don't want to check error
249  KSPSetNormType(MLRsubksp[i], KSP_NORM_NONE);
250  }
251  else
252  {
253  KSPSetType(MLRsubksp[i], KSPPREONLY);
254  }
255 
256  // Extract the preconditioner for subksp object.
257  KSPGetPC(MLRsubksp[i], &MLRsubpc);
258 
259  // The subpc type will almost always be ILU
260  PCType localPCType = PCILU;
261  PCSetType(MLRsubpc, localPCType);
262 
263  // Set PC factor
264  PCFactorSetPivotInBlocks(MLRsubpc, PETSC_TRUE);
265  PCFactorSetShiftType(MLRsubpc, MAT_SHIFT_NONZERO);
266  PCFactorSetShiftAmount(MLRsubpc, PETSC_DECIDE);
267 
268  // Setup the matrix ordering for the subpc object:
269  // 'natural':'natural',
270  // 'rcm':'rcm',
271  // 'nested dissection':'nd' (default),
272  // 'one way dissection':'1wd',
273  // 'quotient minimum degree':'qmd',
274  MatOrderingType localMatrixOrdering;
275  if (matOrdering == "natural")
276  {
277  localMatrixOrdering = MATORDERINGNATURAL;
278  }
279  else if (matOrdering == "nd")
280  {
281  localMatrixOrdering = MATORDERINGND;
282  }
283  else if (matOrdering == "rcm")
284  {
285  localMatrixOrdering = MATORDERINGRCM;
286  }
287  else if (matOrdering == "1wd")
288  {
289  localMatrixOrdering = MATORDERING1WD;
290  }
291  else if (matOrdering == "qmd")
292  {
293  localMatrixOrdering = MATORDERINGQMD;
294  }
295  else
296  {
297  Info << "matOrdering not known. Using default: nested dissection" << endl;
298  localMatrixOrdering = MATORDERINGND;
299  }
300  PCFactorSetMatOrderingType(MLRsubpc, localMatrixOrdering);
301 
302  // Set the ILU parameters
303  PCFactorSetLevels(MLRsubpc, localFillLevel);
304  }
305 
306  // Set the norm to unpreconditioned
307  KSPSetNormType(ksp, KSP_NORM_UNPRECONDITIONED);
308  // Setup monitor if necessary:
309  if (printInfo)
310  {
311  KSPMonitorSet(ksp, myKSPMonitor, this, 0);
312  }
313 
314  PetscInt maxIts = gmresMaxIters;
315  PetscScalar rtol, atol;
316  assignValueCheckAD(rtol, gmresRelTol);
317  assignValueCheckAD(atol, gmresAbsTol);
318  KSPSetTolerances(ksp, rtol, atol, PETSC_DEFAULT, maxIts);
319 
320  if (printInfo)
321  {
322  Info << "Solver Type: " << kspObjectType << endl;
323  Info << "GMRES Restart: " << restartGMRES << endl;
324  Info << "ASM Overlap: " << MLRoverlap << endl;
325  Info << "Global PC Iters: " << globalPreConIts << endl;
326  Info << "Local PC Iters: " << localPreConIts << endl;
327  Info << "Mat ReOrdering: " << matOrdering << endl;
328  Info << "ILU PC Fill Level: " << localFillLevel << endl;
329  Info << "GMRES Max Iterations: " << maxIts << endl;
330  Info << "GMRES Relative Tolerance: " << rtol << endl;
331  Info << "GMRES Absolute Tolerance: " << atol << endl;
332  }
333 }
334 
336  const KSP ksp,
337  const Vec rhsVec,
338  Vec solVec)
339 {
340  /*
341  Description:
342  Solve a linear equation.
343 
344  Input:
345  ksp: the KSP object, obtained from calling Foam::createMLRKSP
346 
347  rhsVec: the right-hand-side petsc vector
348 
349  Output:
350  solVec: the solution vector
351 
352  Return 0 if the linear equation solution finished successfully otherwise return 1
353  */
354 
355  Info << "Solving Linear Equation... " << this->getRunTime() << " s" << endl;
356 
357  //Solve adjoint
358  // VecZeroEntries(solVec);
359 
360  // set up rGMRESHist to save the tolerance history for the GMRES solution
361  // these vars are for store the tolerance for GMRES linear solution
362  label gmresMaxIters = daOption_.getSubDictOption<label>("adjEqnOption", "gmresMaxIters");
363  PetscScalar rGMRESHist[gmresMaxIters + 1];
364  label nGMRESIters = gmresMaxIters + 1;
365  KSPSetResidualHistory(ksp, rGMRESHist, nGMRESIters, PETSC_TRUE);
366 
367  // solve KSP
368  KSPSolve(ksp, rhsVec, solVec);
369 
370  //Print convergence information
371  label its;
372  KSPGetIterationNumber(ksp, &its);
373  PetscScalar initResNorm = rGMRESHist[0];
374  PetscScalar finalResNorm = rGMRESHist[its];
375  PetscPrintf(
376  PETSC_COMM_WORLD,
377  "Main iteration %D KSP Residual norm %14.12e %d s \n",
378  its,
379  finalResNorm,
380  this->getRunTime());
381  PetscPrintf(PETSC_COMM_WORLD, "Total iterations %D\n", its);
382 
383  VecAssemblyBegin(solVec);
384  VecAssemblyEnd(solVec);
385 
386  Info << "Solving Linear Equation... Completed! "
387  << this->getRunTime() << " s" << endl;
388 
389  // now we need to check if the linear equation solution is successful
390 
391  scalar absResRatio = finalResNorm / daOption_.getSubDictOption<scalar>("adjEqnOption", "gmresAbsTol");
392  scalar relResRatio = finalResNorm / initResNorm / daOption_.getSubDictOption<scalar>("adjEqnOption", "gmresRelTol");
393  scalar resDiff = daOption_.getSubDictOption<scalar>("adjEqnOption", "gmresTolDiff");
394  if (relResRatio > resDiff && absResRatio > resDiff)
395  {
396  Info << "Residual tolerance not satisfied, solution failed!" << endl;
397  return 1;
398  }
399  else
400  {
401  Info << "Residual tolerance satisfied, solution finished!" << endl;
402  return 0;
403  }
404 
405  return 1;
406 }
407 
409  KSP ksp,
410  PetscInt n,
411  PetscReal rnorm,
412  void* ctx)
413 {
414 
415  /*
416  Descripton:
417  Write the solution vector and residual norm to stdout.
418  - PetscPrintf() handles output for multiprocessor jobs
419  by printing from only one processor in the communicator.
420  - The parallel viewer PETSC_VIEWER_STDOUT_WORLD handles
421  data from multiple processors so that the output
422  is not jumbled.
423  */
424 
425  DALinearEqn* daLinearEqn = (DALinearEqn*)ctx;
426 
427  // residual print frequency
428  PetscInt printFrequency = daLinearEqn->getPrintInterval();
429  if (n % printFrequency == 0)
430  {
431  PetscPrintf(
432  PETSC_COMM_WORLD,
433  "Main iteration %D KSP Residual norm %14.12e %d s\n",
434  n,
435  rnorm,
436  daLinearEqn->getRunTime());
437  }
438  return 0;
439 }
440 
442 {
443  /*
444  Descripton:
445  Return the runtime
446  */
447  return mesh_.time().elapsedClockTime();
448 }
449 
451 {
452  /*
453  Descripton:
454  Return the printInterval from DAOption
455  */
456  return daOption_.getOption<label>("printInterval");
457 }
458 
459 } // End namespace Foam
460 
461 // ************************************************************************* //
DALinearEqn.H
Foam::DALinearEqn::solveLinearEqn
label solveLinearEqn(const KSP ksp, const Vec rhsVec, Vec solVec)
solve the linear equation given a ksp and right-hand-side vector
Definition: DALinearEqn.C:335
Foam::DAOption
Definition: DAOption.H:29
daOption
DAOption daOption(mesh, pyOptions_)
Foam::DAOption::getOption
classType getOption(const word key) const
get an option from subDict and key
Definition: DAOption.H:92
mesh
fvMesh & mesh
Definition: createRefsHeatTransfer.H:4
Foam::DAOption::getSubDictOption
classType getSubDictOption(const word subDict, const word subDictKey) const
get an dictionary option from subDict and key
Definition: DAOption.H:165
assignValueCheckAD
#define assignValueCheckAD(valA, valB)
Definition: DAMacroFunctions.H:103
Foam
Definition: multiFreqScalarFvPatchField.C:144
Foam::DALinearEqn::mesh_
const fvMesh & mesh_
Foam::fvMesh object.
Definition: DALinearEqn.H:43
Foam::DALinearEqn::getPrintInterval
label getPrintInterval()
return printInterval from DAOption
Definition: DALinearEqn.C:450
Foam::DALinearEqn::getRunTime
label getRunTime()
return the runtime for the adjoint solver
Definition: DALinearEqn.C:441
Foam::DALinearEqn
Definition: DALinearEqn.H:31
Foam::DALinearEqn::myKSPMonitor
static PetscErrorCode myKSPMonitor(KSP, PetscInt, PetscReal, void *)
ksp monitor function
Definition: DALinearEqn.C:408
Foam::DALinearEqn::daOption_
const DAOption & daOption_
Foam::DAOption object.
Definition: DALinearEqn.H:46
Foam::DALinearEqn::createMLRKSP
void createMLRKSP(const Mat jacMat, const Mat jacPCMat, KSP ksp)
create a multi-level, Richardson KSP object
Definition: DALinearEqn.C:27