Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
37namespace pism {
38
39namespace fct {
40// positive part
41static double pp(double x) {
42 return std::max(x, 0.0);
43}
44
45// negative part
46static double np(double x) {
47 return std::min(x, 0.0);
48}
49
50static 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
55static 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
60static 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
67static 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
74static 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
84static 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
94static 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
163const array::Scalar& MPDATA2::x() const {
164 return m_x;
165}
166
167MPDATA2::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 */
182static 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
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 */
212static 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 */
269static 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 */
281static 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
309void 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
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:64
void copy_from(const Array2D< T > &source)
Definition Array2D.hh:73
stencils::Star< T > star(int i, int j) const
Definition Array2D.hh:79
stencils::Box< T > box(int i, int j) const
Definition Array2D.hh:93
std::shared_ptr< const Grid > grid() const
Definition Array.cc:131
void update_ghosts()
Updates ghost points.
Definition Array.cc:615
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:75
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:37
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
Star stencil points (in the map-plane).
Definition stencils.hh:30