Nektar++
Loading...
Searching...
No Matches
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::SolverUtils
42{
43std::string DriverParareal::className =
48
49/**
50 *
51 */
58
59/**
60 *
61 */
70
71/**
72 *
73 */
74void DriverParareal::v_Execute([[maybe_unused]] std::ostream &out)
75{
76 // Timing.
78 NekDouble totalTime = 0.0, predictorTime = 0.0, coarseSolveTime = 0.0,
79 fineSolveTime = 0.0, correctionTime = 0.0;
80
81 // Get and assert parameters from session file.
83
84 // Initialie time step parameters.
89
90 // Pre-solve for one time-step to initialize preconditioner.
92
93 // Start iteration windows.
94 m_comm->GetTimeComm()->Block();
96 for (size_t w = 0; w < m_numWindowsPIT; w++)
97 {
98 timer.Start();
99 // Initialize time for the current window.
102
103 // Print window number.
104 PrintHeader((boost::format("WINDOWS #%1%") % (w + 1)).str(), '*');
105
106 // Update coarse initial condition.
108
109 // Run predictor.
110 for (size_t i = 0; i < m_nVar; ++i)
111 {
112 RecvFromPreviousProc(m_EqSys[m_coarseLevel]->UpdatePhysField(i));
113 }
114 if (m_chunkRank > 0)
115 {
117 }
119 for (size_t i = 0; i < m_nVar; ++i)
120 {
121 SendToNextProc(m_EqSys[m_coarseLevel]->UpdatePhysField(i));
122 }
123
124 // Interpolate coarse solution.
126
127 // Compute exact solution, if necessary.
128 if (m_exactSolution)
129 {
131 }
132 timer.Stop();
133 predictorTime += timer.Elapsed().count();
134 totalTime += timer.Elapsed().count();
135
136 // Solution convergence monitoring.
137 timer.Start();
140 timer.Stop();
141 totalTime += timer.Elapsed().count();
142 if (m_chunkRank == m_numChunks - 1 &&
143 m_comm->GetSpaceComm()->GetRank() == 0)
144 {
145 std::cout << "Total Computation Time : " << totalTime << "s"
146 << std::endl
147 << std::flush;
148 }
149
150 // Start Parareal iteration.
151 size_t iter = 1;
152 int convergenceCurr = false;
153 int convergencePrev = (m_chunkRank == 0);
154 while (iter <= m_iterMaxPIT && !convergenceCurr)
155 {
156 // Use previous parareal solution as "exact solution", if necessary.
157 timer.Start();
158 if (!m_exactSolution)
159 {
161 }
162 timer.Stop();
163 totalTime += timer.Elapsed().count();
164
165 // Calculate fine solution (parallel-in-time).
166 timer.Start();
169 timer.Stop();
170 fineSolveTime += timer.Elapsed().count();
171 totalTime += timer.Elapsed().count();
172
173 // Compute F -> F - Gold
174 timer.Start();
176 timer.Stop();
177 correctionTime += timer.Elapsed().count();
178 totalTime += timer.Elapsed().count();
179
180 // Receive coarse solution from previous processor.
181 timer.Start();
183 timer.Stop();
184 totalTime += timer.Elapsed().count();
185
186 // Calculate coarse solution (serial-in-time).
187 timer.Start();
190 iter);
191 timer.Stop();
192 coarseSolveTime += timer.Elapsed().count();
193 totalTime += timer.Elapsed().count();
194
195 // Compute F -> F + Gnew
196 timer.Start();
198 timer.Stop();
199 correctionTime += timer.Elapsed().count();
200 totalTime += timer.Elapsed().count();
201
202 // Solution convergence monitoring.
205 if (m_chunkRank == m_numChunks - 1 &&
206 m_comm->GetSpaceComm()->GetRank() == 0)
207 {
208 std::cout << "Total Computation Time : " << totalTime << "s"
209 << std::endl
210 << std::flush;
211 std::cout << " - Predictor Time : " << predictorTime << "s"
212 << std::endl
213 << std::flush;
214 std::cout << " - Coarse Solve Time : " << coarseSolveTime << "s"
215 << std::endl
216 << std::flush;
217 std::cout << " - Fine Solve Time : " << fineSolveTime << "s"
218 << std::endl
219 << std::flush;
220 std::cout << " - Correction Time : " << correctionTime << "s"
221 << std::endl
222 << std::flush;
223 }
224
225 // Check convergence of L2 error for each time chunk.
226 convergenceCurr = (vL2ErrorMax() < m_tolerPIT && convergencePrev) ||
227 (m_chunkRank + 1 == iter);
228
229 // Send solution to next processor.
230 timer.Start();
231 SendToNextProc(m_fineSolution, convergenceCurr);
232 timer.Stop();
233 totalTime += timer.Elapsed().count();
234
235 // Increment iteration index.
236 iter++;
237 }
238
239 // Copy converged check points.
241
242 // Write time chunk solution to files.
244
245 // Apply windowing.
246 timer.Start();
248 timer.Stop();
249 totalTime += timer.Elapsed().count();
250 }
251
252 m_comm->GetTimeComm()->Block();
253 PrintHeader("SUMMARY", '*');
256 if (m_chunkRank == m_numChunks - 1 &&
257 m_comm->GetSpaceComm()->GetRank() == 0)
258 {
259 std::cout << "Total Computation Time : " << totalTime << "s"
260 << std::endl
261 << std::flush;
262 std::cout << " - Predictor Time : " << predictorTime << "s" << std::endl
263 << std::flush;
264 std::cout << " - Coarse Solve Time : " << coarseSolveTime << "s"
265 << std::endl
266 << std::flush;
267 std::cout << " - Fine Solve Time : " << fineSolveTime << "s"
268 << std::endl
269 << std::flush;
270 std::cout << " - Correction Time : " << correctionTime << "s"
271 << std::endl
272 << std::flush;
273 }
274}
275
276/**
277 *
278 */
280{
281 // Allocate storage for Parareal solver.
285 for (size_t i = 0; i < m_nVar; ++i)
286 {
289 m_fineSolution[i] = m_EqSys[m_fineLevel]->UpdatePhysField(i);
292 ? m_EqSys[m_coarseLevel]->UpdatePhysField(i)
294 }
295}
296
297/**
298 *
299 */
301{
302 // Assert time-stepping parameters
303 ASSERTL0(
305 "Total number of fine step should be divisible by number of chunks.");
306
307 ASSERTL0(
309 "Total number of coarse step should be divisible by number of chunks.");
310
312 "Total number of fine step should be divisible by number of "
313 "windows times number of chunks.");
314
316 "Total number of coarse step should be divisible by number of "
317 "windows times number of chunks.");
318
321 "Fine and coarse total computational times do not match");
322
324 ->GetTimeIntegrationScheme()
325 ->GetNumIntegrationPhases() == 1,
326 "Only single step time-integration schemes currently supported "
327 "for Parareal");
328
330 ->GetTimeIntegrationScheme()
331 ->GetNumIntegrationPhases() == 1,
332 "Only single step time-integration schemes currently supported "
333 "for Parareal");
334
335 // Assert I/O parameters
336 if (m_EqSys[m_fineLevel]->GetInfoSteps())
337 {
338 ASSERTL0(m_nsteps[m_fineLevel] % (m_EqSys[m_fineLevel]->GetInfoSteps() *
340 0,
341 "number of IO_InfoSteps should divide number of fine steps "
342 "per time chunk");
343 }
344
345 if (m_EqSys[m_coarseLevel]->GetInfoSteps())
346 {
348 (m_EqSys[m_coarseLevel]->GetInfoSteps() * m_numChunks *
350 0,
351 "number of IO_InfoSteps should divide number of coarse steps "
352 "per time chunk");
353 }
354
355 if (m_EqSys[m_fineLevel]->GetCheckpointSteps())
356 {
358 (m_EqSys[m_fineLevel]->GetCheckpointSteps() *
360 0,
361 "number of IO_CheckSteps should divide number of fine steps "
362 "per time chunk");
363 }
364
365 if (m_EqSys[m_coarseLevel]->GetCheckpointSteps())
366 {
368 (m_EqSys[m_coarseLevel]->GetCheckpointSteps() *
370 0,
371 "number of IO_CheckSteps should divide number of coarse steps "
372 "per time chunk");
373 }
374}
375
376/**
377 *
378 */
380{
381 // Interpolate solution to fine field.
382 Interpolate(m_EqSys[timeLevel]->UpdateFields(),
385}
386
387/**
388 *
389 */
391{
392 // Restrict fine field to coarse solution.
393 Interpolate(m_EqSys[m_fineLevel]->UpdateFields(),
394 m_EqSys[timeLevel]->UpdateFields(), m_initialCondition,
396}
397
398/**
399 *
400 */
401void DriverParareal::UpdateSolution(const size_t timeLevel,
402 const NekDouble time, const size_t nstep,
403 const size_t wd, const size_t iter)
404{
405 // Number of checkpoint by chunk.
406 size_t nChkPts =
407 m_EqSys[timeLevel]->GetCheckpointSteps()
408 ? m_nsteps[timeLevel] / m_EqSys[timeLevel]->GetCheckpointSteps()
409 : 1;
410
411 // Checkpoint index.
412 size_t iChkPts = (m_chunkRank + wd * m_numChunks) * nChkPts + 1;
413
414 // Reinitialize check point number for each parallel-in-time
415 // iteration.
416 m_EqSys[timeLevel]->SetCheckpointNumber(iChkPts);
417
418 // Update parallel-in-time iteration number.
419 m_EqSys[timeLevel]->SetIterationNumberPIT(iter);
420
421 // Update parallel-in-time window number.
422 m_EqSys[timeLevel]->SetWindowNumberPIT(wd);
423
424 m_EqSys[timeLevel]->SetTime(time);
425 m_EqSys[timeLevel]->SetSteps(nstep);
426 m_EqSys[timeLevel]->DoSolve();
427}
428
429/**
430 *
431 */
433{
434 // Correct solution F -> F - Gold.
435 for (size_t i = 0; i < m_nVar; ++i)
436 {
438 m_coarseSolution[i], 1, m_fineSolution[i], 1);
439 }
440}
441
442/**
443 *
444 */
446{
447 // Interpolate coarse solution.
449
450 // Correct solution F -> F + Gnew.
451 for (size_t i = 0; i < m_nVar; ++i)
452 {
454 m_coarseSolution[i], 1, m_fineSolution[i], 1);
455 }
456}
457
458/**
459 *
460 */
462{
464 {
465 // Interpolate coarse solution to fine field.
466 Interpolate(m_EqSys[m_coarseLevel]->UpdateFields(),
467 m_EqSys[m_fineLevel]->UpdateFields(),
469 }
470}
471
472/**
473 *
474 */
476{
477 if (w == m_numWindowsPIT - 1)
478 {
479 // No windowing required for the last window.
480 return;
481 }
482
483 // Use last chunk solution as initial condition for the next
484 // window.
485 if (m_chunkRank == m_numChunks - 1)
486 {
487 for (size_t i = 0; i < m_nVar; ++i)
488 {
490 m_EqSys[m_fineLevel]->UpdatePhysField(i), 1,
491 m_initialCondition[i], 1);
492 }
493 }
494
495 // Broadcast I.C. for windowing.
496 for (size_t i = 0; i < m_nVar; ++i)
497 {
498 m_comm->GetTimeComm()->Bcast(m_initialCondition[i], m_numChunks - 1);
499 }
500}
501
502/**
503 *
504 */
505void DriverParareal::CopyConvergedCheckPoints(const size_t w, const size_t k)
506{
507 // Determine max number of iteration.
508 size_t kmax = k;
509 m_comm->GetTimeComm()->AllReduce(kmax, Nektar::LibUtilities::ReduceMax);
510
511 if (m_comm->GetSpaceComm()->GetRank() == 0)
512 {
513 for (size_t j = k; j < kmax; j++)
514 {
515 // Copy converged solution files from directory corresponding to
516 // iteration j - 1 to the directory corresponding to iteration j.
517
518 auto sessionName = m_EqSys[m_fineLevel]->GetSessionName();
519
520 // Input directory name.
521 std::string indir =
522 sessionName + "_" + std::to_string(j - 1) + ".pit";
523
524 /// Output directory name.
525 std::string outdir = sessionName + "_" + std::to_string(j) + ".pit";
526
527 for (size_t timeLevel = 0; timeLevel < m_nTimeLevel; timeLevel++)
528 {
529 // Number of checkpoint by chunk.
530 size_t nChkPts =
531 m_EqSys[timeLevel]->GetCheckpointSteps()
532 ? m_nsteps[timeLevel] /
533 m_EqSys[timeLevel]->GetCheckpointSteps()
534 : 0;
535
536 // Checkpoint index.
537 size_t iChkPts = (m_chunkRank + w * m_numChunks) * nChkPts;
538
539 for (size_t i = 1; i <= nChkPts; i++)
540 {
541 // Filename corresponding to checkpoint iChkPts.
542 std::string filename = sessionName + "_timeLevel" +
543 std::to_string(timeLevel) + "_" +
544 std::to_string(iChkPts + i) + ".chk";
545
546 // Intput full file name.
547 std::string infullname = indir + "/" + filename;
548
549 // Output full file name.
550 std::string outfullname = outdir + "/" + filename;
551
552 // Remove output file if already existing.
553 fs::remove_all(outfullname);
554
555 // Copy converged solution files.
556 fs::copy(infullname, outfullname);
557 }
558 }
559 }
560 }
561}
562
563/**
564 *
565 */
567{
568 PrintHeader("PRINT SOLUTION FILES", '-');
569
570 // Update field coefficients.
572
573 // Output solution files.
574 m_EqSys[m_fineLevel]->Output();
575}
576
577} // namespace Nektar::SolverUtils
#define ASSERTL0(condition, msg)
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
static std::string RegisterEnumValue(std::string pEnum, std::string pString, int pEnumValue)
Registers an enumeration value.
LibUtilities::CommSharedPtr m_comm
Communication object.
Definition Driver.h:80
Base class for the development of parallel-in-time solvers.
NekDouble m_totalTime
Total time integration interval.
void UpdateFieldCoeffs(const size_t timeLevel, const Array< OneD, const Array< OneD, NekDouble > > &in=NullNekDoubleArrayOfArray)
NekDouble m_chunkTime
Time integration interval per chunk.
void SendToNextProc(Array< OneD, Array< OneD, NekDouble > > &array, int &convergence)
size_t m_iterMaxPIT
Maximum number of parallel-in-time iteration.
Array< OneD, size_t > m_nsteps
Number of time steps for each time level.
SOLVER_UTILS_EXPORT void v_InitObject(std::ostream &out=std::cout) override
Virtual function for initialisation implementation.
void EvaluateExactSolution(const size_t timeLevel, const NekDouble &time)
void RecvFromPreviousProc(Array< OneD, Array< OneD, NekDouble > > &array, int &convergence)
Array< OneD, std::shared_ptr< UnsteadySystem > > m_EqSys
Equation system to solve.
void PrintHeader(const std::string &title, const char c)
NekDouble m_tolerPIT
ParallelInTime tolerance.
void PrintSolverInfo(std::ostream &out=std::cout)
bool m_exactSolution
Using exact solution to compute error norms.
void CopyFromPhysField(const size_t timeLevel, Array< OneD, Array< OneD, NekDouble > > &out)
void CopyToPhysField(const size_t timeLevel, const Array< OneD, const Array< OneD, NekDouble > > &in)
Array< OneD, size_t > m_npts
Number of dof for each time level.
Array< OneD, Array< OneD, NekDouble > > m_exactsoln
void SolutionConvergenceSummary(const size_t timeLevel)
Array< OneD, NekDouble > m_timestep
Time step for each time level.
void SolutionConvergenceMonitoring(const size_t timeLevel, const size_t iter)
void Interpolate(const Array< OneD, MultiRegions::ExpListSharedPtr > &infield, const Array< OneD, MultiRegions::ExpListSharedPtr > &outfield, const Array< OneD, Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray)
SOLVER_UTILS_EXPORT void v_Execute(std::ostream &out=std::cout) override
Virtual function for solve implementation.
static constexpr size_t m_fineLevel
void UpdateInitialConditionFromSolver(const size_t timeLevel)
static constexpr size_t m_coarseLevel
static std::string className
Name of the class.
void CopyConvergedCheckPoints(const size_t w, const size_t k)
SOLVER_UTILS_EXPORT void v_InitObject(std::ostream &out=std::cout) override
Virtual function for initialisation implementation.
void UpdateSolution(const size_t timeLevel, const NekDouble time, const size_t nstep, const size_t wd, const size_t iter)
Array< OneD, Array< OneD, NekDouble > > m_coarseSolution
static DriverSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
Creates an instance of this class.
SOLVER_UTILS_EXPORT DriverParareal(const LibUtilities::SessionReaderSharedPtr pSession, const SpatialDomains::MeshGraphSharedPtr pGraph)
Constructor.
void UpdateSolverInitialCondition(const size_t timeLevel)
Array< OneD, Array< OneD, NekDouble > > m_fineSolution
Array< OneD, Array< OneD, NekDouble > > m_initialCondition
std::shared_ptr< SessionReader > SessionReaderSharedPtr
DriverFactory & GetDriverFactory()
Definition Driver.cpp:64
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition MeshGraph.h:224
static Array< OneD, Array< OneD, NekDouble > > NullNekDoubleArrayOfArray
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.hpp:180
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition Vmath.hpp:825
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.hpp:220