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
5using namespace amrex;
6
7void
9{
11
12 for (int lev = 0; lev <= finest_level; ++lev)
13 {
15 }
16
17 ParallelDescriptor::ReduceRealMin(&dt_tmp[0], dt_tmp.size());
18
19 Real dt_0 = dt_tmp[0];
20 int n_factor = 1;
21 for (int lev = 0; lev <= finest_level; ++lev) {
22 dt_tmp[lev] = amrex::min(dt_tmp[lev], change_max*dt[lev]);
24 dt_0 = amrex::min(dt_0, n_factor*dt_tmp[lev]);
25 }
26
27 // dt_0 is identical on every rank here, so this aborts collectively. The
28 // bogus_large_value test is the load-bearing one: the sentinel used to survive to
29 // this point intact whenever stop_time was left at its default of Real::max(), and to
30 // be laundered into stop_time - t_new[0] -- a single step covering the entire run --
31 // whenever stop_time was set. The change_max cap above cannot catch either case,
32 // since dt[] is itself seeded to bogus_large_value.
33 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(dt_0 > zero && std::isfinite(dt_0) &&
35 "REMORA::ComputeDt: computed a non-positive, "
36 "non-finite, or unusably large level-0 dt");
37
38 // Limit dt's by the value of stop_time.
39 const Real eps = Real(1.e-3)*dt_0;
40 if (t_new[0] + dt_0 > stop_time - eps) {
41 dt_0 = stop_time - t_new[0];
42 }
43
44 dt[0] = dt_0;
45 for (int lev = 1; lev <= finest_level; ++lev) {
46 dt[lev] = dt[lev-1] / nsubsteps[lev];
47 }
48}
49
50/**
51 * Estimate the largest stable slow (baroclinic) timestep on a level.
52 *
53 * The estimate is the smaller of an advective limit and an external gravity wave limit,
54 * each a maximum over the wet cells of the level:
55 *
56 * dt_adv = cfl / max( |u|*pm, |v|*pn, |w|/Hz )
57 * dt_grav = cfl * ndtfast / max( sqrt(g*|h|) * sqrt(pm^2 + pn^2) )
58 *
59 * The second is the Courant quantity ROMS forms as Cg_max in metrics.F. It is what keeps
60 * the estimate finite for a run started from rest, where every velocity is zero and the
61 * advective limit alone is undefined.
62 *
63 * pm and pn are the per-cell 1/dx and 1/dy metric terms rather than the geometry's
64 * uniform cell size, so stretched and curvilinear grids give the right answer.
65 *
66 * @param[in] level level of refinement
67 */
68Real
70{
71 BL_PROFILE("REMORA::estTimeStep()");
72
73 // The barotropic mode is substepped ndtfast times per baroclinic step
74 // (REMORA_Advance.cpp), so the slow step only has to resolve the external gravity
75 // wave to within that ratio. ReadParameters guarantees ndtfast is positive.
76
77 // g is a file-scope constexpr; hoist it into a local for the device lambda.
78 const Real grav = g;
79
80 MultiFab ccvel(grids[level],dmap[level],3,0);
81
84
85 // Two maxima over the same cells. amrex::ReduceMax's FabArray overloads take at most
86 // three FabArrays and this needs six, so drive ReduceOps directly. Unlike
87 // amrex::ReduceMax, ReduceOps::eval has no host fallback in a GPU build, so no
88 // Gpu::LaunchSafeGuard is wanted here.
91 using ReduceTuple = typename decltype(reduce_data)::Type;
92
93#ifdef _OPENMP
94#pragma omp parallel if (Gpu::notInLaunchRegion())
95#endif
96 for ( MFIter mfi(ccvel, TilingIfNotGPU()); mfi.isValid(); ++mfi )
97 {
98 // ccvel carries no ghost cells, so this is exactly its valid region -- the same
99 // cells the advective estimate has always visited.
100 const Box& bx = mfi.tilebox();
101
102 Array4<Real const> const& u = ccvel.const_array(mfi);
103 Array4<Real const> const& Hz = vec_Hz[level]->const_array(mfi);
104
105 // h, pm, pn and mskr live on the z-slab BoxArray built box-for-box from
106 // grids[level] with the same DistributionMapping, so they index off this same
107 // MFIter and are read at k = 0.
108 Array4<Real const> const& h = vec_h[level]->const_array(mfi);
109 Array4<Real const> const& pm = vec_pm[level]->const_array(mfi);
110 Array4<Real const> const& pn = vec_pn[level]->const_array(mfi);
111 Array4<Real const> const& mskr = vec_mskr[level]->const_array(mfi);
112
114 [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
115 {
116 // Land contributes nothing: h there is a fill depth, not a real one.
117 if (mskr(i,j,0) <= Real(0.5)) { return {zero, zero}; }
118
119 Real inv_adv = amrex::max(amrex::Math::abs(u(i,j,k,0)) * pm(i,j,0),
120 amrex::Math::abs(u(i,j,k,1)) * pn(i,j,0));
121
122 // Hz is the true thickness of this terrain-following layer; the geometry's
123 // uniform dz is the thickness of a sigma level, not of a cell.
124 if (Hz(i,j,k) > zero) {
125 inv_adv = amrex::max(inv_adv, amrex::Math::abs(u(i,j,k,2)) / Hz(i,j,k));
126 }
127
128 // Component 0 of vec_h is the bathymetry, positive down. Component 1 is not a
129 // second copy of it -- stretch_transform overwrites it with the bottom z_w.
130 const Real c = std::sqrt(grav * amrex::Math::abs(h(i,j,0,0)));
131 const Real inv_bt = c * std::sqrt(pm(i,j,0)*pm(i,j,0) + pn(i,j,0)*pn(i,j,0));
132
133 return {inv_adv, inv_bt};
134 });
135 } // mfi
136
138
139 // A rank owning no boxes never calls eval and leaves its local maxima at
140 // std::numeric_limits<Real>::lowest(); the MPI max repairs that, and every test below
141 // is against the reduced values, which are identical on every rank.
142 Real inv[2] = { amrex::get<0>(host_tuple), amrex::get<1>(host_tuple) };
143 ParallelDescriptor::ReduceRealMax(inv, 2);
144 const Real inv_adv = inv[0];
145 const Real inv_bt = inv[1];
146
147 // Guard both divisions rather than letting them produce inf: inv_adv is zero for any
148 // quiescent start, and inv_bt is zero for a level that is entirely land.
149 const Real estdt_adv = (inv_adv > zero) ? cfl / inv_adv : bogus_large_value;
150 const Real estdt_bt = (inv_bt > zero) ? cfl * Real(ndtfast) / inv_bt : bogus_large_value;
151
152 const Real estdt_lowM = amrex::min(estdt_adv, estdt_bt);
153
154 if (verbose) {
155 amrex::Print() << "Using cfl = " << cfl << std::endl;
156 if (inv_adv > zero) {
157 amrex::Print() << " advective limit at level " << level << ": " << estdt_adv << std::endl;
158 } else {
159 amrex::Print() << " advective limit at level " << level << ": none (velocity is zero)" << std::endl;
160 }
161 if (inv_bt > zero) {
162 amrex::Print() << " gravity wave limit at level " << level << ": " << estdt_bt
163 << " (ndtfast = " << ndtfast << ")" << std::endl;
164 } else {
165 amrex::Print() << " gravity wave limit at level " << level << ": none (no wet cell has a depth)" << std::endl;
166 }
167 if (fixed_dt > zero) {
169 amrex::Print() << "Slow dt at level " << level << " would be: " << estdt_lowM << std::endl;
170 } else {
171 amrex::Print() << "Slow dt at level " << level << " would be undefined " << std::endl;
172 }
173 amrex::Print() << "Fixed dt at level " << level << " is: " << fixed_dt << std::endl;
174 } else {
175 amrex::Print() << "Slow dt at level " << level << ": " << estdt_lowM << std::endl;
176 }
177 }
178
179 if (fixed_dt > zero) {
180 return fixed_dt;
181 }
182
183 // Water at rest still carries gravity waves, so estdt_bt is finite for any level with
184 // one wet cell of nonzero depth. Reaching here means there is no such cell; returning
185 // the sentinel would let ComputeDt clamp dt to stop_time - t_new[0] and run the whole
186 // simulation in a single step.
188 amrex::Abort("REMORA::estTimeStep: cannot estimate a timestep at level " +
189 std::to_string(level) + ". No wet cell (mskr > 0.5) has a nonzero "
190 "depth, so neither the advective nor the gravity wave limit is "
191 "defined. Check the bathymetry and the land mask, or set "
192 "remora.fixed_dt");
193 }
194
195 return estdt_lowM;
196}
constexpr amrex::Real bogus_large_value
constexpr amrex::Real zero
constexpr amrex::Real g
mf_h setVal(geomdata.ProbHi(2))
static amrex::Real fixed_dt
User specified fixed baroclinic time step.
Definition REMORA.H:1671
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:406
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pm
horizontal scaling factor: 1 / dx (2D)
Definition REMORA.H:568
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:557
amrex::Real stop_time
Whether max_step was set in the inputs; the default above is not a distinguishable sentinel.
Definition REMORA.H:1624
amrex::Vector< amrex::MultiFab * > zvel_new
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:392
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hz
Width of cells in the vertical (z-) direction (3D, Hz in ROMS)
Definition REMORA.H:412
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:390
amrex::Vector< amrex::MultiFab * > xvel_new
multilevel data container for current step's x velocities (u in ROMS)
Definition REMORA.H:388
amrex::Vector< int > nsubsteps
How many substeps on each level?
Definition REMORA.H:1554
void ComputeDt()
a wrapper for estTimeStep()
static amrex::Real change_max
Fraction maximum change in subsequent time steps.
Definition REMORA.H:1669
amrex::Vector< amrex::Real > t_new
new time at each level
Definition REMORA.H:1556
static int ndtfast
User specified, number of barotropic steps per baroclinic step.
Definition REMORA.H:1673
amrex::Real estTimeStep(int lev) const
compute dt from CFL considerations
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:570
static amrex::Real cfl
CFL condition.
Definition REMORA.H:1667
static int verbose
Verbosity level of output.
Definition REMORA.H:1753
amrex::Vector< amrex::Real > dt
time step at each level
Definition REMORA.H:1560