Nektar++
DriverParareal.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File DriverParareal.cpp
4 //
5 // For more information, please see: http://www.nektar.info
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description: Driver class for the parareal solver
32 //
33 ///////////////////////////////////////////////////////////////////////////////
34 
35 #include <iomanip>
36 
39 
40 using namespace std;
41 
42 namespace Nektar
43 {
44 namespace SolverUtils
45 {
46 string DriverParareal::className = GetDriverFactory().RegisterCreatorFunction(
47  "Parareal", DriverParareal::create);
48 string DriverParareal::driverLookupId =
49  LibUtilities::SessionReader::RegisterEnumValue("Driver", "Parareal", 0);
50 
51 /**
52  *
53  */
54 DriverParareal::DriverParareal(
57  : Driver(pSession, pGraph)
58 {
59 }
60 
61 /**
62  *
63  */
65 {
66 }
67 
68 /**
69  *
70  */
71 void DriverParareal::v_InitObject(ostream &out)
72 {
73  try
74  {
75  // Retrieve the equation system to solve.
76  ASSERTL0(m_session->DefinesSolverInfo("EqType"),
77  "EqType SolverInfo tag must be defined.");
78  std::string vEquation = m_session->GetSolverInfo("EqType");
79  if (m_session->DefinesSolverInfo("SolverType"))
80  {
81  vEquation = m_session->GetSolverInfo("SolverType");
82  }
83 
84  // Check such a module exists for this equation.
85  ASSERTL0(
86  GetEquationSystemFactory().ModuleExists(vEquation),
87  "EquationSystem '" + vEquation +
88  "' is not defined.\n"
89  "Ensure equation name is correct and module is compiled.\n");
90 
91  // Retrieve the type of evolution operator to use
92  /// @todo At the moment this is Navier-Stokes specific - generalise?
94  m_session->GetSolverInfoAsEnum<EvolutionOperatorType>(
95  "EvolutionOperator");
96 
97  m_nequ = 2;
98 
100 
101  // Set the AdvectiveType tag and create EquationSystem objects.
102  switch (m_EvolutionOperator)
103  {
104  case eNonlinear:
105  // Set coarse parareal session file.
107 
108  // Set fine parareal solver.
109  m_session->SetTag("AdvectiveType", "Convective");
110  m_session->SetTag("PararealSolver", "FineSolver");
112  vEquation, m_session, m_graph);
113 
114  // Set coarse parareal solver.
115  m_sessionCoarse->SetTag("AdvectiveType", "Convective");
116  m_sessionCoarse->SetTag("PararealSolver", "CoarseSolver");
118  vEquation, m_sessionCoarse, m_graphCoarse);
119  break;
120  case eDirect:
121  // Set coarse parareal session file.
123 
124  // Set fine parareal solver.
125  m_session->SetTag("AdvectiveType", "Linearised");
126  m_session->SetTag("PararealSolver", "FineSolver");
128  vEquation, m_session, m_graph);
129 
130  // Set coarse parareal solver.
131  m_sessionCoarse->SetTag("AdvectiveType", "Linearised");
132  m_sessionCoarse->SetTag("PararealSolver", "CoarseSolver");
134  vEquation, m_sessionCoarse, m_graphCoarse);
135  break;
136  case eAdjoint:
137  // Set coarse parareal session file.
139 
140  // Set fine parareal solver.
141  m_session->SetTag("AdvectiveType", "Adjoint");
142  m_session->SetTag("PararealSolver", "FineSolver");
144  vEquation, m_session, m_graph);
145 
146  // Set coarse parareal solver.
147  m_sessionCoarse->SetTag("AdvectiveType", "Adjoint");
148  m_sessionCoarse->SetTag("PararealSolver", "CoarseSolver");
150  vEquation, m_sessionCoarse, m_graphCoarse);
151  break;
152  case eSkewSymmetric:
153  // Set coarse parareal session file.
155 
156  // Set fine parareal solver.
157  m_session->SetTag("AdvectiveType", "SkewSymmetric");
158  m_session->SetTag("PararealSolver", "FineSolver");
160  vEquation, m_session, m_graph);
161 
162  // Set coarse parareal solver.
163  m_sessionCoarse->SetTag("AdvectiveType", "SkewSymmetric");
164  m_sessionCoarse->SetTag("PararealSolver", "CoarseSolver");
166  vEquation, m_sessionCoarse, m_graphCoarse);
167  break;
168  default:
169  ASSERTL0(false, "Unrecognised evolution operator.");
170  }
171  }
172  catch (int e)
173  {
174  ASSERTL0(e == -1, "No such class class defined.");
175  out << "An error occurred during driver initialisation." << endl;
176  }
177 }
178 
179 /// Set the Parareal (coarse solver) session file
181 {
182  // Get the coarse solver session file.
183  string meshFile;
184  string coarseSolverFile;
185  vector<string> coarseSolverFilenames;
186  bool opt = (m_session->GetFilenames()[0].substr(
187  m_session->GetFilenames()[0].size() - 3) == "opt");
188  meshFile = m_session->GetFilenames()[0 + opt];
189  coarseSolverFile = m_session->GetFilenames().size() > 1 + opt
190  ? m_session->GetFilenames()[1 + opt]
191  : m_session->GetFilenames()[0 + opt];
192  coarseSolverFile = coarseSolverFile.substr(0, coarseSolverFile.size() - 4);
193  coarseSolverFile += "_coarseSolver.xml";
194  std::ifstream f(coarseSolverFile);
195 
196  if (f.good())
197  {
198  // if _coarseSolver.xml exit, read session file
199  if (m_session->GetFilenames().size() > 1 + opt)
200  {
201  coarseSolverFilenames.push_back(meshFile);
202  }
203  coarseSolverFilenames.push_back(coarseSolverFile);
204  }
205  else
206  {
207  // if _coarseSolver.xml does not exit, use original session file
208  coarseSolverFilenames.push_back(m_session->GetFilenames()[0 + opt]);
209  if (m_session->GetFilenames().size() > 1 + opt)
210  {
211  coarseSolverFilenames.push_back(m_session->GetFilenames()[1 + opt]);
212  }
213  }
214 
215  // Define argument for the coarse parareal solver.
216  int npx = m_session->DefinesCmdLineArgument("npx")
217  ? m_session->GetCmdLineArgument<int>("npx")
218  : 1;
219  int npy = m_session->DefinesCmdLineArgument("npy")
220  ? m_session->GetCmdLineArgument<int>("npy")
221  : 1;
222  int npz = m_session->DefinesCmdLineArgument("npz")
223  ? m_session->GetCmdLineArgument<int>("npz")
224  : 1;
225  int nsz = m_session->DefinesCmdLineArgument("nsz")
226  ? m_session->GetCmdLineArgument<int>("nsz")
227  : 1;
228  int npt = m_session->DefinesCmdLineArgument("npt")
229  ? m_session->GetCmdLineArgument<int>("npt")
230  : 1;
231 
232  // Convert into string.
233  std::string npx_string = std::to_string(npx);
234  std::string npy_string = std::to_string(npy);
235  std::string npz_string = std::to_string(npz);
236  std::string nsz_string = std::to_string(nsz);
237  std::string npt_string = std::to_string(npt);
238 
239  char *argv[] = {const_cast<char *>("Solver"), // this is just a place holder
240  const_cast<char *>("--npx"),
241  const_cast<char *>(npx_string.c_str()),
242  const_cast<char *>("--npy"),
243  const_cast<char *>(npy_string.c_str()),
244  const_cast<char *>("--npz"),
245  const_cast<char *>(npz_string.c_str()),
246  const_cast<char *>("--nsz"),
247  const_cast<char *>(nsz_string.c_str()),
248  const_cast<char *>("--npt"),
249  const_cast<char *>(npt_string.c_str()),
250  nullptr};
251 
252  // Set session for coarse solver.
254  11, argv, coarseSolverFilenames, m_session->GetComm());
255 
256  // Set graph for coarse solver.
258 
259  // Set BndRegionOrdering (necessary for DG with periodic BC) FIXME
260  m_graphCoarse->SetBndRegionOrdering(m_graph->GetBndRegionOrdering());
261 
262  // Set CompositeOrdering (necessary for DG with periodic BC) FIXME
263  m_graphCoarse->SetCompositeOrdering(m_graph->GetCompositeOrdering());
264 
265  // If a coarse solver session file is not specified, use
266  // m_coarseSolveFactor to determine the timestep of the coarse solver
267  // (default value is 100.0).
268  if (!f.good())
269  {
270  double timeStep =
271  m_session->GetParameter("TimeStep") * m_coarseSolveFactor;
272  int numSteps =
273  m_session->GetParameter("NumSteps") / m_coarseSolveFactor;
274  m_sessionCoarse->SetParameter("TimeStep", timeStep);
275  m_sessionCoarse->SetParameter("NumSteps", numSteps);
276  }
277 }
278 
280  const NekDouble time, const int nstep, const int iter,
281  const Array<OneD, const Array<OneD, NekDouble>> &input,
283 {
284  // Output coarse timestep.
285  if (m_chunkRank == iter && m_comm->GetSpaceComm()->GetRank() == 0)
286  {
287  std::cout << "RUNNING COARSE SOLVE: dt = " << m_coarseTimeStep
288  << " nsteps = " << m_coarseSteps / m_numChunks << std::endl
289  << std::flush;
290  }
291 
292  // Set to coarse timestep.
293  m_equ[1]->SetTime(time);
294  m_equ[1]->SetSteps(nstep);
295 
296  // Copy initial condition from input.
297  if (m_equ[0]->GetNpoints() == m_equ[1]->GetNpoints())
298  {
299  // Interpolation not necessary, directly copy data.
300  for (int i = 0; i < m_equ[1]->GetNvariables(); ++i)
301  {
302  m_equ[1]->CopyToPhysField(i, input[i]);
303  }
304  }
305  else
306  {
307  // Copy data to fine solver and interpolate from fine to coarse (WIP).
308  for (int i = 0; i < m_equ[0]->GetNvariables(); ++i)
309  {
310  m_equ[0]->CopyToPhysField(i, input[i]);
311  }
312  m_interp.InterpExp1ToExp2(m_equ[0]->UpdateFields(),
313  m_equ[1]->UpdateFields());
314  }
315 
316  // Solve equations.
317  m_equ[1]->DoSolve();
318 
319  // Copy solution to output.
320  if (m_equ[0]->GetNpoints() == m_equ[1]->GetNpoints())
321  {
322  // Interpolation not necessary, directly copy data.
323  for (int i = 0; i < m_equ[1]->GetNvariables(); ++i)
324  {
325  m_equ[1]->CopyFromPhysField(i, output[i]);
326  }
327  }
328  else
329  {
330  // Copy data from coarse solver and interpolate coarse to fine (WIP).
331  m_interp.InterpExp1ToExp2(m_equ[1]->UpdateFields(),
332  m_equ[0]->UpdateFields());
333  for (int i = 0; i < m_equ[1]->GetNvariables(); ++i)
334  {
335  m_equ[0]->CopyFromPhysField(i, output[i]);
336  }
337  }
338 }
339 
341  const NekDouble time, const int nstep, const int iter,
342  const Array<OneD, const Array<OneD, NekDouble>> &input,
344 {
345  // Output fine timestep.
346  if (m_chunkRank == iter && m_comm->GetSpaceComm()->GetRank() == 0)
347  {
348  std::cout << "RUNNING FINE SOLVE: dt = " << m_fineTimeStep
349  << " nsteps = " << m_fineSteps / m_numChunks << std::endl
350  << std::flush;
351  }
352 
353  // Number of checkpoint by chunk.
354  int nChkPts =
355  m_session->GetParameter("IO_CheckSteps")
356  ? m_fineSteps /
357  int(m_session->GetParameter("IO_CheckSteps") * m_numChunks)
358  : 1;
359 
360  // Parareal iteration number.
361  int nIter = m_equ[0]->GetPararealIterationNumber();
362 
363  // Set to fine timestep.
364  m_equ[0]->SetTime(time);
365  m_equ[0]->SetSteps(nstep);
366 
367  // Reinitialize check point number for each parareal iteration.
368  m_equ[0]->SetCheckpointNumber(m_chunkRank * nChkPts + 1);
369 
370  // Update parareal iteration number.
371  m_equ[0]->SetPararealIterationNumber(++nIter);
372 
373  // Copy initial condition from input.
374  for (int i = 0; i < m_equ[0]->GetNvariables(); ++i)
375  {
376  m_equ[0]->CopyToPhysField(i, input[i]);
377  }
378 
379  // Solve equations.
380  m_equ[0]->DoSolve();
381 
382  // Copy solution to output.
383  for (int i = 0; i < m_equ[0]->GetNvariables(); ++i)
384  {
385  m_equ[0]->CopyFromPhysField(i, output[i]);
386  }
387 }
388 
389 void DriverParareal::v_Execute(ostream &out)
390 {
392  NekDouble CPUtime = 0.0;
393 
394  m_numChunks = m_session->GetComm()->GetTimeComm()->GetSize();
395  m_chunkRank = m_session->GetComm()->GetTimeComm()->GetRank();
396 
397  // Set parameters from session file.
398  m_pararealToler = m_session->DefinesParameter("PararealToler")
399  ? m_session->GetParameter("PararealToler")
400  : m_pararealToler;
401  m_pararealIterMax = m_session->DefinesParameter("PararealIterMax")
402  ? m_session->GetParameter("PararealIterMax")
403  : m_numChunks;
404  m_fineTimeStep = m_session->GetParameter("TimeStep");
405  m_coarseTimeStep = m_sessionCoarse->GetParameter("TimeStep");
406  m_fineSteps = m_session->GetParameter("NumSteps");
407  m_coarseSteps = m_sessionCoarse->GetParameter("NumSteps");
410  m_exactSolution = m_session->DefinesParameter("ExactSolution")
411  ? m_session->GetParameter("ExactSolution")
412  : m_exactSolution;
413 
414  // Turnoff I/O for coarse solver.
415  m_equ[1]->SetInfoSteps(0);
416  m_equ[1]->SetCheckpointSteps(0);
417 
418  // Check time step inputs.
419  ASSERTL0(
420  m_fineSteps % m_numChunks == 0,
421  "Total number of fine step should be divisible by number of chunks.");
422 
423  ASSERTL0(
424  m_coarseSteps % m_numChunks == 0,
425  "Total number of coarse step should be divisible by number of chunks.");
426 
428  m_fineTimeStep * m_fineSteps) < 1e-12,
429  "Fine and coarse total computational times do not match");
430 
431  // Check I/O inputs.
432  if (m_session->GetParameter("IO_InfoSteps"))
433  {
434  ASSERTL0(m_fineSteps % int(m_session->GetParameter("IO_InfoSteps") *
435  m_numChunks) ==
436  0,
437  "number of IO_InfoSteps should divide number of fine steps "
438  "per time chunk");
439  }
440  if (m_session->GetParameter("IO_CheckSteps"))
441  {
442  ASSERTL0(m_fineSteps % int(m_session->GetParameter("IO_CheckSteps") *
443  m_numChunks) ==
444  0,
445  "number of IO_CheckSteps should divide number of fine steps "
446  "per time chunk");
447  }
448 
449  // Fine solver summary.
450  if (m_chunkRank == 0 && m_comm->GetSpaceComm()->GetRank() == 0)
451  {
452  std::cout << "========================================================="
453  "=============="
454  << std::endl
455  << std::flush;
456  std::cout << "========================= FINE PROPAGATOR INFO "
457  "========================"
458  << std::endl
459  << std::flush;
460  m_equ[0]->PrintSummary(out);
461 
462  std::cout << std::endl << std::flush;
463  }
464 
465  // Coarse solver summary.
466  if (m_chunkRank == 0 && m_comm->GetSpaceComm()->GetRank() == 0)
467  {
468  std::cout << "========================================================="
469  "=============="
470  << std::endl
471  << std::flush;
472  std::cout << "======================== COARSE PROPAGATOR INFO "
473  "======================="
474  << std::endl
475  << std::flush;
476  m_equ[1]->PrintSummary(out);
477  }
478 
479  timer.Start();
480 
481  // Allocate storage for parareal solver.
482  int nPts = m_equ[0]->GetNpoints();
483  int nVar = m_equ[0]->GetNvariables();
484  Array<OneD, Array<OneD, NekDouble>> solution(nVar);
485  Array<OneD, Array<OneD, NekDouble>> solutionCoarsePrev(nVar);
486  Array<OneD, Array<OneD, NekDouble>> solutionCoarseCurr(nVar);
488  Array<OneD, Array<OneD, NekDouble>> exactsoln(nVar);
489  for (int i = 0; i < nVar; ++i)
490  {
491  solution[i] = Array<OneD, NekDouble>(nPts, 0.0);
492  solutionCoarsePrev[i] = Array<OneD, NekDouble>(nPts, 0.0);
493  solutionCoarseCurr[i] = Array<OneD, NekDouble>(nPts, 0.0);
494  ic[i] = Array<OneD, NekDouble>(nPts, 0.0);
495  exactsoln[i] = Array<OneD, NekDouble>(nPts, 0.0);
496  }
497 
498  // Initialize fine solver.
499  m_equ[0]->DoInitialise();
500 
501  // Initialize coarse solver.
502  m_equ[1]->SetUseInitialCondition(false);
503  m_equ[1]->DoInitialise();
504 
505  // Get initial conditions.
506  for (int i = 0; i < nVar; ++i)
507  {
508  m_equ[0]->CopyFromPhysField(i, ic[i]);
509  }
510 
511  // Run coarse solution, G(y_j^k) to get initial conditions.
512  if (m_chunkRank == 0 && m_comm->GetSpaceComm()->GetRank() == 0)
513  {
514  std::cout << "** INITIAL CONDITION **" << std::endl << std::flush;
515  }
516  LibUtilities::CommSharedPtr tComm = m_session->GetComm()->GetTimeComm();
517  int recvProc = m_chunkRank - 1;
518  int sendProc = m_chunkRank + 1;
519 
520  // Iterate to improve on the approximation
521  // For the moment, we use the maximum number of iterations
522  // We can add a tolerance threshold later.
523  // We start the iteration with the current approximation stored in
524  // the 'solution' array.
525  int k = 0;
526  int kmax = 0;
527  int convergenceCurr = false;
528  int convergencePrev = (m_chunkRank == 0);
529  NekDouble vL2Error = 0.0;
530  Array<OneD, NekDouble> vL2Errors(nVar, 0.0);
531  Array<OneD, NekDouble> vLinfErrors(nVar, 0.0);
532 
533  // To compute the initial conditions, the coarse solver is run serially
534  // for each time chunk to avoid communication time between processors.
535  if (m_chunkRank > 0)
536  {
538  ic);
539  }
540 
541  // Compute initial coarse solution.
542  if (m_chunkRank == 0 && m_comm->GetSpaceComm()->GetRank() == 0)
543  {
544  std::cout << "** ITERATION " << 0 << " **" << std::endl << std::flush;
545  }
546 
548  ic, solutionCoarsePrev);
549 
550  for (int i = 0; i < nVar; ++i)
551  {
552  for (int q = 0; q < nPts; ++q)
553  {
554  solution[i][q] = solutionCoarsePrev[i][q];
555  }
556  }
557 
558  if (m_exactSolution)
559  {
560  // Evaluate exact solution.
561  for (int i = 0; i < nVar; ++i)
562  {
563  m_equ[0]->EvaluateExactSolution(i, exactsoln[i],
564  (m_chunkRank + 1) * m_chunkTime);
565  }
566  }
567 
568  timer.Stop();
569  CPUtime += timer.Elapsed().count();
570  if (m_chunkRank == m_numChunks - 1 &&
571  m_comm->GetSpaceComm()->GetRank() == 0)
572  {
573  std::cout << "-------------------------------------------" << std::endl
574  << std::flush;
575  std::cout << "Total Computation Time = " << CPUtime << "s" << std::endl
576  << std::flush;
577  std::cout << "-------------------------------------------" << std::endl
578  << std::flush;
579  }
580  for (int i = 0; i < nVar; ++i)
581  {
582  vL2Errors[i] = m_equ[1]->L2Error(i, exactsoln[i], 1);
583  vLinfErrors[i] = m_equ[1]->LinfError(i, exactsoln[i]);
584  if (m_chunkRank == m_numChunks - 1 &&
585  m_comm->GetSpaceComm()->GetRank() == 0)
586  {
587  std::cout << "L2 error (variable " << m_equ[1]->GetVariable(i)
588  << ") : " << vL2Errors[i] << std::endl
589  << std::flush;
590  std::cout << "Linf error (variable " << m_equ[1]->GetVariable(i)
591  << ") : " << vLinfErrors[i] << std::endl
592  << std::flush;
593  }
594  }
595  timer.Start();
596 
597  // Start Parareal iteration loop.
598  while (k < m_pararealIterMax && !convergenceCurr)
599  {
600  if (m_chunkRank == min(k, m_numChunks - 1) &&
601  m_comm->GetSpaceComm()->GetRank() == 0)
602  {
603  std::cout << "** ITERATION " << k + 1 << " **" << std::endl
604  << std::flush;
605  }
606 
607  // Use previous parareal solution as "exact solution".
608  if (!m_exactSolution)
609  {
610  for (int i = 0; i < nVar; ++i)
611  {
612  for (int q = 0; q < nPts; ++q)
613  {
614  exactsoln[i][q] = solution[i][q];
615  }
616  }
617  }
618 
619  // Calculate fine solution, F(y_j^k)
620  // Again no communication necessary.
622  ic, solution);
623 
624  // Calculate coarse solve correction G(y_j^{k+1})
625  // These are dependent on the previous time slice, so need to
626  // compute serially.
627  if (m_chunkRank > 0 && !convergencePrev)
628  {
629  // All time slices, apart from the first, receive their initial
630  // state from the previous time slice.
631  tComm->Recv(recvProc, convergencePrev);
632  for (int i = 0; i < nVar; ++i)
633  {
634  tComm->Recv(recvProc, ic[i]);
635  }
636  }
637 
638  // Run the coarse solver
640  k, ic, solutionCoarseCurr);
641 
642  // Calculate the new approximation y_{j+1}^{k+1}
643  // This is calculated point-wise.
644  for (int i = 0; i < nVar; ++i)
645  {
646  for (int q = 0; q < nPts; ++q)
647  {
648  solution[i][q] +=
649  solutionCoarseCurr[i][q] - solutionCoarsePrev[i][q];
650 
651  solutionCoarsePrev[i][q] = solutionCoarseCurr[i][q];
652  }
653  }
654 
655  // Compute L2 error for each time chunk.
656  vL2Error = 0.0;
657  for (int i = 0; i < nVar; ++i)
658  {
659  // Copy the new approximation back.
660  m_equ[0]->CopyToPhysField(i, solution[i]);
661 
662  vL2Error = max(vL2Error, m_equ[0]->L2Error(i, exactsoln[i], 1));
663  }
664 
665  // Check convergence of L2 error for each time chunk.
666  if ((vL2Error < m_pararealToler && convergencePrev) || m_chunkRank == k)
667  {
668  convergenceCurr = true;
669  }
670 
671  // All but the last time slice should communicate the solution to
672  // the next time slice. This will become the initial condition for
673  // the next slice.
674  if (m_chunkRank < m_numChunks - 1)
675  {
676  tComm->Send(sendProc, convergenceCurr);
677  for (int i = 0; i < nVar; ++i)
678  {
679  tComm->Send(sendProc, solution[i]);
680  }
681  }
682 
683  if (m_exactSolution)
684  {
685  // Evaluate exact solution.
686  for (int i = 0; i < nVar; ++i)
687  {
688  m_equ[0]->EvaluateExactSolution(
689  i, exactsoln[i], (m_chunkRank + 1) * m_chunkTime);
690  }
691  }
692 
693  timer.Stop();
694  CPUtime += timer.Elapsed().count();
695  if (m_chunkRank == m_numChunks - 1 &&
696  m_comm->GetSpaceComm()->GetRank() == 0)
697  {
698  std::cout << "-------------------------------------------"
699  << std::endl
700  << std::flush;
701  std::cout << "Total Computation Time = " << CPUtime << "s"
702  << std::endl
703  << std::flush;
704  std::cout << "-------------------------------------------"
705  << std::endl
706  << std::flush;
707  }
708  for (int i = 0; i < nVar; ++i)
709  {
710  vL2Errors[i] = m_equ[0]->L2Error(i, exactsoln[i], 1);
711  vLinfErrors[i] = m_equ[0]->LinfError(i, exactsoln[i]);
712  if (m_chunkRank == m_numChunks - 1 &&
713  m_comm->GetSpaceComm()->GetRank() == 0)
714  {
715  std::cout << "L2 error (variable " << m_equ[0]->GetVariable(i)
716  << ") : " << vL2Errors[i] << std::endl
717  << std::flush;
718  std::cout << "Linf error (variable " << m_equ[0]->GetVariable(i)
719  << ") : " << vLinfErrors[i] << std::endl
720  << std::flush;
721  }
722  }
723  timer.Start();
724 
725  // Increment counter.
726  k++;
727  kmax = k;
728  }
729  timer.Stop();
730 
731  // If already converged, simply copy previous computed solution.
732  m_comm->GetTimeComm()->AllReduce(kmax, Nektar::LibUtilities::ReduceMax);
733  for (; k < kmax; k++)
734  {
735  if (m_comm->GetSpaceComm()->GetRank() == 0 &&
736  m_session->GetParameter("IO_CheckSteps"))
737  {
738  std::string olddir = m_equ[0]->GetSessionName() + "_" +
739  boost::lexical_cast<std::string>(k) + ".pit";
740  std::string outdir = m_equ[0]->GetSessionName() + "_" +
741  boost::lexical_cast<std::string>(k + 1) +
742  ".pit";
743 
744  int nChkPts =
745  m_fineSteps /
746  int(m_session->GetParameter("IO_CheckSteps") * m_numChunks);
747  for (int i = 0; i < nChkPts; i++)
748  {
749  // Old file name
750  std::string oldname =
751  olddir + "/" + m_equ[0]->GetSessionName() + "_" +
752  boost::lexical_cast<std::string>(m_chunkRank * nChkPts + i +
753  1) +
754  ".chk";
755 
756  // New file name
757  std::string outname =
758  outdir + "/" + m_equ[0]->GetSessionName() + "_" +
759  boost::lexical_cast<std::string>(m_chunkRank * nChkPts + i +
760  1) +
761  ".chk";
762 
763  // Remove file if already existing
764  fs::remove_all(outname);
765 
766  // Copy from previous converged solution
767  fs::copy(oldname, outname);
768  }
769  }
770  }
771 
772  // Update solution before printing restart solution.
773  for (int i = 0; i < nVar; ++i)
774  {
775  m_equ[0]->CopyToPhysField(i, solution[i]);
776  m_equ[0]->UpdateFields()[i]->FwdTransLocalElmt(
777  m_equ[0]->UpdateFields()[i]->GetPhys(),
778  m_equ[0]->UpdateFields()[i]->UpdateCoeffs());
779  }
780 
781  // Print restart solution.
782  m_equ[0]->Output();
783 
784  // Wait for all processors to finish their writing activities
785  m_comm->Block();
786 
787  // Print total computational time.
788  if (m_chunkRank == m_numChunks - 1)
789  {
790  if (m_comm->GetSpaceComm()->GetRank() == 0)
791  {
792  std::cout << "-------------------------------------------"
793  << std::endl
794  << std::flush;
795  std::cout << "Total Computation Time = " << CPUtime << "s"
796  << std::endl
797  << std::flush;
798  std::cout << "-------------------------------------------"
799  << std::endl
800  << std::flush;
801  }
802 
803  // Print solution errors.
804  for (int i = 0; i < nVar; ++i)
805  {
806  // Copy the new approximation back.
807  m_equ[0]->CopyToPhysField(i, solution[i]);
808 
809  // Evaluate exact solution.
810  m_equ[0]->EvaluateExactSolution(i, exactsoln[i], m_totalTime);
811 
812  // Evaluate error norms.
813  NekDouble vL2Error = m_equ[0]->L2Error(i, exactsoln[i]);
814  NekDouble vLinfError = m_equ[0]->LinfError(i, exactsoln[i]);
815 
816  if (m_comm->GetSpaceComm()->GetRank() == 0)
817  {
818  std::cout << "L 2 error (variable " << m_equ[0]->GetVariable(i)
819  << ") : " << vL2Error << std::endl
820  << std::flush;
821  std::cout << "L inf error (variable "
822  << m_equ[0]->GetVariable(i) << ") : " << vLinfError
823  << std::endl
824  << std::flush;
825  }
826  }
827  }
828 
829  // Speed-up analysis
830  if (true)
831  {
832  int count = 20;
833 
834  if (m_chunkRank == m_numChunks - 1 &&
835  m_comm->GetSpaceComm()->GetRank() == 0)
836  {
837  std::cout << "-------------------------------------------"
838  << std::endl
839  << std::flush;
840  std::cout << "PARAREAL SPEED-UP ANALYSIS" << std::endl
841  << std::flush;
842  std::cout << "-------------------------------------------"
843  << std::endl
844  << std::flush;
845  }
846 
847  // Mean communication time.
848  NekDouble commTime = 0.0;
849  timer.Start();
850  for (int n = 0; n < count; n++)
851  {
852  if (m_chunkRank > 0)
853  {
854  for (int i = 0; i < nVar; ++i)
855  {
856  tComm->Recv(recvProc, ic[i]);
857  }
858  }
859 
860  if (m_chunkRank < m_numChunks - 1)
861  {
862  for (int i = 0; i < nVar; ++i)
863  {
864  tComm->Send(sendProc, solution[i]);
865  }
866  }
867  }
868  timer.Stop();
869  commTime = timer.Elapsed().count() / count;
870 
871  // Print communication time.
872  if (m_chunkRank == m_numChunks - 1 &&
873  m_comm->GetSpaceComm()->GetRank() == 0)
874  {
875  std::cout << "-------------------------------------------"
876  << std::endl
877  << std::flush;
878  std::cout << "Mean Communication Time = " << commTime << "s"
879  << std::endl
880  << std::flush;
881  std::cout << "-------------------------------------------"
882  << std::endl
883  << std::flush;
884  }
885 
886  // Mean restriction time.
887  NekDouble restTime = 0.0;
888  if (m_equ[0]->GetNpoints() != m_equ[1]->GetNpoints())
889  {
890  timer.Start();
891  for (int n = 0; n < count; n++)
892  {
893  m_interp.InterpExp1ToExp2(m_equ[0]->UpdateFields(),
894  m_equ[1]->UpdateFields());
895  }
896  timer.Stop();
897  restTime = timer.Elapsed().count() / count;
898 
899  // Print restriction time.
900  if (m_chunkRank == m_numChunks - 1 &&
901  m_comm->GetSpaceComm()->GetRank() == 0)
902  {
903  std::cout << "-------------------------------------------"
904  << std::endl
905  << std::flush;
906  std::cout << "Mean Restriction Time = " << restTime << "s"
907  << std::endl
908  << std::flush;
909  std::cout << "-------------------------------------------"
910  << std::endl
911  << std::flush;
912  }
913  }
914 
915  // Mean interpolation time.
916  NekDouble interTime = 0.0;
917  if (m_equ[0]->GetNpoints() != m_equ[1]->GetNpoints())
918  {
919  timer.Start();
920  for (int n = 0; n < count; n++)
921  {
922  m_interp.InterpExp1ToExp2(m_equ[1]->UpdateFields(),
923  m_equ[0]->UpdateFields());
924  }
925  timer.Stop();
926  interTime = timer.Elapsed().count() / count;
927 
928  // Print restriction time.
929  if (m_chunkRank == m_numChunks - 1 &&
930  m_comm->GetSpaceComm()->GetRank() == 0)
931  {
932  std::cout << "-------------------------------------------"
933  << std::endl
934  << std::flush;
935  std::cout << "Mean Interpolation Time = " << interTime << "s"
936  << std::endl
937  << std::flush;
938  std::cout << "-------------------------------------------"
939  << std::endl
940  << std::flush;
941  }
942  }
943 
944  // Mean coarse solver time.
945  NekDouble coarseSolveTime = 0.0;
946  timer.Start();
947  for (int n = 0; n < count; n++)
948  {
949  RunCoarseSolve(0.0, 100, -1, ic, solution);
950  }
951  timer.Stop();
952  coarseSolveTime = 0.01 * timer.Elapsed().count() / count *
954  restTime - interTime;
955 
956  // Print restriction time.
957  if (m_chunkRank == m_numChunks - 1 &&
958  m_comm->GetSpaceComm()->GetRank() == 0)
959  {
960  std::cout << "-------------------------------------------"
961  << std::endl
962  << std::flush;
963  std::cout << "Mean Coarse Solve Time = " << coarseSolveTime << "s"
964  << std::endl
965  << std::flush;
966  std::cout << "-------------------------------------------"
967  << std::endl
968  << std::flush;
969  }
970 
971  // Fine solver time.
972  NekDouble fineSolveTime = 0.0;
973  timer.Start();
974  // Turnoff I/O for fine solver.
975  m_equ[0]->SetInfoSteps(0);
976  m_equ[0]->SetCheckpointSteps(0);
977  for (int n = 0; n < count; n++)
978  {
979  RunFineSolve(0.0, 100, -1, ic, solution);
980  }
981  timer.Stop();
982  fineSolveTime = 0.01 * timer.Elapsed().count() / count *
984 
985  // Print fine solve time.
986  if (m_chunkRank == m_numChunks - 1 &&
987  m_comm->GetSpaceComm()->GetRank() == 0)
988  {
989  std::cout << "-------------------------------------------"
990  << std::endl
991  << std::flush;
992  std::cout << "Mean Fine Solve Time = " << fineSolveTime << "s"
993  << std::endl
994  << std::flush;
995  std::cout << "-------------------------------------------"
996  << std::endl
997  << std::flush;
998  }
999 
1000  // Print speedup time.
1001  if (m_chunkRank == m_numChunks - 1 &&
1002  m_comm->GetSpaceComm()->GetRank() == 0)
1003  {
1004  std::cout << "-------------------------------------------"
1005  << std::endl
1006  << std::flush;
1007  std::cout << "Maximum Speed-up" << std::endl << std::flush;
1008  std::cout << "-------------------------------------------"
1009  << std::endl
1010  << std::flush;
1011  for (int k = 1; k <= m_numChunks; k++)
1012  {
1013  NekDouble ratio = double(k) / m_numChunks;
1014  NekDouble ratioSolve = coarseSolveTime / fineSolveTime;
1015  NekDouble speedup = 1.0 / ((1.0 + ratio) * ratioSolve + ratio);
1016  std::cout << "Speed-up (" << k << ") = " << speedup << std::endl
1017  << std::flush;
1018  }
1019  std::cout << "-------------------------------------------"
1020  << std::endl
1021  << std::flush;
1022  std::cout << "Speed-up with comm." << std::endl << std::flush;
1023  std::cout << "-------------------------------------------"
1024  << std::endl
1025  << std::flush;
1026  for (int k = 1; k <= m_numChunks; k++)
1027  {
1028  NekDouble ratio = double(k) / m_numChunks;
1029  NekDouble ratioComm = commTime / fineSolveTime;
1030  NekDouble ratioSolve = coarseSolveTime / fineSolveTime;
1031  NekDouble speedup = 1.0 / ((1.0 + ratio) * ratioSolve + ratio +
1032  ratioComm / m_numChunks);
1033  std::cout << "Speed-up (" << k << ") = " << speedup << std::endl
1034  << std::flush;
1035  }
1036  std::cout << "-------------------------------------------"
1037  << std::endl
1038  << std::flush;
1039  std::cout << "Speed-up with comm. and interp." << std::endl
1040  << std::flush;
1041  std::cout << "-------------------------------------------"
1042  << std::endl
1043  << std::flush;
1044  for (int k = 1; k <= m_numChunks; k++)
1045  {
1046  NekDouble ratio = double(k) / m_numChunks;
1047  NekDouble ratioComm = commTime / fineSolveTime;
1048  NekDouble ratioSolve =
1049  (coarseSolveTime + restTime + interTime) / fineSolveTime;
1050  NekDouble speedup =
1051  1.0 / ((1.0 + ratio) * ratioSolve + ratio +
1052  ratioComm * k * (2 * m_numChunks - k - 1) / 2.0 /
1053  m_numChunks);
1054  std::cout << "Speed-up (" << k << ") = " << speedup << std::endl
1055  << std::flush;
1056  }
1057  std::cout << "-------------------------------------------"
1058  << std::endl
1059  << std::flush;
1060  }
1061  }
1062 }
1063 } // namespace SolverUtils
1064 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:198
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:144
static SessionReaderSharedPtr CreateInstance(int argc, char *argv[])
Creates an instance of the SessionReader class.
Base class for the development of solvers.
Definition: Driver.h:67
LibUtilities::SessionReaderSharedPtr m_session
Session reader object.
Definition: Driver.h:88
LibUtilities::CommSharedPtr m_comm
Communication object.
Definition: Driver.h:85
SpatialDomains::MeshGraphSharedPtr m_graph
MeshGraph object.
Definition: Driver.h:94
enum EvolutionOperatorType m_EvolutionOperator
Evolution Operator.
Definition: Driver.h:103
Array< OneD, EquationSystemSharedPtr > m_equ
Equation system to solve.
Definition: Driver.h:97
int m_nequ
number of equations
Definition: Driver.h:100
virtual SOLVER_UTILS_EXPORT void v_Execute(std::ostream &out=std::cout) override
Virtual function for solve implementation.
virtual SOLVER_UTILS_EXPORT ~DriverParareal()
Destructor.
NekDouble m_chunkTime
Time for chunks.
int m_pararealIterMax
Maximum number of parareal iteration.
int m_coarseSteps
Number of steps for the coarse solver.
void RunCoarseSolve(const NekDouble time, const int nstep, const int iter, const Array< OneD, const Array< OneD, NekDouble >> &input, Array< OneD, Array< OneD, NekDouble >> &output)
void RunFineSolve(const NekDouble time, const int nstep, const int iter, const Array< OneD, const Array< OneD, NekDouble >> &input, Array< OneD, Array< OneD, NekDouble >> &output)
virtual SOLVER_UTILS_EXPORT void v_InitObject(std::ostream &out=std::cout) override
Second-stage initialisation.
NekDouble m_pararealToler
Parareal tolerance.
NekDouble m_coarseTimeStep
Timestep for coarse solver.
LibUtilities::SessionReaderSharedPtr m_sessionCoarse
Parareal (coarse solver) session reader object.
int m_numChunks
Number of time chunks.
NekDouble m_fineTimeStep
Timestep for fine solver.
NekDouble m_totalTime
Total time integration interval.
FieldUtils::Interpolator< Array< OneD, MultiRegions::ExpListSharedPtr > > m_interp
int m_fineSteps
Number of steps for the fine solver.
SpatialDomains::MeshGraphSharedPtr m_graphCoarse
Parareal (coarse solver) MeshGraph object.
bool m_exactSolution
Using exact solution to compute error norms.
NekDouble m_coarseSolveFactor
Coarse solver time factor.
void SetPararealSessionFile(void)
Set the Parareal (coarse solver) session file.
static MeshGraphSharedPtr Read(const LibUtilities::SessionReaderSharedPtr pSession, LibUtilities::DomainRangeShPtr rng=LibUtilities::NullDomainRangeShPtr, bool fillGraph=true)
Definition: MeshGraph.cpp:111
def copy(self)
Definition: pycml.py:2663
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:54
DriverFactory & GetDriverFactory()
Definition: Driver.cpp:64
EquationSystemFactory & GetEquationSystemFactory()
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:172
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
double NekDouble