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
BedSmoother.cc
Go to the documentation of this file.
1// Copyright (C) 2010--2023 Ed Bueler and Constantine Khroulev
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#include <cassert>
20
21#include "pism/stressbalance/sia/BedSmoother.hh"
22#include "pism/util/Context.hh"
23#include "pism/util/Grid.hh"
24#include "pism/util/Logger.hh"
25#include "pism/util/array/CellType.hh"
26#include "pism/util/error_handling.hh"
27#include "pism/util/petscwrappers/Vec.hh"
28#include "pism/util/pism_utilities.hh"
29#include "pism/util/VariableMetadata.hh"
30
31namespace pism {
32namespace stressbalance {
33
34BedSmoother::BedSmoother(std::shared_ptr<const Grid> g)
35 : m_grid(g),
36 m_config(g->ctx()->config()),
37 m_topgsmooth(m_grid, "topgsmooth"),
38 m_maxtl(m_grid, "maxtl"),
39 m_C2(m_grid, "C2bedsmooth"),
40 m_C3(m_grid, "C3bedsmooth"),
41 m_C4(m_grid, "C3bedsmooth")
42{
43
44 const Logger &log = *m_grid->ctx()->log();
45
46 {
47 // allocate Vecs that live on all procs; all have to be as "wide" as any of
48 // their prospective uses
50 .long_name("smoothed bed elevation, in bed roughness parameterization")
51 .units("m");
53 .long_name("maximum elevation in local topography patch, in bed roughness parameterization")
54 .units("m");
55 m_C2.metadata(0)
56 .long_name("polynomial coeff of H^-2, in bed roughness parameterization")
57 .units("m^2");
58 m_C3.metadata(0)
59 .long_name("polynomial coeff of H^-3, in bed roughness parameterization")
60 .units("m^3");
61 m_C4.metadata(0)
62 .long_name("polynomial coeff of H^-4, in bed roughness parameterization")
63 .units("m^4");
64
65 // allocate Vecs that live on processor 0:
72 }
73
74 m_Glen_exponent = m_config->get_number("stress_balance.sia.Glen_exponent"); // choice is SIA; see #285
75 m_smoothing_range = m_config->get_number("stress_balance.sia.bed_smoother.range");
76
77 if (m_smoothing_range > 0.0) {
78 log.message(2,
79 "* Initializing bed smoother object with %.3f km half-width ...\n",
80 units::convert(m_grid->ctx()->unit_system(), m_smoothing_range, "m", "km"));
81 }
82
83 // Make sure that Nx and Ny are initialized. In most cases SIAFD::update() will call
84 // preprocess_bed() and set appropriate values, but in a zero-length (-y 0) run IceModel does not
85 // call SIAFD::update()... We may need to re-structure this class so that everything is
86 // initialized right after construction and users don't have to call preprocess_bed() manually.
87 m_Nx = -1;
88 m_Ny = -1;
89}
90
91
92/*!
93Input lambda gives physical half-width (in m) of square over which to do the
94average. Only square smoothing domains are allowed with this call, which is the
95default case.
96 */
98
99 if (m_smoothing_range <= 0.0) {
100 // smoothing completely inactive. we transfer the original bed topg,
101 // including ghosts, to public member topgsmooth ...
103 // and we tell theta() to return theta=1
104 m_Nx = -1;
105 m_Ny = -1;
106 return;
107 }
108
109 // determine Nx, Ny, which are always at least one if m_smoothing_range > 0
110 m_Nx = static_cast<int>(ceil(m_smoothing_range / m_grid->dx()));
111 m_Ny = static_cast<int>(ceil(m_smoothing_range / m_grid->dy()));
112 if (m_Nx < 1) {
113 m_Nx = 1;
114 }
115 if (m_Ny < 1) {
116 m_Ny = 1;
117 }
118
119 preprocess_bed(topg, m_Nx, m_Ny);
120}
121
123 return m_topgsmooth;
124}
125
126/*!
127Inputs Nx,Ny gives half-width in number of grid points, over which to do the
128average.
129 */
131 unsigned int Nx, unsigned int Ny) {
132
133 if ((Nx >= m_grid->Mx()) || (Ny >= m_grid->My())) {
134 throw RuntimeError(PISM_ERROR_LOCATION, "input Nx, Ny in bed smoother is too large because\n"
135 "domain of smoothing exceeds Grid domain");
136 }
137 m_Nx = Nx;
138 m_Ny = Ny;
139
140 topg.put_on_proc0(*m_topgp0);
142 // next call *does indeed* fill ghosts in topgsmooth
144
146 // following calls *do* fill the ghosts
151}
152
153
154//! Computes the smoothed bed by a simple average over a rectangle of grid points.
156
157 ParallelSection rank0(m_grid->com);
158 try {
159 if (m_grid->rank() == 0) {
160 const int Mx = (int)m_grid->Mx();
161 const int My = (int)m_grid->My();
162
164 b0(*m_topgp0, Mx, My),
165 bs(*m_topgsmoothp0, Mx, My);
166
167 for (int j=0; j < My; j++) {
168 for (int i=0; i < Mx; i++) {
169 // average only over those points which are in the grid; do
170 // not wrap periodically
171 double sum = 0.0, count = 0.0;
172 for (int r = -m_Nx; r <= m_Nx; r++) {
173 for (int s = -m_Ny; s <= m_Ny; s++) {
174 if ((i+r >= 0) and (i+r < Mx) and (j+s >= 0) and (j+s < My)) {
175 sum += b0(i+r, j+s);
176 count += 1.0;
177 }
178 }
179 }
180 // unprotected division by count but r=0,s=0 case guarantees count>=1
181 bs(i, j) = sum / count;
182 }
183 }
184 }
185 } catch (...) {
186 rank0.failed();
187 }
188 rank0.check();
189}
190
191
193
194 const unsigned int Mx = m_grid->Mx(), My = m_grid->My();
195
196 ParallelSection rank0(m_grid->com);
197 try {
198 if (m_grid->rank() == 0) {
200 b0(*m_topgp0, Mx, My),
201 bs(*m_topgsmoothp0, Mx, My),
202 mt(*m_maxtlp0, Mx, My),
203 c2(*m_C2p0, Mx, My),
204 c3(*m_C3p0, Mx, My),
205 c4(*m_C4p0, Mx, My);
206
207 for (int j=0; j < (int)My; j++) {
208 for (int i=0; i < (int)Mx; i++) {
209 // average only over those points which are in the grid
210 // do not wrap periodically
211 double
212 topgs = bs(i, j),
213 maxtltemp = 0.0,
214 sum2 = 0.0,
215 sum3 = 0.0,
216 sum4 = 0.0,
217 count = 0.0;
218
219 for (int r = -m_Nx; r <= m_Nx; r++) {
220 for (int s = -m_Ny; s <= m_Ny; s++) {
221 if ((i+r >= 0) && (i+r < (int)Mx) && (j+s >= 0) && (j+s < (int)My)) {
222 // tl is elevation of local topography at a pt in patch
223 const double tl = b0(i+r, j+s) - topgs;
224 maxtltemp = std::max(maxtltemp, tl);
225 // accumulate 2nd, 3rd, and 4th powers with only 3 multiplications
226 const double tl2 = tl * tl;
227 sum2 += tl2;
228 sum3 += tl2 * tl;
229 sum4 += tl2 * tl2;
230 count += 1.0;
231 }
232 }
233 }
234 mt(i, j) = maxtltemp;
235
236 // unprotected division by count but r=0,s=0 case guarantees count>=1
237 c2(i, j) = sum2 / count;
238 c3(i, j) = sum3 / count;
239 c4(i, j) = sum4 / count;
240 }
241 }
242
243 // scale the coeffs in Taylor series
244 const double
246 k = (n + 2) / n,
247 s2 = k * (2 * n + 2) / (2 * n),
248 s3 = s2 * (3 * n + 2) / (3 * n),
249 s4 = s3 * (4 * n + 2) / (4 * n);
250
251 PetscErrorCode ierr;
252 ierr = VecScale(*m_C2p0,s2);
253 PISM_CHK(ierr, "VecScale");
254
255 ierr = VecScale(*m_C3p0,s3);
256 PISM_CHK(ierr, "VecScale");
257
258 ierr = VecScale(*m_C4p0,s4);
259 PISM_CHK(ierr, "VecScale");
260 }
261 } catch (...) {
262 rank0.failed();
263 }
264 rank0.check();
265}
266
267
268//! Computes a smoothed thickness map.
269/*!
270The result `thksmooth` is the difference between the given upper surface
271elevation (`usurf`) and the stored smoothed bed topography (`topgsmooth`),
272except where the given original thickness (`thk`) is zero. In places where
273the original thickness is zero, the result `thksmooth` is also set to zero.
274
275Ghosted values are updated directly and no communication occurs. In fact,
276we \e assume `usurf`, `thk`, and `thksmooth` all have stencil width at least
277equal to GHOSTS. We \e check whether `topgsmooth`, which has stencil width
278maxGHOSTS, has at least GHOSTS stencil width, and throw an error if not.
279
280Call preprocess_bed() first.
281 */
283 const array::Scalar &thk,
284 const array::CellType2 &mask,
285 array::Scalar &result) const {
286
287 array::AccessScope list{&mask, &m_maxtl, &result, &thk, &m_topgsmooth, &usurf};
288
289 auto GHOSTS = result.stencil_width();
290 assert(mask.stencil_width() >= GHOSTS);
291 assert(m_maxtl.stencil_width() >= GHOSTS);
292 assert(thk.stencil_width() >= GHOSTS);
293 assert(m_topgsmooth.stencil_width() >= GHOSTS);
294 assert(usurf.stencil_width() >= GHOSTS);
295
296 ParallelSection loop(m_grid->com);
297 try {
298 for (auto p = m_grid->points(GHOSTS); p; p.next()) {
299 const int i = p.i(), j = p.j();
300
301 if (thk(i, j) < 0.0) {
302 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "BedSmoother detects negative original thickness\n"
303 "at location (i, j) = (%d, %d) ... ending", i, j);
304 }
305
306 if (thk(i, j) == 0.0) {
307 result(i, j) = 0.0;
308 } else if (m_maxtl(i, j) >= thk(i, j)) {
309 result(i, j) = thk(i, j);
310 } else {
311 if (mask.grounded(i, j)) {
312 // if grounded, compute smoothed thickness as the difference of ice surface
313 // elevation and smoothed bed elevation, making sure the result is non-negative
314 result(i, j) = std::max(usurf(i, j) - m_topgsmooth(i, j), 0.0);
315 } else {
316 // if floating, use original thickness (note: surface elevation was
317 // computed using this thickness and the sea level elevation)
318 result(i, j) = thk(i, j);
319 }
320 }
321 }
322 } catch (...) {
323 loop.failed();
324 }
325 loop.check();
326}
327
328
329/*!
330Implements the strategy for computing \f$\theta(h,x,y)\f$ from previously-
331stored coefficients, described on [Bed roughness parameterization](@ref bedrough) page and in [\ref
332Schoofbasaltopg2003].
333
334Specifically, \f$\theta = \omega^{-n}\f$ where \f$\omega\f$ is a local average
335of a rational function of surface elevation, approximated here by a Taylor polynomial:
336 \f[ \omega = \fint \left(1 - \frac{\tilde b(x_1,x_2,\xi_1,\xi_2)}{H}
337 \right)^{-(n+2)/n}\,d\xi_1\,d\xi_2
338 \approx 1 + C_2 H^{-2} + C_3 H^{-3} + C_4 H^{-4} \f]
339where \f$h =\f$ usurf, \f$H = h -\f$ topgsmooth and \f$\tilde b\f$ is the local
340bed topography, a function with mean zero. The coefficients \f$C_2,C_3,C_4\f$,
341which depend on \f$x,y\f$, are precomputed by `preprocess_bed()`.
342
343Ghosted values are updated directly and no communication occurs. In fact,
344we \e assume `usurf` and `theta` have stencil width at least
345equal to GHOSTS. We \e check whether `topgsmooth`, which has stencil width
346maxGHOSTS, has at least GHOSTS stencil width, and throw an error if not.
347
348Call preprocess_bed() first.
349 */
350void BedSmoother::theta(const array::Scalar &usurf, array::Scalar &result) const {
351
352 if ((m_Nx < 0) || (m_Ny < 0)) {
353 result.set(1.0);
354 return;
355 }
356
357 array::AccessScope list{&m_C2, &m_C3, &m_C4, &m_maxtl, &result, &m_topgsmooth, &usurf};
358
359 unsigned int GHOSTS = result.stencil_width();
360 assert(m_C2.stencil_width() >= GHOSTS);
361 assert(m_C3.stencil_width() >= GHOSTS);
362 assert(m_C4.stencil_width() >= GHOSTS);
363 assert(m_maxtl.stencil_width() >= GHOSTS);
364 assert(m_topgsmooth.stencil_width() >= GHOSTS);
365 assert(usurf.stencil_width() >= GHOSTS);
366
367 const double
368 theta_min = m_config->get_number("stress_balance.sia.bed_smoother.theta_min"),
369 theta_max = 1.0;
370
371 ParallelSection loop(m_grid->com);
372 try {
373 for (auto p = m_grid->points(GHOSTS); p; p.next()) {
374 const int i = p.i(), j = p.j();
375
376 const double H = usurf(i, j) - m_topgsmooth(i, j);
377 if (H > m_maxtl(i, j)) {
378 // thickness exceeds maximum variation in patch of local topography,
379 // so ice buries local topography; note maxtl >= 0 always
380 const double Hinv = 1.0 / std::max(H, 1.0);
381 double omega = 1.0 + Hinv*Hinv * (m_C2(i, j) + Hinv * (m_C3(i, j) + Hinv*m_C4(i, j)));
382 if (omega <= 0) { // this check *should not* be necessary: p4(s) > 0
384 "omega is negative for i=%d, j=%d\n"
385 "in BedSmoother.theta()", i, j);
386 }
387
388 if (omega < 0.001) { // this check *should not* be necessary
389 omega = 0.001;
390 }
391
392 result(i, j) = pow(omega, -m_Glen_exponent);
393 } else {
394 result(i, j) = 0.00;
395 }
396
397 result(i, j) = clip(result(i, j), theta_min, theta_max);
398 }
399 } catch (...) {
400 loop.failed();
401 }
402 loop.check();
403}
404
405
406} // end of namespace stressbalance
407} // end of namespace pism
void message(int threshold, const char format[],...) const __attribute__((format(printf
Print a message to the log.
Definition Logger.cc:49
A basic logging class.
Definition Logger.hh:40
void failed()
Indicates a failure of a parallel section.
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
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
void get_from_proc0(petsc::Vec &onp0)
Gets a local Array2 from processor 0.
Definition Array.cc:1058
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:629
std::shared_ptr< petsc::Vec > allocate_proc0_copy() const
Definition Array.cc:926
void put_on_proc0(petsc::Vec &onp0) const
Puts a local array::Scalar on processor 0.
Definition Array.cc:1015
unsigned int stencil_width() const
Get the stencil width of the current Array. Returns 0 if ghosts are not available.
Definition Array.cc:302
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition Array.cc:476
bool grounded(int i, int j) const
Definition CellType.hh:38
Wrapper around VecGetArray2d and VecRestoreArray2d.
Definition Vec.hh:55
std::shared_ptr< petsc::Vec > m_C3p0
BedSmoother(std::shared_ptr< const Grid > g)
void smoothed_thk(const array::Scalar &usurf, const array::Scalar &thk, const array::CellType2 &mask, array::Scalar &thksmooth) const
Computes a smoothed thickness map.
std::shared_ptr< petsc::Vec > m_topgp0
original bed elevation on processor 0
array::Scalar2 m_topgsmooth
smoothed bed elevation; set by calling preprocess_bed()
std::shared_ptr< petsc::Vec > m_maxtlp0
maximum elevation at (i,j) of local topography (nearby patch)
void smooth_the_bed_on_proc0()
Computes the smoothed bed by a simple average over a rectangle of grid points.
const array::Scalar & smoothed_bed() const
void theta(const array::Scalar &usurf, array::Scalar &result) const
const Config::ConstPtr m_config
std::shared_ptr< petsc::Vec > m_topgsmoothp0
smoothed bed elevation on processor 0
std::shared_ptr< petsc::Vec > m_C4p0
std::shared_ptr< const Grid > m_grid
void preprocess_bed(const array::Scalar &topg)
std::shared_ptr< petsc::Vec > m_C2p0
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
static const double b0
Definition exactTestL.cc:41
#define n
Definition exactTestM.c:37
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
Definition Units.cc:70
static const double g
Definition exactTestP.cc:36
T clip(T x, T a, T b)
static const double c2
Definition exactTestP.cc:45
static const double k
Definition exactTestP.cc:42
int count
Definition test_cube.c:16