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 <algorithm>
13 #include <functional>
14 
15 template<class T>
17 {
18 private:
19  /**
20  * \brief rhs is the right-hand-side function the integrator will use.
21  */
22  std::function<void(T&, const T&, const double, const double)> rhs;
23  std::function<void(T&, T&, T&, const double, const double, const double, const int)> slow_rhs_pre;
24  std::function<void(T&, T&, T&, T&, const double, const double, const double, const int)> slow_rhs_post;
25  std::function<void(int, int, int, T&, const T&, T&, T&, const double, const double,
26  const amrex::Real, const double,
27  const double)> acoustic_substepping;
28 
29  /**
30  * \brief Integrator timestep size (Real)
31  */
32  double timestep;
33 
34  /**
35  * \brief The ratio of slow timestep size / fast timestep size (int)
36  */
38 
39  /**
40  * \brief Should we not do acoustic substepping
41  */
43 
44  /**
45  * \brief Should we use the anelastic integrator
46  */
47  int anelastic;
48 
49  /**
50  * \brief How many components in the cell-centered MultiFab
51  */
53 
54  /**
55  * \brief Do we follow the recommendation to only perform a single substep in the first RK stage
56  */
58 
59  /**
60  * \brief The no_substep function is called when we have no acoustic substepping
61  */
62  std::function<void (T&, T&, T&, const double, const double, int)> no_substep;
63 
64 
65  amrex::Vector<std::unique_ptr<T> > T_store;
66  T* S_sum;
68 
69  void initialize_data (const T& S_data)
70  {
71  // TODO: We can optimize memory by making the cell-centered part of S_sum
72  // have only 2 components, not ncomp_cons components
73  const bool include_ghost = true;
74  amrex::IntegratorOps<T>::CreateLike(T_store, S_data, include_ghost);
75  S_sum = T_store[0].get();
76  amrex::IntegratorOps<T>::CreateLike(T_store, S_data, include_ghost);
77  F_slow = T_store[1].get();
78  // initializing to zero
79  for (long idx = 0; idx < S_sum->size(); idx++) { (*S_sum)[idx].setVal(0); }
80  for (long idx = 0; idx < F_slow->size(); idx++) { (*F_slow)[idx].setVal(0); }
81  }
82 
83 public:
84  MRISplitIntegrator () = default;
85 
86  MRISplitIntegrator (const T& S_data)
87  {
88  initialize_data(S_data);
89  }
90 
91  void initialize (const T& S_data)
92  {
93  initialize_data(S_data);
94  }
95 
96  ~MRISplitIntegrator () = default;
97 
98  // Declare a default move constructor so we ensure the destructor is
99  // not called when we return an object of this class by value
100  MRISplitIntegrator(MRISplitIntegrator&&) noexcept = default;
101 
102  // Declare a default move assignment operator
103  MRISplitIntegrator& operator=(MRISplitIntegrator&& other) noexcept = default;
104 
105  // Delete the copy constructor and copy assignment operators because
106  // the integrator allocates internal memory that is best initialized
107  // from scratch when needed instead of making a copy.
108 
109  // Delete the copy constructor
110  MRISplitIntegrator(const MRISplitIntegrator& other) = delete;
111  //
112  // Delete the copy assignment operator
113  MRISplitIntegrator& operator=(const MRISplitIntegrator& other) = delete;
114 
115  void setNcompCons(int _ncomp_cons)
116  {
117  ncomp_cons = _ncomp_cons;
118  }
119 
120  void setAnelastic(int _anelastic)
121  {
122  anelastic = _anelastic;
123  }
124 
125  void setNoSubstepping(int _no_substepping)
126  {
127  no_substepping = _no_substepping;
128  }
129 
130  void setForceFirstStageSingleSubstep(int _force_stage1_single_substep)
131  {
132  force_stage1_single_substep = _force_stage1_single_substep;
133  }
134 
135  void set_slow_rhs_pre (std::function<void(T&, T&, T&, const double, const double, const double, const int)> F)
136  {
137  slow_rhs_pre = F;
138  }
139  void set_slow_rhs_post (std::function<void(T&, T&, T&, T&, const double, const double, const double, const int)> F)
140  {
141  slow_rhs_post = F;
142  }
143 
144  void set_acoustic_substepping (std::function<void(int, int, int, T&, const T&, T&, T&,
145  const double, const double,
146  const amrex::Real, const double,
147  const double)> F)
148  {
150  }
151 
152  void set_slow_fast_timestep_ratio (const int timestep_ratio = 1)
153  {
154  slow_fast_timestep_ratio = timestep_ratio;
155  }
156 
158  {
160  }
161 
162  void set_no_substep (std::function<void (T&, T&, T&, const double, const double, int)> F)
163  {
164  no_substep = F;
165  }
166 
167  std::function<void(T&, const T&, const double, const double)> get_rhs ()
168  {
169  return rhs;
170  }
171 
172  double advance (T& S_old, T& S_new, double time, const double time_step)
173  {
174  BL_PROFILE_REGION("MRI_advance");
175  using namespace amrex;
176 
177  // *******************************************************************************
178  // !no_substepping: we only update the fast variables every fast timestep, then update
179  // the slow variables after the acoustic sub-stepping. This has
180  // two calls to slow_rhs so that we can update the slow variables
181  // with the velocity field after the acoustic substepping using
182  // the time-averaged velocity from the substepping
183  // no_substepping: we don't do any acoustic subcycling so we only make one call per RK
184  // stage to slow_rhs
185  // *******************************************************************************
186  timestep = time_step;
187 
188  const int substep_ratio = get_slow_fast_timestep_ratio();
189 
190  if (!no_substepping) {
191  AMREX_ALWAYS_ASSERT(substep_ratio > 1 && substep_ratio % 2 == 0);
192  }
193 
194  // Assume before advance() that S_old is valid data at the current time ("time" argument)
195  // And that if data is a MultiFab, both S_old and S_new contain ghost cells for evaluating a stencil based RHS
196  // We need this from S_old. This is convenient for S_new to have so we can use it
197  // as scratch space for stage values without creating a new scratch MultiFab with ghost cells.
198 
199  // NOTE: In the following, we use S_new to hold S*, S**, and finally, S^(n+1) at the new time
200  // DEFINITIONS:
201  // S_old = S^n
202  // S_sum = S(t)
203  // F_slow = F(S_stage)
204 
205  int n_data = IntVars::NumTypes;
206 
207  /**********************************************/
208  /* RK3 Integration with Acoustic Sub-stepping */
209  /**********************************************/
210  Vector<int> num_vars = {ncomp_cons, 1, 1, 1};
211  for (int i(0); i<n_data; ++i)
212  {
213  // Copy old -> new
214  MultiFab::Copy(S_new[i],S_old[i],0,0,num_vars[i],S_old[i].nGrowVect());
215  }
216 
217  // Timestep taken by the fast integrator
218  double dtau;
219 
220  // How many timesteps taken by the fast integrator
221  int nsubsteps;
222 
223  // This is the final time of the full timestep (also the 3rd RK stage)
224  // Real new_time = time + timestep;
225 
226  double time_stage = time;
227  double old_time_stage;
228 
229  if (!anelastic) {
230  // RK3 for compressible integrator
231  for (int nrk = 0; nrk < 3; nrk++)
232  {
233  // Capture the time we got to in the previous RK step
234  old_time_stage = time_stage;
235 
236  if (nrk == 0) {
238  nsubsteps = 1;
239  dtau = timestep / three;
240  } else {
241  // Clamp to 1: substep_ratio is only required to be even, so a ratio of 2
242  // would otherwise give zero substeps and leave S_sum stale in this stage
243  nsubsteps = std::max(substep_ratio/3, 1);
244  dtau = (timestep / three) / static_cast<double>(nsubsteps);
245  }
246  time_stage = time + timestep / three;
247  }
248  if (nrk == 1) {
249  if (no_substepping) {
250  nsubsteps = 1;
251  dtau = myhalf * timestep;
252  } else {
253  nsubsteps = substep_ratio/2;
254  dtau = (myhalf * timestep) / static_cast<double>(nsubsteps);
255  }
256  time_stage = time + timestep / two;
257  }
258  if (nrk == 2) {
259  if (no_substepping) {
260  nsubsteps = 1;
261  dtau = timestep;
262  } else {
263  nsubsteps = substep_ratio;
264  dtau = timestep / static_cast<double>(nsubsteps);
265  }
266  time_stage = time + timestep;
267  }
268 
269  // step 1 starts with S_stage = S^n and we always start substepping at the old time
270  // step 2 starts with S_stage = S^* and we always start substepping at the old time
271  // step 3 starts with S_stage = S^** and we always start substepping at the old time
272 
273  slow_rhs_pre(*F_slow, S_old, S_new, time, old_time_stage, time_stage, nrk);
274 
275  amrex::Real inv_fac = one / 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  acoustic_substepping(ks, nsubsteps, nrk, *F_slow, S_old, S_new, *S_sum, dtau, timestep, 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, 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  slow_rhs_pre(*F_slow, S_old, S_new, time, old_time_stage, time_stage, nrk);
317 
318  no_substep(*S_sum, S_old, *F_slow, time + nsubsteps*dtau, nsubsteps*dtau, nrk);
319 
320  // ****************************************************
321  // Evaluate F_slow(S_stage) only for the slow variables
322  // Note that we are using the current stage versions (in S_new) of the slow variables
323  // (because we didn't update the slow variables in the substepping)
324  // but we are using the "new" versions (in S_sum) of the velocities
325  // (because we did update the fast variables in the substepping)
326  // ****************************************************
327  slow_rhs_post(*F_slow, S_old, S_new, *S_sum, time, old_time_stage, time_stage, nrk);
328  } // nrk
329  }
330 
331  // Return timestep
332  return timestep;
333  }
334 
335  void map_data (std::function<void(T&)> Map)
336  {
337  for (auto& F : T_store) {
338  Map(*F);
339  }
340  }
341 };
342 
343 #endif
constexpr amrex::Real three
Definition: ERF_Constants.H:11
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
Real T
Definition: ERF_InitCustomPert_Bubble.H:106
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:365
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_MRI.H:17
T * F_slow
Definition: ERF_MRI.H:67
std::function< void(T &, const T &, const double, const double)> get_rhs()
Definition: ERF_MRI.H:167
amrex::Vector< std::unique_ptr< T > > T_store
Definition: ERF_MRI.H:65
void set_slow_rhs_post(std::function< void(T &, T &, T &, T &, const double, const double, const double, const int)> F)
Definition: ERF_MRI.H:139
void map_data(std::function< void(T &)> Map)
Definition: ERF_MRI.H:335
void set_slow_rhs_pre(std::function< void(T &, T &, T &, const double, const double, const double, const int)> F)
Definition: ERF_MRI.H:135
int anelastic
Should we use the anelastic integrator.
Definition: ERF_MRI.H:47
void setNcompCons(int _ncomp_cons)
Definition: ERF_MRI.H:115
MRISplitIntegrator()=default
std::function< void(T &, const T &, const double, const double)> rhs
rhs is the right-hand-side function the integrator will use.
Definition: ERF_MRI.H:22
void setForceFirstStageSingleSubstep(int _force_stage1_single_substep)
Definition: ERF_MRI.H:130
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:57
int ncomp_cons
How many components in the cell-centered MultiFab.
Definition: ERF_MRI.H:52
void setNoSubstepping(int _no_substepping)
Definition: ERF_MRI.H:125
double timestep
Integrator timestep size (Real)
Definition: ERF_MRI.H:32
std::function< void(T &, T &, T &, const double, const double, const double, const int)> slow_rhs_pre
Definition: ERF_MRI.H:23
void set_no_substep(std::function< void(T &, T &, T &, const double, const double, int)> F)
Definition: ERF_MRI.H:162
void initialize(const T &S_data)
Definition: ERF_MRI.H:91
MRISplitIntegrator(MRISplitIntegrator &&) noexcept=default
void initialize_data(const T &S_data)
Definition: ERF_MRI.H:69
MRISplitIntegrator(const T &S_data)
Definition: ERF_MRI.H:86
std::function< void(T &, T &, T &, T &, const double, const double, const double, const int)> slow_rhs_post
Definition: ERF_MRI.H:24
void setAnelastic(int _anelastic)
Definition: ERF_MRI.H:120
std::function< void(T &, T &, T &, const double, const double, int)> no_substep
The no_substep function is called when we have no acoustic substepping.
Definition: ERF_MRI.H:62
int get_slow_fast_timestep_ratio()
Definition: ERF_MRI.H:157
double advance(T &S_old, T &S_new, double time, const double time_step)
Definition: ERF_MRI.H:172
int slow_fast_timestep_ratio
The ratio of slow timestep size / fast timestep size (int)
Definition: ERF_MRI.H:37
void set_acoustic_substepping(std::function< void(int, int, int, T &, const T &, T &, T &, const double, const double, const amrex::Real, const double, const double)> F)
Definition: ERF_MRI.H:144
~MRISplitIntegrator()=default
void set_slow_fast_timestep_ratio(const int timestep_ratio=1)
Definition: ERF_MRI.H:152
T * S_sum
Definition: ERF_MRI.H:66
std::function< void(int, int, int, T &, const T &, T &, T &, const double, const double, const amrex::Real, const double, const double)> acoustic_substepping
Definition: ERF_MRI.H:27
int no_substepping
Should we not do acoustic substepping.
Definition: ERF_MRI.H:42
@ NumTypes
Definition: ERF_IndexDefines.H:198
Definition: ERF_ConsoleIO.cpp:15