PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
MPDATA2.cc
Go to the documentation of this file.
1 /* Copyright (C) 2020, 2022, 2023 PISM Authors
2  *
3  * This file is part of PISM.
4  *
5  * PISM is free software; you can redistribute it and/or modify it under the
6  * terms of the GNU General Public License as published by the Free Software
7  * Foundation; either version 3 of the License, or (at your option) any later
8  * version.
9  *
10  * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with PISM; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <cmath>
21 
22 #include "pism/geometry/MPDATA2.hh"
23 
24 #include "pism/util/array/CellType.hh"
25 
26 /*! References:
27  *
28  * [Smolarkiewicz1983] P. K. Smolarkiewicz, “A Simple Positive Definite Advection Scheme
29  * with Small Implicit Diffusion,” Monthly Weather Review, vol. 111, Art. no. 3, Mar.
30  * 1983.
31  *
32  * [Smolarkiewicz1990] P. K. Smolarkiewicz and W. W. Grabowski, “The multidimensional
33  * positive definite advection transport algorithm: nonoscillatory option,” Journal of
34  * Computational Physics, vol. 86, Art. no. 2, Feb. 1990.
35  */
36 
37 namespace pism {
38 
39 namespace fct {
40 // positive part
41 static double pp(double x) {
42  return std::max(x, 0.0);
43 }
44 
45 // negative part
46 static double np(double x) {
47  return std::min(x, 0.0);
48 }
49 
50 static double maximum(const stencils::Star<double> &psi) {
51  using std::max;
52  return max(max(max(max(psi.c, psi.n), psi.e), psi.s), psi.w);
53 }
54 
55 static double minimum(const stencils::Star<double> &psi) {
56  using std::min;
57  return min(min(min(min(psi.c, psi.n), psi.e), psi.s), psi.w);
58 }
59 
60 static double flux_in(const stencils::Star<double> &u,
61  const stencils::Star<double> &psi,
62  double dx, double dy, double dt) {
63  return dt * ((pp(u.w) * psi.w - np(u.e) * psi.e) / dx +
64  (pp(u.s) * psi.s - np(u.n) * psi.n) / dy);
65 }
66 
67 static double flux_out(const stencils::Star<double> &u,
68  const stencils::Star<double> &psi,
69  double dx, double dy, double dt) {
70  return dt * ((pp(u.e) * psi.c - np(u.w) * psi.c) / dx +
71  (pp(u.n) * psi.c - np(u.s) * psi.c) / dy);
72 }
73 
74 static double beta_up(const stencils::Star<double> &u,
75  const stencils::Star<double> &psi,
76  const stencils::Star<double> &psi_old,
77  double dx, double dy, double dt) {
78  const double eps = 1e-15;
79  double psi_max = std::max(maximum(psi), maximum(psi_old));
80 
81  return (psi_max - psi.c) / (flux_in(u, psi, dx, dy, dt) + eps);
82 }
83 
84 static double beta_down(const stencils::Star<double> &u,
85  const stencils::Star<double> &psi,
86  const stencils::Star<double> &psi_old,
87  double dx, double dy, double dt) {
88  const double eps = 1e-15;
89  double psi_min = std::min(minimum(psi), minimum(psi_old));
90 
91  return (psi.c - psi_min) / (flux_out(u, psi, dx, dy, dt) + eps);
92 }
93 
94 static void limit(double dt,
95  const array::Scalar2 &x_old,
96  const array::Scalar2 &x,
97  const array::Staggered1 &velocity,
98  array::Staggered &result) {
99 
100  auto grid = x_old.grid();
101 
102  double
103  dx = grid->dx(),
104  dy = grid->dy();
105 
106  array::AccessScope list{&x_old, &x, &velocity, &result};
107 
108  for (auto p = grid->points(); p; p.next()) {
109  const int i = p.i(), j = p.j();
110 
111  double beta_u{0.0};
112  double beta_d{0.0};
113  {
115  X = x.star(i, j),
116  X_old = x_old.star(i, j),
117  u = velocity.star(i, j);
118 
119  beta_u = beta_up(u, X, X_old, dx, dy, dt);
120  beta_d = beta_down(u, X, X_old, dx, dy, dt);
121  }
122 
123  // east
124  {
126  X = x.star(i + 1, j),
127  X_old = x_old.star(i + 1, j),
128  u = velocity.star(i + 1, j);
129 
130  double C{1.0};
131  // note: the "west" velocity of the east neighbor is the "east" velocity of the
132  // current cell
133  if (u.w > 0.0) {
134  C = std::min(1.0, std::min(beta_d, beta_up(u, X, X_old, dx, dy, dt)));
135  } else {
136  C = std::min(1.0, std::min(beta_u, beta_down(u, X, X_old, dx, dy, dt)));
137  }
138  result(i, j, 0) = u.w * C;
139  }
140 
141  // north
142  {
144  X = x.star(i, j + 1),
145  X_old = x_old.star(i, j + 1),
146  u = velocity.star(i, j + 1);
147 
148  double C{1.0};
149  // note: the "south" velocity of the north neighbor is the "north" velocity of the
150  // current cell
151  if (u.s > 0.0) {
152  C = std::min(1.0, std::min(beta_d, beta_up(u, X, X_old, dx, dy, dt)));
153  } else {
154  C = std::min(1.0, std::min(beta_u, beta_down(u, X, X_old, dx, dy, dt)));
155  }
156  result(i, j, 1) = u.s * C;
157  }
158  } // end of the loop over grid points
159 }
160 
161 } // end of namespace fct
162 
163 const array::Scalar& MPDATA2::x() const {
164  return m_x;
165 }
166 
167 MPDATA2::MPDATA2(std::shared_ptr<const Grid> grid, int N)
168  : m_v(grid, "velocity_staggered"),
169  m_v_old(grid, "tmp"),
170  m_v_ghosted(grid, "velocity_ghosted"),
171  m_x_previous(grid, "previous_state"),
172  m_x_input(grid, "input_field"),
173  m_x(grid, "new_state"),
174  m_N(N) {
175  // empty
176 }
177 
178 /*!
179  * Compute staggered grid (cell interface) velocities given regular grid velocities and
180  * the domain extent (cell type).
181  */
182 static void compute_interface_velocity(const array::CellType &cell_type,
183  const array::Vector &velocity,
184  array::Staggered &result) {
185 
186  auto grid = cell_type.grid();
187 
188  array::AccessScope scope{&cell_type, &velocity, &result};
189 
190  for (auto p = grid->points(); p; p.next()) {
191  const int i = p.i(), j = p.j();
192 
193  Vector2d
194  V = velocity(i, j),
195  V_e = velocity(i + 1, j),
196  V_n = velocity(i, j + 1);
197 
198  int
199  W = static_cast<int>(cell_type.icy(i, j)),
200  W_n = static_cast<int>(cell_type.icy(i, j + 1)),
201  W_e = static_cast<int>(cell_type.icy(i + 1, j));
202 
203  result(i, j, 0) = (W * V.u + W_e * V_e.u) / std::max(W + W_e, 1);
204  result(i, j, 1) = (W * V.v + W_n * V_n.v) / std::max(W + W_n, 1);
205  }
206 }
207 
208 /*!
209  * Compute the "anti-diffusive" flux correction in the form of velocities at cell
210  * interfaces.
211  */
212 static void compute_corrective_velocity(double dt,
213  const array::Staggered &velocity,
214  const array::Scalar2 &x,
215  array::Staggered &result) {
216  using std::fabs;
217 
218  auto grid = x.grid();
219 
220  const double
221  eps = 1e-15,
222  dx = grid->dx(),
223  dy = grid->dy();
224 
225  array::AccessScope scope{&velocity, &x, &result};
226 
227  for (auto p = grid->points(); p; p.next()) {
228  const int i = p.i(), j = p.j();
229 
230  auto X = x.box(i, j);
231 
232  // eastern interface
233  {
234  double
235  u = velocity(i, j, 0),
236  v_bar = 0.25 * (velocity(i + 1, j, 1) + velocity(i, j, 1) +
237  velocity(i, j - 1, 1) + velocity(i + 1, j - 1, 1));
238  // double X_e2 = x(i + 2, j);
239 
240  // equation (13) in Smolarkiewicz1983 for the corrective velocity in the X direction
241  double U = ((fabs(u) * dx - dt * u * u) * (X.e - X.c) / ((X.e + X.c + eps) * dx)
242  - 0.5 * dt * u * v_bar
243  * (X.ne - X.se + X.n - X.s) / ((X.ne + X.se + X.n + X.s + eps) * dy));
244 
245  result(i, j, 0) = U;
246  }
247 
248  // northern interface
249  {
250  double
251  v = velocity(i, j, 1),
252  u_bar = 0.25 * (velocity(i, j + 1, 0) + velocity(i, j, 0) +
253  velocity(i - 1, j, 0) + velocity(i - 1, j + 1, 0));
254  // double X_n2 = x(i, j + 2);
255 
256  // equation (13) in Smolarkiewicz1983 for the corrective velocity in the Y direction
257  double V = ((fabs(v) * dy - dt * v * v) * (X.n - X.c) / ((X.n + X.c + eps) * dy)
258  - 0.5 * dt * v * u_bar *
259  (X.ne - X.nw + X.e - X.w) / ((X.ne + X.nw + X.e + X.w + eps) * dx));
260 
261  result(i, j, 1) = V;
262  }
263  }
264 }
265 
266 /*!
267  * Upwinded flux
268  */
269 static double upwind(double x, double x_n, double u) {
270  return u * (u >= 0.0 ? x : x_n);
271 }
272 
273 /*!
274  * Perform an explicit step using first order upwinding.
275  *
276  * @param[in] dt time step length
277  * @param[in] velocity cell interface velocities
278  * @param[in] x_old current state
279  * @param[out] x new state
280  */
281 static void step(double dt,
282  const array::Staggered1 &velocity,
283  const array::Scalar1 &x_old,
284  array::Scalar &x) {
285 
286  auto grid = x.grid();
287  double
288  dx = grid->dx(),
289  dy = grid->dy();
290 
291  array::AccessScope scope{&velocity, &x_old, &x};
292 
293  for (auto p = grid->points(); p; p.next()) {
294  const int i = p.i(), j = p.j();
295 
296  auto u = velocity.star(i, j);
297  auto f = x_old.star(i, j);
298 
299  double
300  Q_e = upwind(f.c, f.e, u.e),
301  Q_w = upwind(f.w, f.c, u.w),
302  Q_n = upwind(f.c, f.n, u.n),
303  Q_s = upwind(f.s, f.c, u.s);
304 
305  x(i, j) = x_old(i, j) - dt * ((Q_e - Q_w) / dx + (Q_n - Q_s) / dy);
306  }
307 }
308 
309 void MPDATA2::update(double dt,
310  const array::CellType &cell_type,
311  const array::Scalar &x,
312  const array::Vector &velocity,
313  bool nonoscillatory) {
314 
315  // make a ghosted copy (needed to limit fluxes)
317 
318  for (int k = 0; k < m_N; ++k) {
319  if (k == 0) {
321  m_v_ghosted.copy_from(velocity);
323  } else {
327  if (nonoscillatory) {
330  }
331  }
332 
333  m_v.update_ghosts();
334  step(dt, m_v, m_x_previous, m_x);
335  }
336 }
337 
338 } // end of namespace pism
array::Scalar2 m_x_input
Definition: MPDATA2.hh:55
array::Vector1 m_v_ghosted
Definition: MPDATA2.hh:51
array::Staggered1 m_v_old
Definition: MPDATA2.hh:49
array::Staggered1 m_v
Definition: MPDATA2.hh:49
const array::Scalar & x() const
Definition: MPDATA2.cc:163
array::Scalar m_x
Definition: MPDATA2.hh:58
MPDATA2(std::shared_ptr< const Grid > grid, int N)
Definition: MPDATA2.cc:167
array::Scalar2 m_x_previous
Definition: MPDATA2.hh:54
void update(double dt, const array::CellType &cell_type, const array::Scalar &x, const array::Vector &velocity, bool nonoscillatory=false)
Definition: MPDATA2.cc:309
This class represents a 2D vector field (such as ice velocity) at a certain grid point.
Definition: Vector2d.hh:29
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition: Array.hh:65
stencils::Box< T > box(int i, int j) const
Definition: Array2D.hh:93
void copy_from(const Array2D< T > &source)
Definition: Array2D.hh:73
stencils::Star< T > star(int i, int j) const
Definition: Array2D.hh:79
std::shared_ptr< const Grid > grid() const
Definition: Array.cc:132
void update_ghosts()
Updates ghost points.
Definition: Array.cc:693
bool icy(int i, int j) const
Definition: CellType.hh:42
"Cell type" mask. Adds convenience methods to array::Scalar.
Definition: CellType.hh:30
stencils::Star< double > star(int i, int j) const
Returns the values at interfaces of the cell i,j using the staggered grid.
Definition: Staggered.hh:73
void copy_from(const array::Staggered &input)
Definition: Staggered.cc:42
A class for storing and accessing internal staggered-grid 2D fields. Uses dof=2 storage....
Definition: Staggered.hh:35
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:165
double min(const array::Scalar &input)
Finds minimum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:193
static double flux_out(const stencils::Star< double > &u, const stencils::Star< double > &psi, double dx, double dy, double dt)
Definition: MPDATA2.cc:67
static double beta_up(const stencils::Star< double > &u, const stencils::Star< double > &psi, const stencils::Star< double > &psi_old, double dx, double dy, double dt)
Definition: MPDATA2.cc:74
static double maximum(const stencils::Star< double > &psi)
Definition: MPDATA2.cc:50
static double minimum(const stencils::Star< double > &psi)
Definition: MPDATA2.cc:55
static double beta_down(const stencils::Star< double > &u, const stencils::Star< double > &psi, const stencils::Star< double > &psi_old, double dx, double dy, double dt)
Definition: MPDATA2.cc:84
static double np(double x)
Definition: MPDATA2.cc:46
static void limit(double dt, const array::Scalar2 &x_old, const array::Scalar2 &x, const array::Staggered1 &velocity, array::Staggered &result)
Definition: MPDATA2.cc:94
static double pp(double x)
Definition: MPDATA2.cc:41
static double flux_in(const stencils::Star< double > &u, const stencils::Star< double > &psi, double dx, double dy, double dt)
Definition: MPDATA2.cc:60
static void step(double dt, const array::Staggered1 &velocity, const array::Scalar1 &x_old, array::Scalar &x)
Definition: MPDATA2.cc:281
static double upwind(double x, double x_n, double u)
Definition: MPDATA2.cc:269
static const double k
Definition: exactTestP.cc:42
static void compute_corrective_velocity(double dt, const array::Staggered &velocity, const array::Scalar2 &x, array::Staggered &result)
Definition: MPDATA2.cc:212
static void compute_interface_velocity(const array::CellType &cell_type, const array::Vector &velocity, array::Staggered &result)
Definition: MPDATA2.cc:182
const int C[]
Definition: ssafd_code.cc:44
Star stencil points (in the map-plane).
Definition: stencils.hh:30