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#include <boost/format.hpp>
40
41namespace Nektar
42{
43namespace SolverUtils
44{
45std::string DriverParareal::className =
50
51/**
52 *
53 */
57 : DriverParallelInTime(pSession, pGraph)
58{
59}
60
61/**
62 *
63 */
64void DriverParareal::v_InitObject(std::ostream &out)
65{
67}
68
69/**
70 *
71 */
72void DriverParareal::v_Execute(std::ostream &out)
73{
74 // Set timer.
76 NekDouble CPUtime = 0.0;
77
78 // Set time communication parameters.
79 LibUtilities::CommSharedPtr tComm = m_session->GetComm()->GetTimeComm();
80 m_numChunks = tComm->GetSize();
81 m_chunkRank = tComm->GetRank();
82
83 // Get and assert parameters from session file.
86
87 // Print solver info.
90
91 // Initialization.
92 InitialiseEqSystem(false);
95
96 // Allocate storage for Parareal solver.
97 Array<OneD, Array<OneD, NekDouble>> solutionCoarseCurr(m_nVar);
98 Array<OneD, Array<OneD, NekDouble>> solutionCoarsePrev(m_nVar);
101 for (size_t i = 0; i < m_nVar; ++i)
102 {
103 solutionCoarseCurr[i] = Array<OneD, NekDouble>(m_fineNpts, 0.0);
104 solutionCoarsePrev[i] = Array<OneD, NekDouble>(m_fineNpts, 0.0);
106 solution[i] = Array<OneD, NekDouble>(m_fineNpts, 0.0);
107 }
108
109 // Get initial condition from fields.
111
112 // Start iteration windows.
113 tComm->Block();
118 for (size_t w = 0; w < m_numWindowsPIT; w++)
119 {
120 timer.Start();
121
122 // Initialize time for the current window.
124
125 // Print window number.
126 PrintHeaderTitle1((boost::format("WINDOWS #%1%") % (w + 1)).str());
127
128 // Run coarse solver.
129 if (m_chunkRank > 0)
130 {
133 }
134 RunCoarseSolve(m_time, m_coarseSteps, ic, solutionCoarsePrev);
135 CopySolutionVector(solutionCoarsePrev, solution);
136
137 // Update fields with solution.
138 CopyToFinePhysField(solution);
139
140 // Compute exact solution, if necessary.
141 if (m_exactSolution)
142 {
144 }
145
146 // Solution convergence monitoring.
147 timer.Stop();
148 CPUtime += timer.Elapsed().count();
149 PrintHeaderTitle2((boost::format("ITERATION %1%") % 0).str());
151 timer.Start();
152
153 // Start Parareal iteration.
154 size_t k = 0;
155 size_t kmax = 0;
156 int convergenceCurr = false;
157 int convergencePrev = (m_chunkRank == 0);
158 while (k < m_iterMaxPIT && !convergenceCurr)
159 {
160 // Use previous parareal solution as "exact solution", if necessary.
161 if (!m_exactSolution)
162 {
164 }
165
166 // Calculate fine solution (parallel-in-time).
167 RunFineSolve(m_time, m_fineSteps, k, w, ic, solution);
168
169 // Receive coarse solution from previous processor.
170 RecvInitialConditionFromPreviousProc(ic, convergencePrev);
171
172 // Calculate coarse solution (serial-in-time).
173 RunCoarseSolve(m_time, m_coarseSteps, ic, solutionCoarseCurr);
174
175 // Calculate corrected solution.
176 PararealCorrection(solutionCoarseCurr, solutionCoarsePrev,
177 solution);
178
179 // Save current coarse solution.
180 CopySolutionVector(solutionCoarseCurr, solutionCoarsePrev);
181
182 // Update fields with corrected solution.
183 CopyToFinePhysField(solution);
184
185 // Solution convergence monitoring.
186 timer.Stop();
187 CPUtime += timer.Elapsed().count();
188 PrintHeaderTitle2((boost::format("ITERATION %1%") % (k + 1)).str());
190 timer.Start();
191
192 // Check convergence of L2 error for each time chunk.
193 if ((vL2ErrorMax() < m_tolerPIT && convergencePrev) ||
194 m_chunkRank == k)
195 {
196 convergenceCurr = true;
197 }
198
199 // Send coarse solution to next processor.
200 SendSolutionToNextProc(solution, convergenceCurr);
201
202 // Increment index.
203 k++;
204 kmax = k;
205 }
206 timer.Stop();
207
208 // Copy converged check points.
209 CopyConvergedCheckPoints(w, k, kmax);
210
211 // Update solution before printing solution.
212 UpdateSolution(solution);
213
214 // Print solution files.
216
217 // Windowing.
218 if (w != m_numWindowsPIT - 1)
219 {
220 ApplyWindowing(solution, ic);
221 }
222 }
223
224 // Post-processing.
225 tComm->Block();
226 PrintHeaderTitle1("SUMMARY");
230}
231
232/**
233 *
234 */
236{
237 // Allocate memory.
240 for (size_t i = 0; i < m_nVar; ++i)
241 {
242 buffer1[i] = Array<OneD, NekDouble>(m_fineNpts, 0.0);
243 buffer2[i] = Array<OneD, NekDouble>(m_fineNpts, 0.0);
244 }
245
246 // Estimate communication time.
247 return EstimateCommunicationTime(buffer1, buffer2);
248}
249
250/**
251 *
252 */
254{
256 {
257 return 0.0; // No restriction necessary
258 }
259 else
260 {
261 // Average restriction time over niter iteration.
262 size_t niter = 20;
264 timer.Start();
265 for (size_t n = 0; n < niter; n++)
266 {
268 }
269 timer.Stop();
270 return timer.Elapsed().count() / niter;
271 }
272}
273
274/**
275 *
276 */
278{
280 {
281 return 0.0; // No interpolation
282 }
283 else
284 {
285 // Average interpolation time over niter iteration.
286 size_t niter = 20;
288 timer.Start();
289 for (size_t n = 0; n < niter; n++)
290 {
292 }
293 timer.Stop();
294 return timer.Elapsed().count() / niter;
295 }
296}
297
298/**
299 *
300 */
302{
303 // Allocate memory.
305 for (size_t i = 0; i < m_nVar; ++i)
306 {
308 }
309
310 // Turnoff I/O.
311 m_coarseEqSys->SetInfoSteps(0);
312 m_coarseEqSys->SetCheckpointSteps(0);
313
314 // Get initial condition.
316
317 // Estimate coarse solver time.
319 timer.Start();
320
321 // Set to coarse timestep.
322 m_coarseEqSys->SetTime(m_time + m_chunkTime);
323 m_coarseEqSys->SetSteps(10);
324
325 // Copy initial condition.
326 for (size_t i = 0; i < m_fineEqSys->GetNvariables(); ++i)
327 {
328 m_coarseEqSys->CopyToPhysField(i, sol[i]);
329 }
330
331 // Solve equations.
332 m_coarseEqSys->DoSolve();
333
334 // Copy solution.
335 for (size_t i = 0; i < m_coarseEqSys->GetNvariables(); ++i)
336 {
337 m_coarseEqSys->CopyFromPhysField(i, sol[i]);
338 }
339
340 timer.Stop();
341 return 0.1 * timer.Elapsed().count() * m_coarseSteps;
342}
343
344/**
345 *
346 */
348{
349 // Allocate memory.
351 for (size_t i = 0; i < m_nVar; ++i)
352 {
354 }
355
356 // Turnoff I/O.
357 m_fineEqSys->SetInfoSteps(0);
358 m_fineEqSys->SetCheckpointSteps(0);
359
360 // Get initial condition.
362
363 // Estimate fine solver time.
365 timer.Start();
366 RunFineSolve(m_time + m_chunkTime, 100, 0, 0, sol, sol);
367 timer.Stop();
368 return 0.01 * timer.Elapsed().count() * m_fineSteps;
369}
370
371/**
372 *
373 */
375{
377}
378
379/**
380 *
381 */
383{
384 return 0.0;
385}
386
387/**
388 *
389 */
391 const size_t iter, NekDouble fineSolveTime, NekDouble coarseSolveTime,
392 NekDouble restTime, NekDouble interTime, NekDouble commTime,
393 NekDouble predictorTime, NekDouble overheadTime)
394{
395 // The speed-up estimate is based on "Lunet, T., Bodart, J., Gratton, S., &
396 // Vasseur, X. (2018). Time-parallel simulation of the decay of homogeneous
397 // turbulence using parareal with spatial coarsening. Computing and
398 // Visualization in Science, 19, 31-44".
399
400 size_t nComm = (iter * (2 * m_numChunks - iter - 1)) / 2;
401 NekDouble ratio = double(iter) / m_numChunks;
402 NekDouble ratioPredictor = predictorTime / fineSolveTime;
403 NekDouble ratioSolve = coarseSolveTime / fineSolveTime;
404 NekDouble ratioInterp = (restTime + interTime) / fineSolveTime;
405 NekDouble ratioComm = commTime / fineSolveTime;
406 NekDouble ratioOverhead = overheadTime / fineSolveTime;
407
408 return 1.0 / (ratioPredictor + ratio * (1.0 + ratioSolve + ratioInterp) +
409 (ratioComm * nComm + ratioOverhead) / m_numChunks);
410}
411
412/**
413 *
414 */
416{
417 // Assert time-stepping parameters
418 ASSERTL0(
420 "Total number of fine step should be divisible by number of chunks.");
421
422 ASSERTL0(
424 "Total number of coarse step should be divisible by number of chunks.");
425
427 "Total number of fine step should be divisible by number of "
428 "windows times number of chunks.");
429
431 "Total number of coarse step should be divisible by number of "
432 "windows times number of chunks.");
433
435 m_fineTimeStep * m_fineSteps) < 1e-12,
436 "Fine and coarse total computational times do not match");
437
438 // Assert I/O parameters
439 if (m_infoSteps)
440 {
442 0,
443 "number of IO_InfoSteps should divide number of fine steps "
444 "per time chunk");
445 }
446
447 if (m_checkSteps)
448 {
450 0,
451 "number of IO_CheckSteps should divide number of fine steps "
452 "per time chunk");
453 }
454}
455
456/**
457 *
458 */
460 const NekDouble time, const size_t nstep,
461 const Array<OneD, const Array<OneD, NekDouble>> &input,
463{
464 // Set to coarse timestep.
465 m_coarseEqSys->SetTime(time);
466 m_coarseEqSys->SetSteps(nstep);
467
468 // Restrict initial condition from input.
470
471 // Copy initial condition.
472 for (size_t i = 0; i < m_fineEqSys->GetNvariables(); ++i)
473 {
474 m_coarseEqSys->CopyToPhysField(i, m_tmpcoarse[i]);
475 }
476
477 // Solve equations.
478 m_coarseEqSys->DoSolve();
479
480 // Copy solution.
481 for (size_t i = 0; i < m_coarseEqSys->GetNvariables(); ++i)
482 {
483 m_coarseEqSys->CopyFromPhysField(i, m_tmpcoarse[i]);
484 }
485
486 // Interpolate solution to output.
487 Interpolator(m_tmpcoarse, output);
488}
489
490/**
491 *
492 */
494 const NekDouble time, const size_t nstep, const size_t iter,
495 const size_t wd, const Array<OneD, const Array<OneD, NekDouble>> &input,
497{
498 // Number of checkpoint by chunk.
499 size_t nChkPts = m_checkSteps ? m_fineSteps / m_checkSteps : 1;
500
501 // Checkpoint index.
502 size_t iChkPts = (m_chunkRank + wd * m_numChunks) * nChkPts + 1;
503
504 // Set to fine timestep.
505 m_fineEqSys->SetTime(time);
506 m_fineEqSys->SetSteps(nstep);
507
508 // Reinitialize check point number for each parallel-in-time iteration.
509 m_fineEqSys->SetCheckpointNumber(iChkPts);
510
511 // Update parallel-in-time iteration number.
512 m_fineEqSys->SetIterationNumberPIT(iter + 1);
513
514 // Update parallel-in-time window number.
515 m_fineEqSys->SetWindowNumberPIT(wd);
516
517 // Copy initial condition from input.
518 for (size_t i = 0; i < m_fineEqSys->GetNvariables(); ++i)
519 {
520 m_fineEqSys->CopyToPhysField(i, input[i]);
521 }
522
523 // Solve equations.
524 m_fineEqSys->DoSolve();
525
526 // Copy solution to output.
527 for (size_t i = 0; i < m_fineEqSys->GetNvariables(); ++i)
528 {
529 m_fineEqSys->CopyFromPhysField(i, output[i]);
530 }
531}
532
533/**
534 *
535 */
537 const Array<OneD, const Array<OneD, NekDouble>> &coarse_new,
538 const Array<OneD, const Array<OneD, NekDouble>> &coarse_old,
540{
541 for (size_t i = 0; i < fine.size(); ++i)
542 {
543 Vmath::Vadd(fine[i].size(), fine[i], 1, coarse_new[i], 1, fine[i], 1);
544 Vmath::Vsub(fine[i].size(), fine[i], 1, coarse_old[i], 1, fine[i], 1);
545 }
546}
547
548/**
549 *
550 */
552{
553 PrintHeaderTitle2("PRINT SOLUTION FILES");
554 m_fineEqSys->Output();
555}
556
557/**
558 *
559 */
561 const Array<OneD, const Array<OneD, NekDouble>> &in,
563{
564 LibUtilities::CommSharedPtr tComm = m_session->GetComm()->GetTimeComm();
565
566 // Use last chunk solution as initial condition for the next
567 // window.
568 if (m_chunkRank == m_numChunks - 1)
569 {
570 for (size_t i = 0; i < in.size(); ++i)
571 {
572 Vmath::Vcopy(in[i].size(), in[i], 1, out[i], 1);
573 }
574 }
575
576 // Broadcast I.C. for windowing.
577 for (size_t i = 0; i < out.size(); ++i)
578 {
579 tComm->Bcast(out[i], m_numChunks - 1);
580 }
581}
582
583/**
584 *
585 */
586void DriverParareal::CopyConvergedCheckPoints(const size_t w, const size_t k,
587 size_t kmax)
588{
589 // Determine max number of iteration.
590 LibUtilities::CommSharedPtr tComm = m_session->GetComm()->GetTimeComm();
591 tComm->AllReduce(kmax, Nektar::LibUtilities::ReduceMax);
592
593 if (m_comm->GetSpaceComm()->GetRank() == 0 && m_checkSteps)
594 {
595 for (size_t j = k; j < kmax; j++)
596 {
597 // Copy converged solution files from directory corresponding to
598 // iteration j to the directory corresponding to iteration j + 1.
599
600 // Input directory name.
601 std::string indir = m_fineEqSys->GetSessionName() + "_" +
602 boost::lexical_cast<std::string>(j) + ".pit";
603
604 /// Output directory name.
605 std::string outdir = m_fineEqSys->GetSessionName() + "_" +
606 boost::lexical_cast<std::string>(j + 1) +
607 ".pit";
608
609 // Number of checkpoint by chunk.
610 size_t nChkPts = m_fineSteps / m_checkSteps;
611
612 // Checkpoint index.
613 size_t iChkPts = (m_chunkRank + w * m_numChunks) * nChkPts + 1;
614
615 for (size_t i = 0; i < nChkPts; i++)
616 {
617 // Filename corresponding to checkpoint iChkPts.
618 std::string filename =
619 m_fineEqSys->GetSessionName() + "_" +
620 boost::lexical_cast<std::string>(iChkPts) + ".chk";
621
622 // Intput full file name.
623 std::string infullname = indir + "/" + filename;
624
625 // Output full file name.
626 std::string outfullname = outdir + "/" + filename;
627
628 // Remove output file if already existing.
629 fs::remove_all(outfullname);
630
631 // Copy converged solution files.
632 fs::copy(infullname, outfullname);
633 }
634 }
635 }
636}
637
638} // namespace SolverUtils
639} // 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
static std::string RegisterEnumValue(std::string pEnum, std::string pString, int pEnumValue)
Registers an enumeration value.
LibUtilities::SessionReaderSharedPtr m_session
Session reader object.
Definition: Driver.h:85
LibUtilities::CommSharedPtr m_comm
Communication object.
Definition: Driver.h:82
Base class for the development of parallel-in-time solvers.
NekDouble m_totalTime
Total time integration interval.
size_t m_coarseSteps
Number of steps for the coarse solver.
void SendSolutionToNextProc(Array< OneD, Array< OneD, NekDouble > > &array, int &convergence)
size_t m_iterMaxPIT
Maximum number of parallel-in-time iteration.
size_t m_fineSteps
Number of steps for the fine solver.
void CopyToFinePhysField(const Array< OneD, const Array< OneD, NekDouble > > &in)
NekDouble m_coarseTimeStep
Timestep for coarse solver.
void RecvInitialConditionFromPreviousProc(Array< OneD, Array< OneD, NekDouble > > &array, int &convergence)
void CopyFromFinePhysField(Array< OneD, Array< OneD, NekDouble > > &out)
Array< OneD, Array< OneD, NekDouble > > m_tmpfine
void Interpolator(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray)
std::shared_ptr< SolverUtils::UnsteadySystem > m_coarseEqSys
virtual SOLVER_UTILS_EXPORT void v_InitObject(std::ostream &out=std::cout) override
Virtual function for initialisation implementation.
void PrintHeaderTitle1(const std::string &title)
void PrintHeaderTitle2(const std::string &title)
NekDouble m_tolerPIT
ParallelInTime tolerance.
void PrintCoarseSolverInfo(std::ostream &out=std::cout)
NekDouble m_fineTimeStep
Timestep for fine solver.
void PrintFineSolverInfo(std::ostream &out=std::cout)
bool m_exactSolution
Using exact solution to compute error norms.
size_t m_checkSteps
Number of steps for checkpoint.
void SolutionConvergenceMonitoring(const NekDouble &time)
Array< OneD, Array< OneD, NekDouble > > m_tmpcoarse
std::shared_ptr< SolverUtils::UnsteadySystem > m_fineEqSys
Array< OneD, Array< OneD, NekDouble > > m_exactsoln
NekDouble EstimateCommunicationTime(Array< OneD, Array< OneD, NekDouble > > &buffer1, Array< OneD, Array< OneD, NekDouble > > &buffer2)
void SolutionConvergenceSummary(const NekDouble &time)
void UpdateSolution(const Array< OneD, const Array< OneD, NekDouble > > &in)
void CopySolutionVector(const Array< OneD, const Array< OneD, NekDouble > > &in, Array< OneD, Array< OneD, NekDouble > > &out)
size_t m_infoSteps
Number of steps for info I/O.
virtual SOLVER_UTILS_EXPORT void v_Execute(std::ostream &out=std::cout) override
Virtual function for solve implementation.
virtual NekDouble v_EstimatePredictorTime(void) override
void CopyConvergedCheckPoints(const size_t w, const size_t k, size_t kmax)
void ApplyWindowing(const Array< OneD, const Array< OneD, NekDouble > > &in, Array< OneD, Array< OneD, NekDouble > > &out)
static std::string className
Name of the class.
virtual NekDouble v_EstimateInterpolationTime(void) override
virtual NekDouble v_EstimateCommunicationTime(void) override
virtual SOLVER_UTILS_EXPORT void v_InitObject(std::ostream &out=std::cout) override
Virtual function for initialisation implementation.
virtual NekDouble v_EstimateRestrictionTime(void) override
void RunFineSolve(const NekDouble time, const size_t nstep, const size_t iter, const size_t wd, const Array< OneD, const Array< OneD, NekDouble > > &input, Array< OneD, Array< OneD, NekDouble > > &output)
static DriverSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
Creates an instance of this class.
void PararealCorrection(const Array< OneD, const Array< OneD, NekDouble > > &coarse_new, const Array< OneD, const Array< OneD, NekDouble > > &coarse_old, Array< OneD, Array< OneD, NekDouble > > &fine)
SOLVER_UTILS_EXPORT DriverParareal(const LibUtilities::SessionReaderSharedPtr pSession, const SpatialDomains::MeshGraphSharedPtr pGraph)
Constructor.
virtual NekDouble v_EstimateFineSolverTime(void) override
virtual NekDouble v_EstimateOverheadTime(void) override
void RunCoarseSolve(const NekDouble time, const size_t nstep, const Array< OneD, const Array< OneD, NekDouble > > &input, Array< OneD, Array< OneD, NekDouble > > &output)
virtual NekDouble v_ComputeSpeedUp(const size_t iter, NekDouble fineSolveTime, NekDouble coarseSolveTime, NekDouble restTime, NekDouble interTime, NekDouble commTime, NekDouble predictorOverheadTime, NekDouble overheadTime) override
virtual NekDouble v_EstimateCoarseSolverTime(void) override
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:57
DriverFactory & GetDriverFactory()
Definition: Driver.cpp:67
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:176
std::vector< double > w(NPUPPER)
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
double NekDouble
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition: Vmath.cpp:354
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1191
void Vsub(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Subtract vector z = x-y.
Definition: Vmath.cpp:414