ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_MRI.H
Go to the documentation of this file.
1 #ifndef ERF_MRI_H
2 #define ERF_MRI_H
3 
4 #include <AMReX_REAL.H>
5 #include <AMReX_Vector.H>
6 #include <AMReX_ParmParse.H>
7 #include <AMReX_IntegratorBase.H>
8 
9 #include <ERF_TI_slow_headers.H>
10 #include <ERF_TI_fast_headers.H>
11 
12 #include <functional>
13 
14 template<class T>
16 {
17 private:
18  /**
19  * \brief rhs is the right-hand-side function the integrator will use.
20  */
21  std::function<void(T&, const T&, const amrex::Real, const amrex::Real )> rhs;
22  std::function<void(T&, T&, T&, T&, const amrex::Real, const amrex::Real, const amrex::Real, const int)> slow_rhs_pre;
23  std::function<void(T&, T&, T&, T&, const amrex::Real, const amrex::Real, const amrex::Real, const int)> slow_rhs_inc;
24  std::function<void(T&, T&, T&, T&, T&, const amrex::Real, const amrex::Real, const amrex::Real, const int )> slow_rhs_post;
25  std::function<void(int, int, int, T&, const T&, T&, T&, T&, const amrex::Real, const amrex::Real,
26  const amrex::Real, const amrex::Real)> fast_rhs;
27 
28  /**
29  * \brief Integrator timestep size (Real)
30  */
31  amrex::Real timestep;
32 
33  /**
34  * \brief The ratio of slow timestep size / fast timestep size (int)
35  */
37 
38  /**
39  * \brief Should we not do acoustic substepping
40  */
42 
43  /**
44  * \brief Should we use the anelastic integrator
45  */
46  int anelastic;
47 
48  /**
49  * \brief How many components in the cell-centered MultiFab
50  */
52 
53  /**
54  * \brief Do we follow the recommendation to only perform a single substep in the first RK stage
55  */
57 
58  /**
59  * \brief The pre_update function is called by the integrator on stage data before using it to evaluate a right-hand side.
60  * \brief The post_update function is called by the integrator on stage data at the end of the stage
61  */
62  std::function<void (T&, int)> pre_update;
63  std::function<void (T&, amrex::Real, int, int)> post_update;
64  std::function<void (T&, T&, T&, amrex::Real, amrex::Real, int)> no_substep;
65 
66 
67  amrex::Vector<std::unique_ptr<T> > T_store;
68  T* S_sum;
71 
72  void initialize_data (const T& S_data)
73  {
74  // TODO: We can optimize memory by making the cell-centered part of S_sum, S_scratch
75  // have only 2 components, not ncomp_cons components
76  const bool include_ghost = true;
77  amrex::IntegratorOps<T>::CreateLike(T_store, S_data, include_ghost);
78  S_sum = T_store[0].get();
79  amrex::IntegratorOps<T>::CreateLike(T_store, S_data, include_ghost);
80  S_scratch = T_store[1].get();
81  amrex::IntegratorOps<T>::CreateLike(T_store, S_data, include_ghost);
82  F_slow = T_store[2].get();
83  }
84 
85 public:
86  MRISplitIntegrator () = default;
87 
88  MRISplitIntegrator (const T& S_data)
89  {
90  initialize_data(S_data);
91  }
92 
93  void initialize (const T& S_data)
94  {
95  initialize_data(S_data);
96  }
97 
98  ~MRISplitIntegrator () = default;
99 
100  // Declare a default move constructor so we ensure the destructor is
101  // not called when we return an object of this class by value
102  MRISplitIntegrator(MRISplitIntegrator&&) noexcept = default;
103 
104  // Declare a default move assignment operator
105  MRISplitIntegrator& operator=(MRISplitIntegrator&& other) noexcept = default;
106 
107  // Delete the copy constructor and copy assignment operators because
108  // the integrator allocates internal memory that is best initialized
109  // from scratch when needed instead of making a copy.
110 
111  // Delete the copy constructor
112  MRISplitIntegrator(const MRISplitIntegrator& other) = delete;
113  //
114  // Delete the copy assignment operator
115  MRISplitIntegrator& operator=(const MRISplitIntegrator& other) = delete;
116 
117  void setNcompCons(int _ncomp_cons)
118  {
119  ncomp_cons = _ncomp_cons;
120  }
121 
122  void setAnelastic(int _anelastic)
123  {
124  anelastic = _anelastic;
125  }
126 
127  void setNoSubstepping(int _no_substepping)
128  {
129  no_substepping = _no_substepping;
130  }
131 
132  void setForceFirstStageSingleSubstep(int _force_stage1_single_substep)
133  {
134  force_stage1_single_substep = _force_stage1_single_substep;
135  }
136 
137  void set_slow_rhs_pre (std::function<void(T&, T&, T&, T&, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
138  {
139  slow_rhs_pre = F;
140  }
141  void set_slow_rhs_inc (std::function<void(T&, T&, T&, T&, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
142  {
143  slow_rhs_inc = F;
144  }
145  void set_slow_rhs_post (std::function<void(T&, T&, T&, T&, T&, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
146  {
147  slow_rhs_post = F;
148  }
149 
150  void set_fast_rhs (std::function<void(int, int, int, T&, const T&, T&, T&, T&,
151  const amrex::Real, const amrex::Real,
152  const amrex::Real, const amrex::Real)> F)
153  {
154  fast_rhs = F;
155  }
156 
157  void set_slow_fast_timestep_ratio (const int timestep_ratio = 1)
158  {
159  slow_fast_timestep_ratio = timestep_ratio;
160  }
161 
163  {
165  }
166 
167  void set_pre_update (std::function<void (T&, int)> F)
168  {
169  pre_update = F;
170  }
171 
172  void set_post_update (std::function<void (T&, amrex::Real, int, int)> F)
173  {
174  post_update = F;
175  }
176 
177  void set_no_substep (std::function<void (T&, T&, T&, amrex::Real, amrex::Real, int)> F)
178  {
179  no_substep = F;
180  }
181 
182  std::function<void(T&, const T&, const amrex::Real, int)> get_rhs ()
183  {
184  return rhs;
185  }
186 
187  amrex::Real advance (T& S_old, T& S_new, amrex::Real time, const amrex::Real time_step)
188  {
189  BL_PROFILE_REGION("MRI_advance");
190  using namespace amrex;
191 
192  // *******************************************************************************
193  // !no_substepping: we only update the fast variables every fast timestep, then update
194  // the slow variables after the acoustic sub-stepping. This has
195  // two calls to slow_rhs so that we can update the slow variables
196  // with the velocity field after the acoustic substepping using
197  // the time-averaged velocity from the substepping
198  // no_substepping: we don't do any acoustic subcyling so we only make one call per RK
199  // stage to slow_rhs
200  // *******************************************************************************
201  timestep = time_step;
202 
203  const int substep_ratio = get_slow_fast_timestep_ratio();
204 
205  if (!no_substepping) {
206  AMREX_ALWAYS_ASSERT(substep_ratio > 1 && substep_ratio % 2 == 0);
207  }
208 
209  // Assume before advance() that S_old is valid data at the current time ("time" argument)
210  // And that if data is a MultiFab, both S_old and S_new contain ghost cells for evaluating a stencil based RHS
211  // We need this from S_old. This is convenient for S_new to have so we can use it
212  // as scratch space for stage values without creating a new scratch MultiFab with ghost cells.
213 
214  // NOTE: In the following, we use S_new to hold S*, S**, and finally, S^(n+1) at the new time
215  // DEFINITIONS:
216  // S_old = S^n
217  // S_sum = S(t)
218  // F_slow = F(S_stage)
219 
220  int n_data = IntVars::NumTypes;
221 
222  /**********************************************/
223  /* RK3 Integration with Acoustic Sub-stepping */
224  /**********************************************/
225  Vector<int> num_vars = {ncomp_cons, 1, 1, 1};
226  for (int i(0); i<n_data; ++i)
227  {
228  // Copy old -> new
229  MultiFab::Copy(S_new[i],S_old[i],0,0,num_vars[i],S_old[i].nGrowVect());
230  }
231 
232  // Timestep taken by the fast integrator
233  amrex::Real dtau;
234 
235  // How many timesteps taken by the fast integrator
236  int nsubsteps;
237 
238  // This is the final time of the full timestep (also the 3rd RK stage)
239  // Real new_time = time + timestep;
240 
241  amrex::Real time_stage = time;
242  amrex::Real old_time_stage;
243 
244  const amrex::Real sub_timestep = timestep / substep_ratio;
245 
246  if (!anelastic) {
247  // RK3 for compressible integrator
248  for (int nrk = 0; nrk < 3; nrk++)
249  {
250  // amrex::Print() << "Starting RK3: Step " << nrk+1 << std::endl;
251 
252  // Capture the time we got to in the previous RK step
253  old_time_stage = time_stage;
254 
255  if (nrk == 0) {
257  nsubsteps = 1; dtau = timestep / 3.0;
258  } else {
259  nsubsteps = substep_ratio/3; dtau = sub_timestep ;
260  }
261  time_stage = time + timestep / 3.0;
262  }
263  if (nrk == 1) {
264  if (no_substepping) {
265  nsubsteps = 1; dtau = 0.5 * timestep;
266  } else {
267  nsubsteps = substep_ratio/2; dtau = sub_timestep;
268  }
269  time_stage = time + timestep / 2.0;
270  }
271  if (nrk == 2) {
272  if (no_substepping) {
273  nsubsteps = 1; dtau = timestep;
274  } else {
275  nsubsteps = substep_ratio; dtau = sub_timestep;
276 
277  // STRT HACK -- this hack can be used to approximate the no-substepping algorithm
278  // nsubsteps = 1; dtau = timestep;
279  // END HACK
280  }
281  time_stage = time + timestep;
282  }
283 
284  // step 1 starts with S_stage = S^n and we always start substepping at the old time
285  // step 2 starts with S_stage = S^* and we always start substepping at the old time
286  // step 3 starts with S_stage = S^** and we always start substepping at the old time
287 
288  // All pre_update does is call cons_to_prim, and we have done this with the old
289  // data already before starting the RK steps
290  if (nrk > 0) {
291  pre_update(S_new, S_new[IntVars::cons].nGrow());
292  }
293 
294  // S_scratch also holds the average momenta over the fast iterations --
295  // to be used to update the slow variables -- we will initialize with
296  // the momenta used in the first call to the slow_rhs, then update
297  // inside fast_rhs, then use these values in the later call to slow_rhs
298 
299  slow_rhs_pre(*F_slow, S_old, S_new, *S_scratch, time, old_time_stage, time_stage, nrk);
300 
301  amrex::Real inv_fac = 1.0 / static_cast<amrex::Real>(nsubsteps);
302 
303  // ****************************************************
304  // Acoustic substepping
305  // ****************************************************
306  if (!no_substepping)
307  {
308  // *******************************************************************************
309  // Update the fast variables
310  // *******************************************************************************
311  for (int ks = 0; ks < nsubsteps; ++ks)
312  {
313  fast_rhs(ks, nsubsteps, nrk, *F_slow, S_old, S_new, *S_sum, *S_scratch, dtau, inv_fac,
314  time + ks*dtau, time + (ks+1) * dtau);
315 
316  } // ks
317 
318  } else {
319  no_substep(*S_sum, S_old, *F_slow, time + nsubsteps*dtau, nsubsteps*dtau, nrk);
320  }
321 
322  // ****************************************************
323  // Evaluate F_slow(S_stage) only for the slow variables
324  // Note that we are using the current stage versions (in S_new) of the slow variables
325  // (because we didn't update the slow variables in the substepping)
326  // but we are using the "new" versions (in S_sum) of the velocities
327  // (because we did update the fast variables in the substepping)
328  // ****************************************************
329  slow_rhs_post(*F_slow, S_old, S_new, *S_sum, *S_scratch, time, old_time_stage, time_stage, nrk);
330 
331  // Call the post-update hook for S_new after all the fast steps completed
332  // This will update S_prim that is used in the slow RHS
333  post_update(S_new, time + nsubsteps*dtau, S_new[IntVars::cons].nGrow(), S_new[IntVars::xmom].nGrow());
334  } // nrk
335 
336  } else {
337  // RK2 for anelastic integrator
338  for (int nrk = 0; nrk < 2; nrk++)
339  {
340  // Capture the time we got to in the previous RK step
341  old_time_stage = time_stage;
342 
343  if (nrk == 0) { nsubsteps = 1; dtau = timestep; time_stage = time + timestep; }
344  if (nrk == 1) { nsubsteps = 1; dtau = timestep; time_stage = time + timestep; }
345 
346  // All pre_update does is call cons_to_prim, and we have done this with the old
347  // data already before starting the RK steps
348  if (nrk > 0) {
349  pre_update(S_new, S_new[IntVars::cons].nGrow());
350  }
351 
352  // S_scratch also holds the average momenta over the fast iterations --
353  // to be used to update the slow variables -- we will initialize with
354  // the momenta used in the first call to the slow_rhs, then update
355  // inside fast_rhs, then use these values in the later call to slow_rhs
356 
357  slow_rhs_inc(*F_slow, S_old, S_new, *S_scratch, time, old_time_stage, time_stage, nrk);
358 
359  no_substep(*S_sum, S_old, *F_slow, time + nsubsteps*dtau, nsubsteps*dtau, nrk);
360 
361  slow_rhs_post(*F_slow, S_old, S_new, *S_sum, *S_scratch, time, old_time_stage, time_stage, nrk);
362 
363  post_update(S_new, time + nsubsteps*dtau, S_new[IntVars::cons].nGrow(), S_new[IntVars::xmom].nGrow());
364  } // nrk
365  }
366 
367  // Return timestep
368  return timestep;
369  }
370 
371  void map_data (std::function<void(T&)> Map)
372  {
373  for (auto& F : T_store) {
374  Map(*F);
375  }
376  }
377 };
378 
379 #endif
Definition: ERF_MRI.H:16
std::function< void(T &, amrex::Real, int, int)> post_update
Definition: ERF_MRI.H:63
T * F_slow
Definition: ERF_MRI.H:70
amrex::Vector< std::unique_ptr< T > > T_store
Definition: ERF_MRI.H:67
void set_slow_rhs_pre(std::function< void(T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
Definition: ERF_MRI.H:137
void map_data(std::function< void(T &)> Map)
Definition: ERF_MRI.H:371
std::function< void(T &, T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> slow_rhs_post
Definition: ERF_MRI.H:24
void set_no_substep(std::function< void(T &, T &, T &, amrex::Real, amrex::Real, int)> F)
Definition: ERF_MRI.H:177
void set_slow_rhs_inc(std::function< void(T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
Definition: ERF_MRI.H:141
int anelastic
Should we use the anelastic integrator.
Definition: ERF_MRI.H:46
void setNcompCons(int _ncomp_cons)
Definition: ERF_MRI.H:117
amrex::Real timestep
Integrator timestep size (Real)
Definition: ERF_MRI.H:31
MRISplitIntegrator()=default
std::function< void(T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> slow_rhs_pre
Definition: ERF_MRI.H:22
void setForceFirstStageSingleSubstep(int _force_stage1_single_substep)
Definition: ERF_MRI.H:132
int force_stage1_single_substep
Do we follow the recommendation to only perform a single substep in the first RK stage.
Definition: ERF_MRI.H:56
std::function< void(T &, int)> pre_update
The pre_update function is called by the integrator on stage data before using it to evaluate a right...
Definition: ERF_MRI.H:62
int ncomp_cons
How many components in the cell-centered MultiFab.
Definition: ERF_MRI.H:51
T * S_scratch
Definition: ERF_MRI.H:69
void setNoSubstepping(int _no_substepping)
Definition: ERF_MRI.H:127
void initialize(const T &S_data)
Definition: ERF_MRI.H:93
void set_pre_update(std::function< void(T &, int)> F)
Definition: ERF_MRI.H:167
MRISplitIntegrator(MRISplitIntegrator &&) noexcept=default
std::function< void(T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> slow_rhs_inc
Definition: ERF_MRI.H:23
std::function< void(int, int, int, T &, const T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const amrex::Real)> fast_rhs
Definition: ERF_MRI.H:26
void initialize_data(const T &S_data)
Definition: ERF_MRI.H:72
void set_post_update(std::function< void(T &, amrex::Real, int, int)> F)
Definition: ERF_MRI.H:172
MRISplitIntegrator(const T &S_data)
Definition: ERF_MRI.H:88
std::function< void(T &, const T &, const amrex::Real, int)> get_rhs()
Definition: ERF_MRI.H:182
void set_fast_rhs(std::function< void(int, int, int, T &, const T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const amrex::Real)> F)
Definition: ERF_MRI.H:150
std::function< void(T &, const T &, const amrex::Real, const amrex::Real)> rhs
rhs is the right-hand-side function the integrator will use.
Definition: ERF_MRI.H:21
void setAnelastic(int _anelastic)
Definition: ERF_MRI.H:122
int get_slow_fast_timestep_ratio()
Definition: ERF_MRI.H:162
std::function< void(T &, T &, T &, amrex::Real, amrex::Real, int)> no_substep
Definition: ERF_MRI.H:64
int slow_fast_timestep_ratio
The ratio of slow timestep size / fast timestep size (int)
Definition: ERF_MRI.H:36
~MRISplitIntegrator()=default
void set_slow_fast_timestep_ratio(const int timestep_ratio=1)
Definition: ERF_MRI.H:157
T * S_sum
Definition: ERF_MRI.H:68
amrex::Real advance(T &S_old, T &S_new, amrex::Real time, const amrex::Real time_step)
Definition: ERF_MRI.H:187
void set_slow_rhs_post(std::function< void(T &, T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
Definition: ERF_MRI.H:145
int no_substepping
Should we not do acoustic substepping.
Definition: ERF_MRI.H:41
@ NumTypes
Definition: ERF_IndexDefines.H:143
@ cons
Definition: ERF_IndexDefines.H:139
@ xmom
Definition: ERF_IndexDefines.H:140
@ T
Definition: ERF_IndexDefines.H:99
Definition: ERF_console_io.cpp:12