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_IntegratorBase.H>
7 
8 #include <ERF_TI_slow_headers.H>
9 #include <ERF_TI_fast_headers.H>
10 
11 #include <algorithm>
12 #include <functional>
13 
14 /**
15  * @brief Split integrator for MRI simulations handling slow and fast timescales.
16  * @tparam T State type.
17  */
18 template<class T>
20 {
21 private:
22  /**
23  * \brief rhs is the right-hand-side function the integrator will use.
24  */
25  std::function<void(T&, const T&, const double, const double)> rhs;
26  std::function<void(T&, T&, T&, const double, const double, const double, const int)> slow_rhs_pre;
27  std::function<void(T&, T&, T&, T&, const double, const double, const double, const int)> slow_rhs_post;
28  std::function<void(int, int, int, T&, const T&, T&, T&, const double, const double,
29  const amrex::Real, const double,
30  const double)> acoustic_substepping;
31 
32  /**
33  * \brief Integrator timestep size (Real)
34  */
35  double timestep;
36 
37  /**
38  * \brief The ratio of slow timestep size / fast timestep size (int)
39  */
41 
42  /**
43  * \brief Should we not do acoustic substepping
44  */
46 
47  /**
48  * \brief Should we use the anelastic integrator
49  */
50  int anelastic;
51 
52  /**
53  * \brief Which two-stage scheme the anelastic integrator uses
54  */
55  AnelasticType anelastic_type = AnelasticType::RK2;
56 
57  /**
58  * \brief How many components in the cell-centered MultiFab
59  */
61 
62  /**
63  * \brief Do we follow the recommendation to only perform a single substep in the first RK stage
64  */
66 
67  /**
68  * \brief The no_substep function is called when we have no acoustic substepping
69  */
70  std::function<void (T&, T&, T&, const double, const double, int)> no_substep;
71 
72 
73  amrex::Vector<std::unique_ptr<T> > T_store;
74  T* S_sum;
76 
77  /**
78  * @brief Allocate internal storage for integrator variables.
79  * @param[in] S_data Reference state used to determine storage size.
80  */
81  void initialize_data (const T& S_data)
82  {
83  // TODO: We can optimize memory by making the cell-centered part of S_sum
84  // have only 2 components, not ncomp_cons components
85  const bool include_ghost = true;
86  amrex::IntegratorOps<T>::CreateLike(T_store, S_data, include_ghost);
87  S_sum = T_store[0].get();
88  amrex::IntegratorOps<T>::CreateLike(T_store, S_data, include_ghost);
89  F_slow = T_store[1].get();
90  // initializing to zero
91  for (long idx = 0; idx < S_sum->size(); idx++) { (*S_sum)[idx].setVal(0); }
92  for (long idx = 0; idx < F_slow->size(); idx++) { (*F_slow)[idx].setVal(0); }
93  }
94 
95 public:
96  MRISplitIntegrator () = default;
97 
98  MRISplitIntegrator (const T& S_data)
99  {
100  initialize_data(S_data);
101  }
102 
103  /**
104  * @brief Initialize integrator storage.
105  * @param[in] S_data Reference state used to determine storage size.
106  */
107  void initialize (const T& S_data)
108  {
109  initialize_data(S_data);
110  }
111 
112  ~MRISplitIntegrator () = default;
113 
114  // Declare a default move constructor so we ensure the destructor is
115  // not called when we return an object of this class by value
116  MRISplitIntegrator (MRISplitIntegrator&&) noexcept = default;
117 
118  // Declare a default move assignment operator
119  MRISplitIntegrator& operator=(MRISplitIntegrator&& other) noexcept = default;
120 
121  // Delete the copy constructor and copy assignment operators because
122  // the integrator allocates internal memory that is best initialized
123  // from scratch when needed instead of making a copy.
124 
125  // Delete the copy constructor
126  MRISplitIntegrator (const MRISplitIntegrator& other) = delete;
127  //
128  // Delete the copy assignment operator
129  MRISplitIntegrator& operator=(const MRISplitIntegrator& other) = delete;
130 
131  /**
132  * @brief Set the number of conservative components.
133  * @param[in] _ncomp_cons Number of conservative components.
134  */
135  void setNcompCons (int _ncomp_cons)
136  {
137  ncomp_cons = _ncomp_cons;
138  }
139 
140  /**
141  * @brief Set whether to use the anelastic integrator.
142  * @param[in] _anelastic Integer flag (1 for anelastic, 0 for compressible).
143  */
144  void setAnelastic (int _anelastic)
145  {
146  anelastic = _anelastic;
147  }
148 
149  /**
150  * @brief Set which two-stage scheme the anelastic integrator uses.
151  * @param[in] _anelastic_type AnelasticType::RK2 or AnelasticType::MidPoint.
152  */
153  void setAnelasticType (AnelasticType _anelastic_type = AnelasticType::RK2)
154  {
155  anelastic_type = _anelastic_type;
156  }
157 
158  /**
159  * @brief Set whether acoustic substepping is disabled.
160  * @param[in] _no_substepping Integer flag (1 to disable substepping).
161  */
162  void setNoSubstepping (int _no_substepping)
163  {
164  no_substepping = _no_substepping;
165  }
166 
167  /**
168  * @brief Force the first RK stage to perform only a single substep.
169  * @param[in] _force_stage1_single_substep Integer flag to enable this behavior.
170  */
171  void setForceFirstStageSingleSubstep (int _force_stage1_single_substep)
172  {
173  force_stage1_single_substep = _force_stage1_single_substep;
174  }
175 
176  /**
177  * @brief Set the pre-substepping slow RHS function.
178  * @param[in] F Function to compute the pre-substepping slow RHS.
179  */
180  void set_slow_rhs_pre (std::function<void(T&, T&, T&, const double, const double, const double, const int)> F)
181  {
182  slow_rhs_pre = F;
183  }
184  void set_slow_rhs_post (std::function<void(T&, T&, T&, T&, const double, const double, const double, const int)> F)
185  {
186  slow_rhs_post = F;
187  }
188 
189  /**
190  * @brief Set the acoustic substepping function.
191  * @param[in] F Function to perform the acoustic substepping update.
192  */
193  void set_acoustic_substepping (std::function<void(int, int, int, T&, const T&, T&, T&,
194  const double, const double,
195  const amrex::Real, const double,
196  const double)> F)
197  {
199  }
200 
201  /**
202  * @brief Set the ratio of slow to fast timestep sizes.
203  * @param[in] timestep_ratio Ratio of slow/fast timesteps.
204  */
205  void set_slow_fast_timestep_ratio (const int timestep_ratio = 1)
206  {
207  slow_fast_timestep_ratio = timestep_ratio;
208  }
209 
210  /**
211  * @brief Get the current slow-to-fast timestep ratio.
212  * @return The slow/fast timestep ratio.
213  */
215  {
217  }
218 
219  /**
220  * @brief Set the function to be called when acoustic substepping is disabled.
221  * @param[in] F Function to use in place of substepping.
222  */
223  void set_no_substep (std::function<void (T&, T&, T&, const double, const double, int)> F)
224  {
225  no_substep = F;
226  }
227 
228  std::function<void(T&, const T&, const double, const double)> get_rhs ()
229  {
230  return rhs;
231  }
232 
233  /**
234  * @brief Advance the state from time to time + time_step.
235  * @param[in,out] S_old Current state.
236  * @param[in,out] S_new State to be updated.
237  * @param[in] time Current simulation time.
238  * @param[in] time_step Timestep size.
239  * @return The actual timestep taken.
240  */
241  double advance (T& S_old, T& S_new, double time, const double time_step)
242  {
243  BL_PROFILE_REGION("MRI_advance");
244  using namespace amrex;
245 
246  // *******************************************************************************
247  // !no_substepping: we only update the fast variables every fast timestep, then update
248  // the slow variables after the acoustic sub-stepping. This has
249  // 2 calls to slow_rhs so that we can update the slow variables
250  // with the velocity field after the acoustic substepping using
251  // the time-averaged velocity from the substepping
252  // no_substepping: we don't do any acoustic subcycling so we only make one call per RK
253  // stage to slow_rhs
254  // *******************************************************************************
255  timestep = time_step;
256 
257  const int substep_ratio = get_slow_fast_timestep_ratio();
258 
259  if (!no_substepping) {
260  AMREX_ALWAYS_ASSERT(substep_ratio > 1 && substep_ratio % 2 == 0);
261  }
262 
263  // Assume before advance() that S_old is valid data at the current time ("time" argument)
264  // And that if data is a MultiFab, both S_old and S_new contain ghost cells for evaluating a stencil based RHS
265  // We need this from S_old. This is convenient for S_new to have so we can use it
266  // as scratch space for stage values without creating a new scratch MultiFab with ghost cells.
267 
268  // NOTE: In the following, we use S_new to hold S*, S**, and finally, S^(n+1) at the new time
269  // DEFINITIONS:
270  // S_old = S^n
271  // S_sum = S(t)
272  // F_slow = F(S_stage)
273 
274  int n_data = IntVars::NumTypes;
275 
276  /**********************************************/
277  /* RK3 Integration with Acoustic Sub-stepping */
278  /**********************************************/
279  Vector<int> num_vars = {ncomp_cons, 1, 1, 1};
280  for (int i(0); i<n_data; ++i)
281  {
282  // Copy old -> new
283  MultiFab::Copy(S_new[i],S_old[i],0,0,num_vars[i],S_old[i].nGrowVect());
284  }
285 
286  // Timestep taken by the fast integrator
287  double dtau;
288 
289  // How many timesteps taken by the fast integrator
290  int nsubsteps;
291 
292  // This is the final time of the full timestep (also the 3rd RK stage)
293  // Real new_time = time + timestep;
294 
295  double time_stage = time;
296  double old_time_stage;
297 
298  if (!anelastic) {
299  // RK3 for compressible integrator
300  for (int nrk = 0; nrk < 3; nrk++)
301  {
302  // Capture the time we got to in the previous RK step
303  old_time_stage = time_stage;
304 
305  if (nrk == 0) {
307  nsubsteps = 1;
308  dtau = timestep / three;
309  } else {
310  // Clamp to 1: substep_ratio is only required to be even, so a ratio of 2
311  // would otherwise give zero substeps and leave S_sum stale in this stage
312  nsubsteps = std::max(substep_ratio/3, 1);
313  dtau = (timestep / three) / static_cast<double>(nsubsteps);
314  }
315  time_stage = time + timestep / three;
316  }
317  if (nrk == 1) {
318  if (no_substepping) {
319  nsubsteps = 1;
320  dtau = myhalf * timestep;
321  } else {
322  nsubsteps = std::max(substep_ratio/2, 1);
323  dtau = (myhalf * timestep) / static_cast<double>(nsubsteps);
324  }
325  time_stage = time + timestep / two;
326  }
327  if (nrk == 2) {
328  if (no_substepping) {
329  nsubsteps = 1;
330  dtau = timestep;
331  } else {
332  nsubsteps = substep_ratio;
333  dtau = timestep / static_cast<double>(nsubsteps);
334  }
335  time_stage = time + timestep;
336  }
337 
338  // step 1 starts with S_stage = S^n and we always start substepping at the old time
339  // step 2 starts with S_stage = S^* and we always start substepping at the old time
340  // step 3 starts with S_stage = S^** and we always start substepping at the old time
341 
342  slow_rhs_pre(*F_slow, S_old, S_new, time, old_time_stage, time_stage, nrk);
343 
344  amrex::Real inv_fac = one / static_cast<amrex::Real>(nsubsteps);
345 
346  // ****************************************************
347  // Acoustic substepping
348  // ****************************************************
349  if (!no_substepping)
350  {
351  // *******************************************************************************
352  // Update the fast variables
353  // *******************************************************************************
354  for (int ks = 0; ks < nsubsteps; ++ks)
355  {
356  acoustic_substepping(ks, nsubsteps, nrk, *F_slow, S_old, S_new, *S_sum, dtau, timestep, inv_fac,
357  time + ks*dtau, time + (ks+1) * dtau);
358 
359  } // ks
360 
361  } else {
362  no_substep(*S_sum, S_old, *F_slow, time + nsubsteps*dtau, nsubsteps*dtau, nrk);
363  }
364 
365  // ****************************************************
366  // Evaluate F_slow(S_stage) only for the slow variables
367  // Note that we are using the current stage versions (in S_new) of the slow variables
368  // (because we didn't update the slow variables in the substepping)
369  // but we are using the "new" versions (in S_sum) of the velocities
370  // (because we did update the fast variables in the substepping)
371  // ****************************************************
372  slow_rhs_post(*F_slow, S_old, S_new, *S_sum, time, old_time_stage, time_stage, nrk);
373  } // nrk
374 
375  } else {
376  // Two-stage integrator for the anelastic equations.
377  //
378  // SSP-RK2 (Heun): both stages advance a full timestep from S^n, and the
379  // second stage averages the slow source with the one from the first,
380  // which erf_slow_rhs_{pre,post} do when (anelastic_type == RK2 && nrk == 1).
381  //
382  // MidPoint: the first stage advances only a half timestep,
383  // S^* = S^n + (dt/2) F(S^n)
384  // S^n+1 = S^n + dt F(S^*)
385  // and the second stage takes no average. The vertical diffusion
386  // is second order in time with a tridiagonal solve in the first
387  // stage only and none in the second.
388  const bool l_midpoint = (anelastic_type == AnelasticType::MidPoint);
389 
390  for (int nrk = 0; nrk < 2; nrk++)
391  {
392  // Capture the time we got to in the previous RK step
393  old_time_stage = time_stage;
394 
395  // Set the timestep for this stage -- both stages advance from the old time
396  nsubsteps = 1;
397  dtau = (l_midpoint && (nrk == 0)) ? myhalf * timestep : timestep;
398  time_stage = time + nsubsteps * dtau;
399 
400  slow_rhs_pre(*F_slow, S_old, S_new, time, old_time_stage, time_stage, nrk);
401 
402  no_substep(*S_sum, S_old, *F_slow, time + nsubsteps*dtau, nsubsteps*dtau, nrk);
403 
404  // ****************************************************
405  // Evaluate F_slow(S_stage) only for the slow variables
406  // Note that we are using the current stage versions (in S_new) of the slow variables
407  // (because we didn't update the slow variables in the substepping)
408  // but we are using the "new" versions (in S_sum) of the velocities
409  // (because we did update the fast variables in the substepping)
410  // ****************************************************
411  slow_rhs_post(*F_slow, S_old, S_new, *S_sum, time, old_time_stage, time_stage, nrk);
412  } // nrk
413  }
414 
415  // Return timestep
416  return timestep;
417  }
418 
419  /**
420  * @brief Apply a mapping function to all internal stored data.
421  * @param[in] Map Mapping function to apply to each stored state object.
422  */
423  void map_data (std::function<void(T&)> Map)
424  {
425  for (auto& F : T_store) {
426  Map(*F);
427  }
428  }
429 };
430 
431 #endif
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:396
constexpr amrex::Real three
Definition: ERF_NumericalConstants.H:32
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:31
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Split integrator for MRI simulations handling slow and fast timescales.
Definition: ERF_MRI.H:20
T * F_slow
Definition: ERF_MRI.H:75
std::function< void(T &, const T &, const double, const double)> get_rhs()
Definition: ERF_MRI.H:228
amrex::Vector< std::unique_ptr< T > > T_store
Definition: ERF_MRI.H:73
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:184
void map_data(std::function< void(T &)> Map)
Apply a mapping function to all internal stored data.
Definition: ERF_MRI.H:423
void set_slow_rhs_pre(std::function< void(T &, T &, T &, const double, const double, const double, const int)> F)
Set the pre-substepping slow RHS function.
Definition: ERF_MRI.H:180
int anelastic
Should we use the anelastic integrator.
Definition: ERF_MRI.H:50
void setNcompCons(int _ncomp_cons)
Set the number of conservative components.
Definition: ERF_MRI.H:135
void setAnelasticType(AnelasticType _anelastic_type=AnelasticType::RK2)
Set which two-stage scheme the anelastic integrator uses.
Definition: ERF_MRI.H:153
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:25
void setForceFirstStageSingleSubstep(int _force_stage1_single_substep)
Force the first RK stage to perform only a single substep.
Definition: ERF_MRI.H:171
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:65
int ncomp_cons
How many components in the cell-centered MultiFab.
Definition: ERF_MRI.H:60
void setNoSubstepping(int _no_substepping)
Set whether acoustic substepping is disabled.
Definition: ERF_MRI.H:162
AnelasticType anelastic_type
Which two-stage scheme the anelastic integrator uses.
Definition: ERF_MRI.H:55
double timestep
Integrator timestep size (Real)
Definition: ERF_MRI.H:35
std::function< void(T &, T &, T &, const double, const double, const double, const int)> slow_rhs_pre
Definition: ERF_MRI.H:26
void set_no_substep(std::function< void(T &, T &, T &, const double, const double, int)> F)
Set the function to be called when acoustic substepping is disabled.
Definition: ERF_MRI.H:223
void initialize(const T &S_data)
Initialize integrator storage.
Definition: ERF_MRI.H:107
MRISplitIntegrator(MRISplitIntegrator &&) noexcept=default
void initialize_data(const T &S_data)
Allocate internal storage for integrator variables.
Definition: ERF_MRI.H:81
MRISplitIntegrator(const T &S_data)
Definition: ERF_MRI.H:98
std::function< void(T &, T &, T &, T &, const double, const double, const double, const int)> slow_rhs_post
Definition: ERF_MRI.H:27
void setAnelastic(int _anelastic)
Set whether to use the anelastic integrator.
Definition: ERF_MRI.H:144
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:70
int get_slow_fast_timestep_ratio()
Get the current slow-to-fast timestep ratio.
Definition: ERF_MRI.H:214
double advance(T &S_old, T &S_new, double time, const double time_step)
Advance the state from time to time + time_step.
Definition: ERF_MRI.H:241
int slow_fast_timestep_ratio
The ratio of slow timestep size / fast timestep size (int)
Definition: ERF_MRI.H:40
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)
Set the acoustic substepping function.
Definition: ERF_MRI.H:193
~MRISplitIntegrator()=default
void set_slow_fast_timestep_ratio(const int timestep_ratio=1)
Set the ratio of slow to fast timestep sizes.
Definition: ERF_MRI.H:205
T * S_sum
Definition: ERF_MRI.H:74
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:30
int no_substepping
Should we not do acoustic substepping.
Definition: ERF_MRI.H:45
@ NumTypes
Definition: ERF_IndexDefines.H:236
@ T
Definition: ERF_IndexDefines.H:128
Definition: ERF_ConsoleIO.cpp:15