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