REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_ComputeTimestep.cpp
Go to the documentation of this file.
1#include <REMORA.H>
2
3#include <cmath>
4#include <iomanip>
5#include <sstream>
6
7using namespace amrex;
8
9/**
10 * Report the per-level timestep hierarchy: nsubsteps[lev] baroclinic steps per parent step,
11 * each subdivided ndtfast times. Must follow ComputeDt, since dt is seeded to
12 * bogus_large_value.
13 */
14void
16{
17 // No hierarchy to report, and estTimeStep's verbose output already covers dt. Nothing
18 // else prints the derived values, and no test exercises estTimeStep -- every input sets
19 // remora.fixed_dt -- so this is the practical check that dt and dtfast came out right.
20 if (max_level == 0) { return; }
21
22 amrex::Print() << "\n Timestep hierarchy"
23 << " (remora.do_substep = " << do_substep
24 << ", remora.ndtfast = " << ndtfast
25 << ", nfast = " << nfast << ")\n"
26 << " ==================\n\n"
27 << " Level Ref ratio Substeps Fast steps Slow dt Fast dt\n"
28 << " per lev-1 per lev-0 (s) (s)\n";
29
30 // Running product of the substep counts: barotropic work per level-0 step.
31 int cum_substeps = 1;
32
33 for (int lev = 0; lev <= finest_level; ++lev)
34 {
36
37 std::ostringstream ratio;
38 if (lev == 0) {
39 ratio << "---";
40 } else {
41 ratio << ref_ratio[lev-1][0] << " x " << ref_ratio[lev-1][1];
42 }
43
44 amrex::Print() << " " << std::setw(5) << lev
45 << " " << std::setw(9) << ratio.str()
46 << " " << std::setw(9) << nsubsteps[lev]
47 << " " << std::setw(10) << cum_substeps * ndtfast
48 << " " << std::setw(11) << std::fixed << std::setprecision(4) << dt[lev]
49 << " " << std::setw(11) << std::fixed << std::setprecision(4)
50 << dt[lev] / Real(ndtfast)
51 << "\n";
52 }
53 amrex::Print() << std::endl;
54}
55
56void
58{
60
61 for (int lev = 0; lev <= finest_level; ++lev)
62 {
64 }
65
66 ParallelDescriptor::ReduceRealMin(&dt_tmp[0], dt_tmp.size());
67
68 Real dt_0 = dt_tmp[0];
69 int n_factor = 1;
70 for (int lev = 0; lev <= finest_level; ++lev) {
71 dt_tmp[lev] = amrex::min(dt_tmp[lev], change_max*dt[lev]);
73 dt_0 = amrex::min(dt_0, n_factor*dt_tmp[lev]);
74 }
75
76 // dt_0 is identical on every rank here, so this aborts collectively. The
77 // bogus_large_value test is the load-bearing one: the sentinel used to survive to
78 // this point intact whenever stop_time was left at its default of Real::max(), and to
79 // be laundered into stop_time - t_new[0] -- a single step covering the entire run --
80 // whenever stop_time was set. The change_max cap above cannot catch either case,
81 // since dt[] is itself seeded to bogus_large_value.
82 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(dt_0 > zero && std::isfinite(dt_0) &&
84 "REMORA::ComputeDt: computed a non-positive, "
85 "non-finite, or unusably large level-0 dt");
86
87 // Limit dt's by the value of stop_time.
89 const Real eps = Real(1.e-3)*dt_0;
90 if (t_new[0] + dt_0 > stop_elapsed - eps) {
91 dt_0 = stop_elapsed - t_new[0];
92 }
93
94 dt[0] = dt_0;
95 for (int lev = 1; lev <= finest_level; ++lev) {
96 dt[lev] = dt[lev-1] / nsubsteps[lev];
97 }
98}
99
100/**
101 * Estimate the largest stable slow (baroclinic) timestep on a level.
102 *
103 * The estimate is the smaller of an advective limit and an external gravity wave limit,
104 * each a maximum over the wet cells of the level:
105 *
106 * dt_adv = cfl / max( |u|*pm, |v|*pn, |w|/Hz )
107 * dt_grav = cfl * ndtfast / max( sqrt(g*|h|) * sqrt(pm^2 + pn^2) )
108 *
109 * The second is the Courant quantity ROMS forms as Cg_max in metrics.F. It is what keeps
110 * the estimate finite for a run started from rest, where every velocity is zero and the
111 * advective limit alone is undefined.
112 *
113 * pm and pn are the per-cell 1/dx and 1/dy metric terms rather than the geometry's
114 * uniform cell size, so stretched and curvilinear grids give the right answer.
115 *
116 * @param[in] level level of refinement
117 */
118Real
120{
121 BL_PROFILE("REMORA::estTimeStep()");
122
123 // The barotropic mode is substepped ndtfast times per baroclinic step
124 // (REMORA_Advance.cpp), so the slow step only has to resolve the external gravity
125 // wave to within that ratio. ReadParameters guarantees ndtfast is positive.
126
127 // g is a file-scope constexpr; hoist it into a local for the device lambda.
128 const Real grav = solverChoice.g;
129
130 MultiFab ccvel(grids[level],dmap[level],3,0);
131
134
135 // Two maxima over the same cells. amrex::ReduceMax's FabArray overloads take at most
136 // three FabArrays and this needs six, so drive ReduceOps directly. Unlike
137 // amrex::ReduceMax, ReduceOps::eval has no host fallback in a GPU build, so no
138 // Gpu::LaunchSafeGuard is wanted here.
141 using ReduceTuple = typename decltype(reduce_data)::Type;
142
143#ifdef _OPENMP
144#pragma omp parallel if (Gpu::notInLaunchRegion())
145#endif
146 for ( MFIter mfi(ccvel, TilingIfNotGPU()); mfi.isValid(); ++mfi )
147 {
148 // ccvel carries no ghost cells, so this is exactly its valid region -- the same
149 // cells the advective estimate has always visited.
150 const Box& bx = mfi.tilebox();
151
152 Array4<Real const> const& u = ccvel.const_array(mfi);
153 Array4<Real const> const& Hz = vec_Hz[level]->const_array(mfi);
154
155 // h, pm, pn and mskr live on the z-slab BoxArray built box-for-box from
156 // grids[level] with the same DistributionMapping, so they index off this same
157 // MFIter and are read at k = 0.
158 Array4<Real const> const& h = vec_h[level]->const_array(mfi);
159 Array4<Real const> const& pm = vec_pm[level]->const_array(mfi);
160 Array4<Real const> const& pn = vec_pn[level]->const_array(mfi);
161 Array4<Real const> const& mskr = vec_mskr[level]->const_array(mfi);
162
164 [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
165 {
166 // Land contributes nothing: h there is a fill depth, not a real one.
167 if (mskr(i,j,0) <= Real(0.5)) { return {zero, zero}; }
168
169 Real inv_adv = amrex::max(amrex::Math::abs(u(i,j,k,0)) * pm(i,j,0),
170 amrex::Math::abs(u(i,j,k,1)) * pn(i,j,0));
171
172 // Hz is the true thickness of this terrain-following layer; the geometry's
173 // uniform dz is the thickness of a sigma level, not of a cell.
174 if (Hz(i,j,k) > zero) {
175 inv_adv = amrex::max(inv_adv, amrex::Math::abs(u(i,j,k,2)) / Hz(i,j,k));
176 }
177
178 // Component 0 of vec_h is the bathymetry, positive down. Component 1 is not a
179 // second copy of it -- stretch_transform overwrites it with the bottom z_w.
180 const Real c = std::sqrt(grav * amrex::Math::abs(h(i,j,0,0)));
181 const Real inv_bt = c * std::sqrt(pm(i,j,0)*pm(i,j,0) + pn(i,j,0)*pn(i,j,0));
182
183 return {inv_adv, inv_bt};
184 });
185 } // mfi
186
188
189 // A rank owning no boxes never calls eval and leaves its local maxima at
190 // std::numeric_limits<Real>::lowest(); the MPI max repairs that, and every test below
191 // is against the reduced values, which are identical on every rank.
192 Real inv[2] = { amrex::get<0>(host_tuple), amrex::get<1>(host_tuple) };
193 ParallelDescriptor::ReduceRealMax(inv, 2);
194 const Real inv_adv = inv[0];
195 const Real inv_bt = inv[1];
196
197 // Guard both divisions rather than letting them produce inf: inv_adv is zero for any
198 // quiescent start, and inv_bt is zero for a level that is entirely land.
199 const Real estdt_adv = (inv_adv > zero) ? cfl / inv_adv : bogus_large_value;
200 const Real estdt_bt = (inv_bt > zero) ? cfl * Real(ndtfast) / inv_bt : bogus_large_value;
201
202 const Real estdt_lowM = amrex::min(estdt_adv, estdt_bt);
203
204 if (verbose) {
205 amrex::Print() << "Using cfl = " << cfl << std::endl;
206 if (inv_adv > zero) {
207 amrex::Print() << " advective limit at level " << level << ": " << estdt_adv << std::endl;
208 } else {
209 amrex::Print() << " advective limit at level " << level << ": none (velocity is zero)" << std::endl;
210 }
211 if (inv_bt > zero) {
212 amrex::Print() << " gravity wave limit at level " << level << ": " << estdt_bt
213 << " (ndtfast = " << ndtfast << ")" << std::endl;
214 } else {
215 amrex::Print() << " gravity wave limit at level " << level << ": none (no wet cell has a depth)" << std::endl;
216 }
217 if (fixed_dt > zero) {
219 amrex::Print() << "Slow dt at level " << level << " would be: " << estdt_lowM << std::endl;
220 } else {
221 amrex::Print() << "Slow dt at level " << level << " would be undefined " << std::endl;
222 }
223 amrex::Print() << "Fixed dt at level " << level << " is: " << fixed_dt << std::endl;
224 } else {
225 amrex::Print() << "Slow dt at level " << level << ": " << estdt_lowM << std::endl;
226 }
227 }
228
229 if (fixed_dt > zero) {
230 return fixed_dt;
231 }
232
233 // Water at rest still carries gravity waves, so estdt_bt is finite for any level with
234 // one wet cell of nonzero depth. Reaching here means there is no such cell; returning
235 // the sentinel would let ComputeDt clamp dt to stop_time - t_new[0] and run the whole
236 // simulation in a single step.
238 amrex::Abort("REMORA::estTimeStep: cannot estimate a timestep at level " +
239 std::to_string(level) + ". No wet cell (mskr > 0.5) has a nonzero "
240 "depth, so neither the advective nor the gravity wave limit is "
241 "defined. Check the bathymetry and the land mask, or set "
242 "remora.fixed_dt");
243 }
244
245 return estdt_lowM;
246}
constexpr amrex::Real bogus_large_value
constexpr amrex::Real zero
mf_h setVal(geomdata.ProbHi(2))
int nfast
Number of fast steps to take.
Definition REMORA.H:1828
double stop_time
Whether max_step was set in the inputs; the default above is not a distinguishable sentinel.
Definition REMORA.H:1774
static amrex::Real fixed_dt
User specified fixed baroclinic time step.
Definition REMORA.H:1824
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_h
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:413
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pm
horizontal scaling factor: 1 / dx (2D)
Definition REMORA.H:603
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:584
int do_substep
Whether to substep fine levels in time.
Definition REMORA.H:1831
amrex::Vector< amrex::MultiFab * > zvel_new
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:399
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hz
Width of cells in the vertical (z-) direction (3D, Hz in ROMS)
Definition REMORA.H:424
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:397
amrex::Vector< amrex::MultiFab * > xvel_new
multilevel data container for current step's x velocities (u in ROMS)
Definition REMORA.H:395
amrex::Vector< int > nsubsteps
How many substeps on each level?
Definition REMORA.H:1691
void ComputeDt()
a wrapper for estTimeStep()
static amrex::Real change_max
Fraction maximum change in subsequent time steps.
Definition REMORA.H:1822
amrex::Vector< amrex::Real > t_new
new time at each level, in seconds since start_time
Definition REMORA.H:1700
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:1949
static int ndtfast
User specified, number of barotropic steps per baroclinic step.
Definition REMORA.H:1826
amrex::Real estTimeStep(int lev) const
compute dt from CFL considerations
void print_timestep_hierarchy() const
report the per-level slow/fast timestep hierarchy; call after ComputeDt
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:605
static amrex::Real cfl
CFL condition.
Definition REMORA.H:1820
amrex::Real elapsed_time(double time) const noexcept
Elapsed time since start_time of a time on the model clock.
Definition REMORA.H:2176
static int verbose
Verbosity level of output.
Definition REMORA.H:1985
amrex::Vector< amrex::Real > dt
time step at each level
Definition REMORA.H:1704