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: if no estimate replaces the
78 // sentinel, it reaches this point intact, and the stop_time clamp below would
79 // turn it into a single step covering the entire run. The change_max cap above
80 // cannot catch it, since dt[] is itself seeded to bogus_large_value.
81 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(dt_0 > zero && std::isfinite(dt_0) &&
83 "REMORA::ComputeDt: computed a non-positive, "
84 "non-finite, or unusably large level-0 dt");
85
86 // Limit dt's by the value of stop_time.
88 const Real eps = Real(1.e-3)*dt_0;
89 if (t_new[0] + dt_0 > stop_elapsed - eps) {
90 dt_0 = stop_elapsed - t_new[0];
91 }
92
93 dt[0] = dt_0;
94 for (int lev = 1; lev <= finest_level; ++lev) {
95 dt[lev] = dt[lev-1] / nsubsteps[lev];
96 }
97}
98
99/**
100 * Estimate the largest stable slow (baroclinic) timestep on a level.
101 *
102 * The estimate is the smaller of an advective limit and an external gravity wave limit,
103 * each a maximum over the wet cells of the level:
104 *
105 * dt_adv = cfl / max( |u|*pm, |v|*pn, |w|/Hz )
106 * dt_grav = cfl * ndtfast / max( sqrt(g*|h|) * sqrt(pm^2 + pn^2) )
107 *
108 * The second is the Courant quantity ROMS forms as Cg_max in metrics.F. It is what keeps
109 * the estimate finite for a run started from rest, where every velocity is zero and the
110 * advective limit alone is undefined.
111 *
112 * pm and pn are the per-cell 1/dx and 1/dy metric terms rather than the geometry's
113 * uniform cell size, so stretched and curvilinear grids give the right answer.
114 *
115 * @param[in] level level of refinement
116 */
117Real
119{
120 BL_PROFILE("REMORA::estTimeStep()");
121
122 // The barotropic mode is substepped ndtfast times per baroclinic step
123 // (REMORA_Advance.cpp), so the slow step only has to resolve the external gravity
124 // wave to within that ratio. ReadParameters guarantees ndtfast is positive.
125
126 // g is a file-scope constexpr; hoist it into a local for the device lambda.
127 const Real grav = solverChoice.g;
128
129 MultiFab ccvel(grids[level],dmap[level],3,0);
130
133
134 // Two maxima over the same cells. amrex::ReduceMax's FabArray overloads take at most
135 // three FabArrays and this needs six, so drive ReduceOps directly. Unlike
136 // amrex::ReduceMax, ReduceOps::eval has no host fallback in a GPU build, so no
137 // Gpu::LaunchSafeGuard is wanted here.
140 using ReduceTuple = typename decltype(reduce_data)::Type;
141
142#ifdef _OPENMP
143#pragma omp parallel if (Gpu::notInLaunchRegion())
144#endif
145 for ( MFIter mfi(ccvel, TilingIfNotGPU()); mfi.isValid(); ++mfi )
146 {
147 // ccvel carries no ghost cells, so this is exactly its valid region -- the same
148 // cells the advective estimate has always visited.
149 const Box& bx = mfi.tilebox();
150
151 Array4<Real const> const& u = ccvel.const_array(mfi);
152 Array4<Real const> const& Hz = vec_Hz[level]->const_array(mfi);
153
154 // h, pm, pn and mskr live on the z-slab BoxArray built box-for-box from
155 // grids[level] with the same DistributionMapping, so they index off this same
156 // MFIter and are read at k = 0.
157 Array4<Real const> const& h = vec_h[level]->const_array(mfi);
158 Array4<Real const> const& pm = vec_pm[level]->const_array(mfi);
159 Array4<Real const> const& pn = vec_pn[level]->const_array(mfi);
160 Array4<Real const> const& mskr = vec_mskr[level]->const_array(mfi);
161
163 [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
164 {
165 // Land contributes nothing: h there is a fill depth, not a real one.
166 if (mskr(i,j,0) <= Real(0.5)) { return {zero, zero}; }
167
168 Real inv_adv = amrex::max(amrex::Math::abs(u(i,j,k,0)) * pm(i,j,0),
169 amrex::Math::abs(u(i,j,k,1)) * pn(i,j,0));
170
171 // Hz is the true thickness of this terrain-following layer; the geometry's
172 // uniform dz is the thickness of a sigma level, not of a cell.
173 if (Hz(i,j,k) > zero) {
174 inv_adv = amrex::max(inv_adv, amrex::Math::abs(u(i,j,k,2)) / Hz(i,j,k));
175 }
176
177 // Component 0 of vec_h is the bathymetry, positive down. Component 1 is not a
178 // second copy of it -- stretch_transform overwrites it with the bottom z_w.
179 const Real c = std::sqrt(grav * amrex::Math::abs(h(i,j,0,0)));
180 const Real inv_bt = c * std::sqrt(pm(i,j,0)*pm(i,j,0) + pn(i,j,0)*pn(i,j,0));
181
182 return {inv_adv, inv_bt};
183 });
184 } // mfi
185
187
188 // A rank owning no boxes never calls eval and leaves its local maxima at
189 // std::numeric_limits<Real>::lowest(); the MPI max repairs that, and every test below
190 // is against the reduced values, which are identical on every rank.
191 Real inv[2] = { amrex::get<0>(host_tuple), amrex::get<1>(host_tuple) };
192 ParallelDescriptor::ReduceRealMax(inv, 2);
193 const Real inv_adv = inv[0];
194 const Real inv_bt = inv[1];
195
196 // Guard both divisions rather than letting them produce inf: inv_adv is zero for any
197 // quiescent start, and inv_bt is zero for a level that is entirely land.
198 const Real estdt_adv = (inv_adv > zero) ? cfl / inv_adv : bogus_large_value;
199 const Real estdt_bt = (inv_bt > zero) ? cfl * Real(ndtfast) / inv_bt : bogus_large_value;
200
201 const Real estdt_lowM = amrex::min(estdt_adv, estdt_bt);
202
203 if (verbose) {
204 amrex::Print() << "Using cfl = " << cfl << std::endl;
205 if (inv_adv > zero) {
206 amrex::Print() << " advective limit at level " << level << ": " << estdt_adv << std::endl;
207 } else {
208 amrex::Print() << " advective limit at level " << level << ": none (velocity is zero)" << std::endl;
209 }
210 if (inv_bt > zero) {
211 amrex::Print() << " gravity wave limit at level " << level << ": " << estdt_bt
212 << " (ndtfast = " << ndtfast << ")" << std::endl;
213 } else {
214 amrex::Print() << " gravity wave limit at level " << level << ": none (no wet cell has a depth)" << std::endl;
215 }
216 if (fixed_dt > zero) {
218 amrex::Print() << "Slow dt at level " << level << " would be: " << estdt_lowM << std::endl;
219 } else {
220 amrex::Print() << "Slow dt at level " << level << " would be undefined " << std::endl;
221 }
222 amrex::Print() << "Fixed dt at level " << level << " is: " << fixed_dt << std::endl;
223 } else {
224 amrex::Print() << "Slow dt at level " << level << ": " << estdt_lowM << std::endl;
225 }
226 }
227
228 if (fixed_dt > zero) {
229 return fixed_dt;
230 }
231
232 // Water at rest still carries gravity waves, so estdt_bt is finite for any level with
233 // one wet cell of nonzero depth. Reaching here means there is no such cell; returning
234 // the sentinel would let ComputeDt clamp dt to stop_time - t_new[0] and run the whole
235 // simulation in a single step.
237 amrex::Abort("REMORA::estTimeStep: cannot estimate a timestep at level " +
238 std::to_string(level) + ". No wet cell (mskr > 0.5) has a nonzero "
239 "depth, so neither the advective nor the gravity wave limit is "
240 "defined. Check the bathymetry and the land mask, or set "
241 "remora.fixed_dt");
242 }
243
244 return estdt_lowM;
245}
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:1870
double stop_time
Time to stop, in seconds on the model clock (not since start_time)
Definition REMORA.H:1816
static amrex::Real fixed_dt
User specified fixed baroclinic time step.
Definition REMORA.H:1866
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:1873
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:1735
void ComputeDt()
a wrapper for estTimeStep()
static amrex::Real change_max
Fraction maximum change in subsequent time steps.
Definition REMORA.H:1864
amrex::Vector< amrex::Real > t_new
new time at each level, in seconds since start_time
Definition REMORA.H:1744
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:2017
static int ndtfast
User specified, number of barotropic steps per baroclinic step.
Definition REMORA.H:1868
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:1862
amrex::Real elapsed_time(double time) const noexcept
Elapsed time since start_time of a time on the model clock.
Definition REMORA.H:2216
static int verbose
Verbosity level of output.
Definition REMORA.H:2053
amrex::Vector< amrex::Real > dt
time step at each level
Definition REMORA.H:1748