PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
Grid.cc
Go to the documentation of this file.
1 // Copyright (C) 2004-2021, 2023 Jed Brown, 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 <array>
22 #include <gsl/gsl_interp.h>
23 #include <map>
24 #include <numeric>
25 #include <petscsys.h>
26 
27 #include "pism/util/ConfigInterface.hh"
28 #include "pism/util/Grid.hh"
29 #include "pism/util/error_handling.hh"
30 #include "pism/pism_config.hh"
31 #include "pism/util/Context.hh"
32 #include "pism/util/Logger.hh"
33 #include "pism/util/Vars.hh"
34 #include "pism/util/io/File.hh"
35 #include "pism/util/petscwrappers/DM.hh"
36 #include "pism/util/projection.hh"
37 #include "pism/util/pism_options.hh"
38 #include "pism/util/pism_utilities.hh"
39 #include "pism/util/io/IO_Flags.hh"
40 
41 #if (Pism_USE_PIO == 1)
42 // Why do I need this???
43 #define _NETCDF
44 #include <pio.h>
45 #endif
46 
47 namespace pism {
48 
49 //! Internal structures of Grid.
50 struct Grid::Impl {
51  Impl(std::shared_ptr<const Context> context);
52 
53  std::shared_ptr<petsc::DM> create_dm(int da_dof, int stencil_width) const;
54  void set_ownership_ranges(const std::vector<unsigned int> &procs_x,
55  const std::vector<unsigned int> &procs_y);
56 
58 
59  std::shared_ptr<const Context> ctx;
60 
62 
63  // int to match types used by MPI
64  int rank;
65  int size;
66 
67  //! @brief array containing lenghts (in the x-direction) of processor sub-domains
68  std::vector<PetscInt> procs_x;
69  //! @brief array containing lenghts (in the y-direction) of processor sub-domains
70  std::vector<PetscInt> procs_y;
71 
73 
75 
76  //! x-coordinates of grid points
77  std::vector<double> x;
78  //! y-coordinates of grid points
79  std::vector<double> y;
80  //! vertical grid levels in the ice; correspond to the storage grid
81  std::vector<double> z;
82 
83  int xs, xm, ys, ym;
84  //! horizontal grid spacing
85  double dx;
86  //! horizontal grid spacing
87  double dy;
88  //! cell area (meters^2)
89  double cell_area;
90  //! number of grid points in the x-direction
91  unsigned int Mx;
92  //! number of grid points in the y-direction
93  unsigned int My;
94 
96 
97  //! x-coordinate of the grid center
98  double x0;
99  //! y-coordinate of the grid center
100  double y0;
101 
102  //! half width of the ice model grid in x-direction (m)
103  double Lx;
104  //! half width of the ice model grid in y-direction (m)
105  double Ly;
106 
107  std::map<std::array<unsigned int, 2>, std::weak_ptr<petsc::DM> > dms;
108 
109  // This DM is used for I/O operations and is not owned by any
110  // array::Array (so far, anyway). We keep a pointer to it here to
111  // avoid re-allocating it many times.
112  std::shared_ptr<petsc::DM> dm_scalar_global;
113 
114  //! @brief A dictionary with pointers to array::Arrays, for passing
115  //! them from the one component to another (e.g. from IceModel to
116  //! surface and ocean models).
118 
119  //! GSL binary search accelerator used to speed up kBelowHeight().
120  gsl_interp_accel *bsearch_accel;
121 
122  //! ParallelIO I/O decompositions.
123  std::map<std::array<int, 2>, int> io_decompositions;
124 };
125 
126 Grid::Impl::Impl(std::shared_ptr<const Context> context)
127  : ctx(context), mapping_info("mapping", ctx->unit_system()) {
128  // empty
129 }
130 
131 /*! @brief Initialize a uniform, shallow (3 z-levels) grid with half-widths (Lx,Ly) and Mx by My
132  * nodes.
133  */
134 std::shared_ptr<Grid> Grid::Shallow(std::shared_ptr<const Context> ctx, double Lx, double Ly,
135  double x0, double y0, unsigned int Mx, unsigned int My,
138  try {
139  grid::Parameters p(*ctx->config());
140  p.Lx = Lx;
141  p.Ly = Ly;
142  p.x0 = x0;
143  p.y0 = y0;
144  p.Mx = Mx;
145  p.My = My;
148 
149  double Lz = ctx->config()->get_number("grid.Lz");
150  p.z = { 0.0, 0.5 * Lz, Lz };
151 
153 
154  return std::make_shared<Grid>(ctx, p);
155  } catch (RuntimeError &e) {
156  e.add_context("initializing a shallow grid");
157  throw;
158  }
159 }
160 
161 //! @brief Create a PISM distributed computational grid.
162 Grid::Grid(std::shared_ptr<const Context> context, const grid::Parameters &p)
163  : com(context->com()), m_impl(new Impl(context)) {
164 
165  try {
166  m_impl->bsearch_accel = gsl_interp_accel_alloc();
167  if (m_impl->bsearch_accel == NULL) {
168  throw RuntimeError(PISM_ERROR_LOCATION, "Failed to allocate a GSL interpolation accelerator");
169  }
170 
171  MPI_Comm_rank(com, &m_impl->rank);
172  MPI_Comm_size(com, &m_impl->size);
173 
174  p.validate();
175 
176  m_impl->Mx = p.Mx;
177  m_impl->My = p.My;
178  m_impl->x0 = p.x0;
179  m_impl->y0 = p.y0;
180  m_impl->Lx = p.Lx;
181  m_impl->Ly = p.Ly;
184  m_impl->z = p.z;
186 
188 
189  {
190  int stencil_width = (int)context->config()->get_number("grid.max_stencil_width");
191 
192  try {
193  std::shared_ptr<petsc::DM> tmp = this->get_dm(1, stencil_width);
194  } catch (RuntimeError &e) {
195  e.add_context("distributing a %d x %d grid across %d processors.", Mx(), My(), size());
196  throw;
197  }
198 
199  // hold on to a DM corresponding to dof=1, stencil_width=0 (it will
200  // be needed for I/O operations)
201  m_impl->dm_scalar_global = this->get_dm(1, 0);
202 
203  DMDALocalInfo info;
204  PetscErrorCode ierr = DMDAGetLocalInfo(*m_impl->dm_scalar_global, &info);
205  PISM_CHK(ierr, "DMDAGetLocalInfo");
206 
207  m_impl->xs = info.xs;
208  m_impl->xm = info.xm;
209  m_impl->ys = info.ys;
210  m_impl->ym = info.ym;
211  }
212 
213  int patch_size = m_impl->xm * m_impl->ym;
214  GlobalMax(com, &patch_size, &m_impl->max_patch_size, 1);
215 
216  } catch (RuntimeError &e) {
217  e.add_context("allocating Grid");
218  throw;
219  }
220 }
221 
222 //! Create a grid from a file, get information from variable `var_name`.
223 static std::shared_ptr<Grid> Grid_FromFile(std::shared_ptr<const Context> ctx, const File &file,
224  const std::string &var_name, grid::Registration r) {
225  try {
226  const Logger &log = *ctx->log();
227 
228  // The following call may fail because var_name does not exist. (And this is fatal!)
229  // Note that this sets defaults using configuration parameters, too.
230  grid::Parameters p(*ctx, file, var_name, r);
231 
232  // if we have no vertical grid information, create a fake 2-level vertical grid.
233  if (p.z.size() < 2) {
234  double Lz = ctx->config()->get_number("grid.Lz");
235  log.message(3,
236  "WARNING: Can't determine vertical grid information using '%s' in %s'\n"
237  " Using 2 levels and Lz of %3.3fm\n",
238  var_name.c_str(), file.filename().c_str(), Lz);
239 
240  p.z = { 0.0, Lz };
241  }
242 
243 
244  p.ownership_ranges_from_options(ctx->size());
245 
246  return std::make_shared<Grid>(ctx, p);
247  } catch (RuntimeError &e) {
248  e.add_context("initializing computational grid from variable \"%s\" in \"%s\"",
249  var_name.c_str(), file.filename().c_str());
250  throw;
251  }
252 }
253 
254 //! Create a grid using one of variables in `var_names` in `file`.
255 std::shared_ptr<Grid> Grid::FromFile(std::shared_ptr<const Context> ctx,
256  const std::string &filename,
257  const std::vector<std::string> &var_names,
258  grid::Registration r) {
259 
260  File file(ctx->com(), filename, io::PISM_NETCDF3, io::PISM_READONLY);
261 
262  for (const auto &name : var_names) {
263  if (file.find_variable(name)) {
264  return Grid_FromFile(ctx, file, name, r);
265  }
266  }
267 
269  "file %s does not have any of %s."
270  " Cannot initialize the grid.",
271  filename.c_str(), join(var_names, ",").c_str());
272 }
273 
275  gsl_interp_accel_free(m_impl->bsearch_accel);
276 
277 #if (Pism_USE_PIO == 1)
278  for (auto p : m_impl->io_decompositions) {
279  int ierr = PIOc_freedecomp(m_impl->ctx->pio_iosys_id(), p.second);
280  if (ierr != PIO_NOERR) {
281  m_impl->ctx->log()->message(1, "Failed to de-allocate a ParallelIO decomposition");
282  }
283  }
284 #endif
285 
286  delete m_impl;
287 }
288 
289 
290 //! Return the index `k` into `zlevels[]` so that `zlevels[k] <= height < zlevels[k+1]` and `k < Mz`.
291 unsigned int Grid::kBelowHeight(double height) const {
292 
293  const double eps = 1.0e-6;
294  if (height < 0.0 - eps) {
296  "height = %5.4f is below base of ice"
297  " (height must be non-negative)\n",
298  height);
299  }
300 
301  if (height > Lz() + eps) {
303  "height = %5.4f is above top of computational"
304  " grid Lz = %5.4f\n",
305  height, Lz());
306  }
307 
308  return gsl_interp_accel_find(m_impl->bsearch_accel, m_impl->z.data(), m_impl->z.size(), height);
309 }
310 
311 //! \brief Computes the number of processors in the X- and Y-directions.
312 static void compute_nprocs(unsigned int Mx, unsigned int My, unsigned int size, unsigned int &Nx,
313  unsigned int &Ny) {
314 
315  if (My <= 0) {
316  throw RuntimeError(PISM_ERROR_LOCATION, "'My' is invalid.");
317  }
318 
319  Nx = (unsigned int)(0.5 + sqrt(((double)Mx) * ((double)size) / ((double)My)));
320  Ny = 0;
321 
322  if (Nx == 0) {
323  Nx = 1;
324  }
325 
326  while (Nx > 0) {
327  Ny = size / Nx;
328  if (Nx * Ny == (unsigned int)size) {
329  break;
330  }
331  Nx--;
332  }
333 
334  if (Mx > My and Nx < Ny) {
335  // Swap Nx and Ny
336  auto tmp = Nx;
337  Nx = Ny;
338  Ny = tmp;
339  }
340 
341  if ((Mx / Nx) < 2) { // note: integer division
343  "Can't split %d grid points into %d parts (X-direction).", Mx,
344  (int)Nx);
345  }
346 
347  if ((My / Ny) < 2) { // note: integer division
349  "Can't split %d grid points into %d parts (Y-direction).", My,
350  (int)Ny);
351  }
352 }
353 
354 
355 //! \brief Computes processor ownership ranges corresponding to equal area
356 //! distribution among processors.
357 static std::vector<unsigned int> ownership_ranges(unsigned int Mx, unsigned int Nx) {
358 
359  std::vector<unsigned int> result(Nx);
360 
361  for (unsigned int i = 0; i < Nx; i++) {
362  result[i] = Mx / Nx + static_cast<unsigned int>((Mx % Nx) > i);
363  }
364  return result;
365 }
366 
367 //! Set processor ownership ranges. Takes care of type conversion (`unsigned int` -> `PetscInt`).
368 void Grid::Impl::set_ownership_ranges(const std::vector<unsigned int> &input_procs_x,
369  const std::vector<unsigned int> &input_procs_y) {
370  if (input_procs_x.size() * input_procs_y.size() != (size_t)size) {
371  throw RuntimeError(PISM_ERROR_LOCATION, "length(procs_x) * length(procs_y) != MPI size");
372  }
373 
374  procs_x.resize(input_procs_x.size());
375  for (unsigned int k = 0; k < input_procs_x.size(); ++k) {
376  procs_x[k] = static_cast<PetscInt>(input_procs_x[k]);
377  }
378 
379  procs_y.resize(input_procs_y.size());
380  for (unsigned int k = 0; k < input_procs_y.size(); ++k) {
381  procs_y[k] = static_cast<PetscInt>(input_procs_y[k]);
382  }
383 }
384 
386  std::vector<unsigned int> x, y;
387 };
388 
389 //! Compute processor ownership ranges using the grid size, MPI communicator size, and command-line
390 //! options `-Nx`, `-Ny`, `-procs_x`, `-procs_y`.
391 static OwnershipRanges compute_ownership_ranges(unsigned int Mx, unsigned int My,
392  unsigned int size) {
393  OwnershipRanges result;
394 
395  unsigned int Nx_default, Ny_default;
396  compute_nprocs(Mx, My, size, Nx_default, Ny_default);
397 
398  // check -Nx and -Ny
399  options::Integer Nx("-Nx", "Number of processors in the x direction", Nx_default);
400  options::Integer Ny("-Ny", "Number of processors in the y direction", Ny_default);
401 
402  // validate results (compute_nprocs checks its results, but we also need to validate command-line
403  // options)
404  if ((Mx / Nx) < 2) {
406  "Can't split %d grid points into %d parts (X-direction).", Mx,
407  (int)Nx);
408  }
409 
410  if ((My / Ny) < 2) {
412  "Can't split %d grid points into %d parts (Y-direction).", My,
413  (int)Ny);
414  }
415 
416  if (Nx * Ny != (int)size) {
417  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "Nx * Ny has to be equal to %d.", size);
418  }
419 
420  // check -procs_x and -procs_y
421  options::IntegerList procs_x("-procs_x", "Processor ownership ranges (x direction)", {});
422  options::IntegerList procs_y("-procs_y", "Processor ownership ranges (y direction)", {});
423 
424  if (procs_x.is_set()) {
425  if (procs_x->size() != (unsigned int)Nx) {
426  throw RuntimeError(PISM_ERROR_LOCATION, "-Nx has to be equal to the -procs_x size.");
427  }
428 
429  result.x.resize(procs_x->size());
430  for (unsigned int k = 0; k < procs_x->size(); ++k) {
431  result.x[k] = procs_x[k];
432  }
433 
434  } else {
435  result.x = ownership_ranges(Mx, Nx);
436  }
437 
438  if (procs_y.is_set()) {
439  if (procs_y->size() != (unsigned int)Ny) {
440  throw RuntimeError(PISM_ERROR_LOCATION, "-Ny has to be equal to the -procs_y size.");
441  }
442 
443  result.y.resize(procs_y->size());
444  for (unsigned int k = 0; k < procs_y->size(); ++k) {
445  result.y[k] = procs_y[k];
446  }
447  } else {
448  result.y = ownership_ranges(My, Ny);
449  }
450 
451  if (result.x.size() * result.y.size() != size) {
452  throw RuntimeError(PISM_ERROR_LOCATION, "length(procs_x) * length(procs_y) != MPI size");
453  }
454 
455  return result;
456 }
457 
458 //! Compute horizontal grid spacing. See compute_horizontal_coordinates() for more.
459 static double compute_horizontal_spacing(double half_width, unsigned int M, bool cell_centered) {
460  if (cell_centered) {
461  return 2.0 * half_width / M;
462  }
463 
464  return 2.0 * half_width / (M - 1);
465 }
466 
467 //! Compute grid coordinates for one direction (X or Y).
468 static std::vector<double> compute_coordinates(unsigned int M, double delta, double v_min,
469  double v_max, bool cell_centered) {
470  std::vector<double> result(M);
471 
472  // Here v_min, v_max define the extent of the computational domain,
473  // which is not necessarily the same thing as the smallest and
474  // largest values of grid coordinates.
475  if (cell_centered) {
476  for (unsigned int i = 0; i < M; ++i) {
477  result[i] = v_min + (i + 0.5) * delta;
478  }
479  result[M - 1] = v_max - 0.5 * delta;
480  } else {
481  for (unsigned int i = 0; i < M; ++i) {
482  result[i] = v_min + i * delta;
483  }
484  result[M - 1] = v_max;
485  }
486  return result;
487 }
488 
489 //! Compute horizontal spacing parameters `dx` and `dy` and grid coordinates using `Mx`, `My`, `Lx`, `Ly` and periodicity.
490 /*!
491 The grid used in PISM, in particular the PETSc DAs used here, are periodic in x and y.
492 This means that the ghosted values ` foo[i+1][j], foo[i-1][j], foo[i][j+1], foo[i][j-1]`
493 for all 2D Vecs, and similarly in the x and y directions for 3D Vecs, are always available.
494 That is, they are available even if i,j is a point at the edge of the grid. On the other
495 hand, by default, `dx` is the full width `2 * Lx` divided by `Mx - 1`.
496 This means that we conceive of the computational domain as starting at the `i = 0`
497 grid location and ending at the `i = Mx - 1` grid location, in particular.
498 This idea is not quite compatible with the periodic nature of the grid.
499 
500 The upshot is that if one computes in a truly periodic way then the gap between the
501 `i = 0` and `i = Mx - 1` grid points should \em also have width `dx`.
502 Thus we compute `dx = 2 * Lx / Mx`.
503  */
505 
506  bool cell_centered = registration == grid::CELL_CENTER;
507 
508  dx = compute_horizontal_spacing(Lx, Mx, cell_centered);
509 
510  dy = compute_horizontal_spacing(Ly, My, cell_centered);
511 
512  cell_area = dx * dy;
513 
514  double x_min = x0 - Lx, x_max = x0 + Lx;
515 
516  x = compute_coordinates(Mx, dx, x_min, x_max, cell_centered);
517 
518  double y_min = y0 - Ly, y_max = y0 + Ly;
519 
520  y = compute_coordinates(My, dy, y_min, y_max, cell_centered);
521 }
522 
523 //! \brief Report grid parameters.
525 
526  const Logger &log = *this->ctx()->log();
527  units::System::Ptr sys = this->ctx()->unit_system();
528 
529  log.message(2, "computational domain and grid:\n");
530 
531  units::Converter km(sys, "m", "km");
532 
533  // report on grid
534  log.message(2, " grid size %d x %d x %d\n", Mx(), My(), Mz());
535 
536  // report on computational box
537  log.message(2, " spatial domain %.2f km x %.2f km x %.2f m\n", km(2 * Lx()),
538  km(2 * Ly()), Lz());
539 
540  // report on grid cell dims
541  const double one_km = 1000.0;
542  if (std::min(dx(), dy()) > one_km) {
543  log.message(2, " horizontal grid cell %.2f km x %.2f km\n", km(dx()), km(dy()));
544  } else {
545  log.message(2, " horizontal grid cell %.0f m x %.0f m\n", dx(), dy());
546  }
547  if (fabs(dz_max() - dz_min()) <= 1.0e-8) {
548  log.message(2, " vertical spacing in ice dz = %.3f m (equal spacing)\n", dz_min());
549  } else {
550  log.message(2, " vertical spacing in ice uneven, %d levels, %.3f m < dz < %.3f m\n", Mz(),
551  dz_min(), dz_max());
552  }
553 
554  // if -verbose (=-verbose 3) then (somewhat redundantly) list parameters of grid
555  {
556  log.message(3, " Grid parameters:\n");
557  log.message(3, " Lx = %6.2f km, Ly = %6.2f km, Lz = %6.2f m, \n", km(Lx()), km(Ly()),
558  Lz());
559  log.message(3, " x0 = %6.2f km, y0 = %6.2f km, (coordinates of center)\n", km(x0()),
560  km(y0()));
561  log.message(3, " Mx = %d, My = %d, Mz = %d, \n", Mx(), My(), Mz());
562  log.message(3, " dx = %6.3f km, dy = %6.3f km, \n", km(dx()), km(dy()));
563  log.message(3, " Nx = %d, Ny = %d\n", (int)m_impl->procs_x.size(),
564  (int)m_impl->procs_y.size());
565 
566  log.message(3, " Registration: %s\n",
568  log.message(3, " Periodicity: %s\n",
570  }
571 
572  {
573  log.message(5, " REALLY verbose output on Grid:\n");
574  log.message(5, " vertical levels in ice (Mz=%d, Lz=%5.4f): ", Mz(), Lz());
575  for (unsigned int k = 0; k < Mz(); k++) {
576  log.message(5, " %5.4f, ", z(k));
577  }
578  log.message(5, "\n");
579  }
580 }
581 
582 
583 //! \brief Computes indices of grid points to the lower left and upper right from (X,Y).
584 /*!
585  * \code
586  * 3 2
587  * o-------o
588  * | |
589  * | + |
590  * o-------o
591  * 0 1
592  * \endcode
593  *
594  * If "+" is the point (X,Y), then (i_left, j_bottom) corresponds to
595  * point "0" and (i_right, j_top) corresponds to point "2".
596  *
597  * Does not check if the resulting indexes are in the current
598  * processor's domain. Ensures that computed indexes are within the
599  * grid.
600  */
601 void Grid::compute_point_neighbors(double X, double Y, int &i_left, int &i_right, int &j_bottom,
602  int &j_top) const {
603  i_left = (int)floor((X - m_impl->x[0]) / m_impl->dx);
604  j_bottom = (int)floor((Y - m_impl->y[0]) / m_impl->dy);
605 
606  i_right = i_left + 1;
607  j_top = j_bottom + 1;
608 
609  i_left = std::max(i_left, 0);
610  i_right = std::max(i_right, 0);
611 
612  i_left = std::min(i_left, (int)m_impl->Mx - 1);
613  i_right = std::min(i_right, (int)m_impl->Mx - 1);
614 
615  j_bottom = std::max(j_bottom, 0);
616  j_top = std::max(j_top, 0);
617 
618  j_bottom = std::min(j_bottom, (int)m_impl->My - 1);
619  j_top = std::min(j_top, (int)m_impl->My - 1);
620 }
621 
622 std::vector<int> Grid::point_neighbors(double X, double Y) const {
623  int i_left, i_right, j_bottom, j_top;
624  this->compute_point_neighbors(X, Y, i_left, i_right, j_bottom, j_top);
625  return { i_left, i_right, j_bottom, j_top };
626 }
627 
628 //! \brief Compute 4 interpolation weights necessary for linear interpolation
629 //! from the current grid. See compute_point_neighbors for the ordering of
630 //! neighbors.
631 std::vector<double> Grid::interpolation_weights(double X, double Y) const {
632  int i_left = 0, i_right = 0, j_bottom = 0, j_top = 0;
633  // these values (zeros) are used when interpolation is impossible
634  double alpha = 0.0, beta = 0.0;
635 
636  compute_point_neighbors(X, Y, i_left, i_right, j_bottom, j_top);
637 
638  if (i_left != i_right) {
639  assert(m_impl->x[i_right] - m_impl->x[i_left] != 0.0);
640  alpha = (X - m_impl->x[i_left]) / (m_impl->x[i_right] - m_impl->x[i_left]);
641  }
642 
643  if (j_bottom != j_top) {
644  assert(m_impl->y[j_top] - m_impl->y[j_bottom] != 0.0);
645  beta = (Y - m_impl->y[j_bottom]) / (m_impl->y[j_top] - m_impl->y[j_bottom]);
646  }
647 
648  return { (1.0 - alpha) * (1.0 - beta), alpha * (1.0 - beta), alpha * beta, (1.0 - alpha) * beta };
649 }
650 
651 //! @brief Get a PETSc DM ("distributed array manager") object for given `dof` (number of degrees of
652 //! freedom per grid point) and stencil width.
653 std::shared_ptr<petsc::DM> Grid::get_dm(unsigned int dm_dof, unsigned int stencil_width) const {
654 
655  std::array<unsigned int, 2> key{ dm_dof, stencil_width };
656 
657  if (m_impl->dms[key].expired()) {
658  // note: here "result" is needed because m_impl->dms is a std::map of weak_ptr
659  //
660  // m_impl->dms[j] = m_impl->create_dm(dm_dof, stencil_width);
661  //
662  // would create a shared_ptr, then assign it to a weak_ptr. At this point the
663  // shared_ptr (the right hand side) will be destroyed and the corresponding weak_ptr
664  // will be a nullptr.
665  auto result = m_impl->create_dm(dm_dof, stencil_width);
666  m_impl->dms[key] = result;
667  return result;
668  }
669 
670  return m_impl->dms[key].lock();
671 }
672 
673 //! Return grid periodicity.
675  return m_impl->periodicity;
676 }
677 
679  return m_impl->registration;
680 }
681 
682 //! Return execution context this grid corresponds to.
683 std::shared_ptr<const Context> Grid::ctx() const {
684  return m_impl->ctx;
685 }
686 
687 //! @brief Create a DM with the given number of `dof` (degrees of freedom per grid point) and
688 //! stencil width.
689 std::shared_ptr<petsc::DM> Grid::Impl::create_dm(int da_dof, int stencil_width) const {
690 
691  ctx->log()->message(3, "* Creating a DM with dof=%d and stencil_width=%d...\n", da_dof,
692  stencil_width);
693 
694  DM result;
695  PetscErrorCode ierr =
696  DMDACreate2d(ctx->com(), DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, DMDA_STENCIL_BOX, Mx, My,
697  (PetscInt)procs_x.size(), (PetscInt)procs_y.size(), da_dof, stencil_width,
698  procs_x.data(), procs_y.data(), // lx, ly
699  &result);
700  PISM_CHK(ierr, "DMDACreate2d");
701 
702 #if PETSC_VERSION_GE(3, 8, 0)
703  ierr = DMSetUp(result);
704  PISM_CHK(ierr, "DMSetUp");
705 #endif
706 
707  return std::make_shared<petsc::DM>(result);
708 }
709 
710 //! MPI rank.
711 int Grid::rank() const {
712  return m_impl->rank;
713 }
714 
715 //! MPI communicator size.
716 unsigned int Grid::size() const {
717  return m_impl->size;
718 }
719 
720 //! Dictionary of variables (2D and 3D fields) associated with this grid.
722  return m_impl->variables;
723 }
724 
725 //! Dictionary of variables (2D and 3D fields) associated with this grid.
726 const Vars &Grid::variables() const {
727  return m_impl->variables;
728 }
729 
730 //! Global starting index of this processor's subset.
731 int Grid::xs() const {
732  return m_impl->xs;
733 }
734 
735 //! Global starting index of this processor's subset.
736 int Grid::ys() const {
737  return m_impl->ys;
738 }
739 
740 //! Width of this processor's sub-domain.
741 int Grid::xm() const {
742  return m_impl->xm;
743 }
744 
745 //! Width of this processor's sub-domain.
746 int Grid::ym() const {
747  return m_impl->ym;
748 }
749 
750 //! Total grid size in the X direction.
751 unsigned int Grid::Mx() const {
752  return m_impl->Mx;
753 }
754 
755 //! Total grid size in the Y direction.
756 unsigned int Grid::My() const {
757  return m_impl->My;
758 }
759 
760 //! Number of vertical levels.
761 unsigned int Grid::Mz() const {
762  return m_impl->z.size();
763 }
764 
765 //! X-coordinates.
766 const std::vector<double> &Grid::x() const {
767  return m_impl->x;
768 }
769 
770 //! Get a particular x coordinate.
771 double Grid::x(size_t i) const {
772  return m_impl->x[i];
773 }
774 
775 //! Y-coordinates.
776 const std::vector<double> &Grid::y() const {
777  return m_impl->y;
778 }
779 
780 //! Get a particular y coordinate.
781 double Grid::y(size_t i) const {
782  return m_impl->y[i];
783 }
784 
785 //! Z-coordinates within the ice.
786 const std::vector<double> &Grid::z() const {
787  return m_impl->z;
788 }
789 
790 //! Get a particular z coordinate.
791 double Grid::z(size_t i) const {
792  return m_impl->z[i];
793 }
794 
795 //! Horizontal grid spacing.
796 double Grid::dx() const {
797  return m_impl->dx;
798 }
799 
800 //! Horizontal grid spacing.
801 double Grid::dy() const {
802  return m_impl->dy;
803 }
804 
805 double Grid::cell_area() const {
806  return m_impl->cell_area;
807 }
808 
809 //! Minimum vertical spacing.
810 double Grid::dz_min() const {
811  double result = m_impl->z.back();
812  for (unsigned int k = 0; k < m_impl->z.size() - 1; ++k) {
813  const double dz = m_impl->z[k + 1] - m_impl->z[k];
814  result = std::min(dz, result);
815  }
816  return result;
817 }
818 
819 //! Maximum vertical spacing.
820 double Grid::dz_max() const {
821  double result = 0.0;
822  for (unsigned int k = 0; k < m_impl->z.size() - 1; ++k) {
823  const double dz = m_impl->z[k + 1] - m_impl->z[k];
824  result = std::max(dz, result);
825  }
826  return result;
827 }
828 
829 //! Half-width of the computational domain.
830 double Grid::Lx() const {
831  return m_impl->Lx;
832 }
833 
834 //! Half-width of the computational domain.
835 double Grid::Ly() const {
836  return m_impl->Ly;
837 }
838 
839 //! Height of the computational domain.
840 double Grid::Lz() const {
841  return m_impl->z.back();
842 }
843 
844 //! X-coordinate of the center of the domain.
845 double Grid::x0() const {
846  return m_impl->x0;
847 }
848 
849 //! Y-coordinate of the center of the domain.
850 double Grid::y0() const {
851  return m_impl->y0;
852 }
853 
854 /*!
855  * Return the size of the biggest sub-domain (part owned by a MPI process)
856  */
857 int Grid::max_patch_size() const {
858  return m_impl->max_patch_size;
859 }
860 
861 
862 namespace grid {
863 //! \brief Set the vertical levels in the ice according to values in `Mz` (number of levels), `Lz`
864 //! (domain height), `spacing` (quadratic or equal) and `lambda` (quadratic spacing parameter).
865 /*!
866  - When `vertical_spacing == EQUAL`, the vertical grid in the ice is equally spaced:
867  `zlevels[k] = k dz` where `dz = Lz / (Mz - 1)`.
868  - When `vertical_spacing == QUADRATIC`, the spacing is a quadratic function. The intent
869  is that the spacing is smaller near the base than near the top.
870 
871  In particular, if
872  \f$\zeta_k = k / (\mathtt{Mz} - 1)\f$ then `zlevels[k] = Lz *`
873  ((\f$\zeta_k\f$ / \f$\lambda\f$) * (1.0 + (\f$\lambda\f$ - 1.0)
874  * \f$\zeta_k\f$)) where \f$\lambda\f$ = 4. The value \f$\lambda\f$
875  indicates the slope of the quadratic function as it leaves the base.
876  Thus a value of \f$\lambda\f$ = 4 makes the spacing about four times finer
877  at the base than equal spacing would be.
878  */
879 std::vector<double> compute_vertical_levels(double new_Lz, unsigned int new_Mz,
880  grid::VerticalSpacing spacing, double lambda) {
881 
882  if (new_Mz < 2) {
883  throw RuntimeError(PISM_ERROR_LOCATION, "Mz must be at least 2");
884  }
885 
886  if (new_Lz <= 0) {
887  throw RuntimeError(PISM_ERROR_LOCATION, "Lz must be positive");
888  }
889 
890  if (spacing == grid::QUADRATIC and lambda <= 0) {
891  throw RuntimeError(PISM_ERROR_LOCATION, "lambda must be positive");
892  }
893 
894  std::vector<double> result(new_Mz);
895 
896  // Fill the levels in the ice:
897  switch (spacing) {
898  case grid::EQUAL: {
899  double dz = new_Lz / ((double)new_Mz - 1);
900 
901  // Equal spacing
902  for (unsigned int k = 0; k < new_Mz - 1; k++) {
903  result[k] = dz * ((double)k);
904  }
905  result[new_Mz - 1] = new_Lz; // make sure it is exactly equal
906  break;
907  }
908  case grid::QUADRATIC: {
909  // this quadratic scheme is an attempt to be less extreme in the fineness near the base.
910  for (unsigned int k = 0; k < new_Mz - 1; k++) {
911  const double zeta = ((double)k) / ((double)new_Mz - 1);
912  result[k] = new_Lz * ((zeta / lambda) * (1.0 + (lambda - 1.0) * zeta));
913  }
914  result[new_Mz - 1] = new_Lz; // make sure it is exactly equal
915  break;
916  }
917  default:
918  throw RuntimeError(PISM_ERROR_LOCATION, "spacing can not be UNKNOWN");
919  }
920 
921  return result;
922 }
923 
924 //! Convert a string to Periodicity.
925 Periodicity string_to_periodicity(const std::string &keyword) {
926  if (keyword == "none") {
927  return NOT_PERIODIC;
928  }
929 
930  if (keyword == "x") {
931  return X_PERIODIC;
932  }
933 
934  if (keyword == "y") {
935  return Y_PERIODIC;
936  }
937 
938  if (keyword == "xy") {
939  return XY_PERIODIC;
940  }
941 
942  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "grid periodicity type '%s' is invalid.",
943  keyword.c_str());
944 }
945 
946 //! Convert Periodicity to a STL string.
948  switch (p) {
949  case NOT_PERIODIC:
950  return "none";
951  case X_PERIODIC:
952  return "x";
953  case Y_PERIODIC:
954  return "y";
955  default:
956  case XY_PERIODIC:
957  return "xy";
958  }
959 }
960 
961 //! Convert an STL string to SpacingType.
962 VerticalSpacing string_to_spacing(const std::string &keyword) {
963  if (keyword == "quadratic") {
964  return QUADRATIC;
965  }
966 
967  if (keyword == "equal") {
968  return EQUAL;
969  }
970 
971  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "ice vertical spacing type '%s' is invalid.",
972  keyword.c_str());
973 }
974 
975 //! Convert SpacingType to an STL string.
977  switch (s) {
978  case EQUAL:
979  return "equal";
980  default:
981  case QUADRATIC:
982  return "quadratic";
983  }
984 }
985 
986 Registration string_to_registration(const std::string &keyword) {
987  if (keyword == "center") {
988  return CELL_CENTER;
989  }
990 
991  if (keyword == "corner") {
992  return CELL_CORNER;
993  }
994 
995  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "invalid grid registration: %s",
996  keyword.c_str());
997 }
998 
1000  switch (registration) {
1001  case CELL_CORNER:
1002  return "corner";
1003  default:
1004  case CELL_CENTER:
1005  return "center";
1006  }
1007 }
1008 
1009 void InputGridInfo::reset() {
1010 
1011  filename = "";
1012 
1013  t_len = 0;
1014 
1015  x0 = 0;
1016  Lx = 0;
1017 
1018  y0 = 0;
1019  Ly = 0;
1020 
1021  z_min = 0;
1022  z_max = 0;
1023 }
1024 
1025 void InputGridInfo::report(const Logger &log, int threshold, units::System::Ptr s) const {
1026  units::Converter km(s, "m", "km");
1027 
1028  log.message(threshold, " x: %5d points, [%10.3f, %10.3f] km, x0 = %10.3f km, Lx = %10.3f km\n",
1029  (int)this->x.size(), km(this->x0 - this->Lx), km(this->x0 + this->Lx), km(this->x0),
1030  km(this->Lx));
1031 
1032  log.message(threshold, " y: %5d points, [%10.3f, %10.3f] km, y0 = %10.3f km, Ly = %10.3f km\n",
1033  (int)this->y.size(), km(this->y0 - this->Ly), km(this->y0 + this->Ly), km(this->y0),
1034  km(this->Ly));
1035 
1036  log.message(threshold, " z: %5d points, [%10.3f, %10.3f] m\n", (int)this->z.size(), this->z_min,
1037  this->z_max);
1038 
1039  log.message(threshold, " t: %5d records\n\n", this->t_len);
1040 }
1041 
1042 InputGridInfo::InputGridInfo(const File &file, const std::string &variable,
1043  units::System::Ptr unit_system, Registration r) {
1044  try {
1045  reset();
1046 
1047  filename = file.filename();
1048  variable_name = variable;
1049 
1050  // try "variable" as the standard_name first, then as the short name:
1051  auto var = file.find_variable(variable, variable);
1052 
1053  if (not var.exists) {
1054  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "variable \"%s\" is missing",
1055  variable.c_str());
1056  }
1057 
1058  auto dimensions = file.dimensions(var.name);
1059 
1060  bool time_dimension_processed = false;
1061  for (const auto &dimension_name : dimensions) {
1062 
1063  AxisType dimtype = file.dimension_type(dimension_name, unit_system);
1064 
1065  this->dimension_types[dimension_name] = dimtype;
1066 
1067  switch (dimtype) {
1068  case X_AXIS: {
1069  this->x = file.read_dimension(dimension_name);
1070  double x_min = vector_min(this->x), x_max = vector_max(this->x);
1071  this->x0 = 0.5 * (x_min + x_max);
1072  this->Lx = 0.5 * (x_max - x_min);
1073  if (r == CELL_CENTER) {
1074  const double dx = this->x[1] - this->x[0];
1075  this->Lx += 0.5 * dx;
1076  }
1077  break;
1078  }
1079  case Y_AXIS: {
1080  this->y = file.read_dimension(dimension_name);
1081  double y_min = vector_min(this->y), y_max = vector_max(this->y);
1082  this->y0 = 0.5 * (y_min + y_max);
1083  this->Ly = 0.5 * (y_max - y_min);
1084  if (r == CELL_CENTER) {
1085  const double dy = this->y[1] - this->y[0];
1086  this->Ly += 0.5 * dy;
1087  }
1088  break;
1089  }
1090  case Z_AXIS: {
1091  this->z = file.read_dimension(dimension_name);
1092  this->z_min = vector_min(this->z);
1093  this->z_max = vector_max(this->z);
1094  break;
1095  }
1096  case T_AXIS: {
1097  if (time_dimension_processed) {
1098  // ignore the second, third, etc dimension interpreted as "time" and override
1099  // the dimension type: it is not "time" in the sense of "record dimension"
1100  this->dimension_types[dimension_name] = UNKNOWN_AXIS;
1101  } else {
1102  this->t_len = file.dimension_length(dimension_name);
1103  time_dimension_processed = false;
1104  }
1105  break;
1106  }
1107  case UNKNOWN_AXIS:
1108  default: {
1109  // ignore unknown axes
1110  break;
1111  }
1112  } // switch
1113  } // for loop
1114  } catch (RuntimeError &e) {
1115  e.add_context("getting grid information using variable '%s' in '%s'", variable.c_str(),
1116  file.filename().c_str());
1117  throw;
1118  }
1119 }
1120 
1121 Parameters::Parameters(const Config &config) {
1122  Lx = config.get_number("grid.Lx");
1123  Ly = config.get_number("grid.Ly");
1124 
1125  x0 = 0.0;
1126  y0 = 0.0;
1127 
1128  Mx = static_cast<unsigned int>(config.get_number("grid.Mx"));
1129  My = static_cast<unsigned int>(config.get_number("grid.My"));
1130 
1131  periodicity = string_to_periodicity(config.get_string("grid.periodicity"));
1132  registration = string_to_registration(config.get_string("grid.registration"));
1133 
1134  double Lz = config.get_number("grid.Lz");
1135  unsigned int Mz = config.get_number("grid.Mz");
1136  double lambda = config.get_number("grid.lambda");
1137  VerticalSpacing s = string_to_spacing(config.get_string("grid.ice_vertical_spacing"));
1138  z = compute_vertical_levels(Lz, Mz, s, lambda);
1139  // does not set ownership ranges because we don't know if these settings are final
1140 }
1141 
1142 void Parameters::ownership_ranges_from_options(unsigned int size) {
1144  procs_x = procs.x;
1145  procs_y = procs.y;
1146 }
1147 
1148 void Parameters::init_from_file(const Context &ctx, const File &file,
1149  const std::string &variable_name, Registration r) {
1150  int size = 0;
1151  MPI_Comm_size(ctx.com(), &size);
1152 
1153  InputGridInfo input_grid(file, variable_name, ctx.unit_system(), r);
1154 
1155  Lx = input_grid.Lx;
1156  Ly = input_grid.Ly;
1157  x0 = input_grid.x0;
1158  y0 = input_grid.y0;
1159  Mx = input_grid.x.size();
1160  My = input_grid.y.size();
1161  registration = r;
1162  z = input_grid.z;
1163 }
1164 
1165 Parameters::Parameters(const Context &ctx, const File &file, const std::string &variable_name,
1166  Registration r) {
1167  init_from_file(ctx, file, variable_name, r);
1168 }
1169 
1170 Parameters::Parameters(const Context &ctx, const std::string &filename,
1171  const std::string &variable_name, Registration r) {
1172  File file(ctx.com(), filename, io::PISM_NETCDF3, io::PISM_READONLY);
1173  init_from_file(ctx, file, variable_name, r);
1174 }
1175 
1176 
1177 void Parameters::horizontal_size_from_options() {
1178  Mx = options::Integer("-Mx", "grid size in X direction", Mx);
1179  My = options::Integer("-My", "grid size in Y direction", My);
1180 }
1181 
1182 void Parameters::horizontal_extent_from_options(std::shared_ptr<units::System> unit_system) {
1183  // Domain size
1184  {
1185  const double km = 1000.0;
1186  Lx = km * options::Real(unit_system, "-Lx", "Half of the grid extent in the Y direction, in km",
1187  "km", Lx / km);
1188  Ly = km * options::Real(unit_system, "-Ly", "Half of the grid extent in the X direction, in km",
1189  "km", Ly / km);
1190  }
1191 
1192  // Alternatively: domain size and extent
1193  {
1194  options::RealList x_range("-x_range", "min,max x coordinate values", {});
1195  options::RealList y_range("-y_range", "min,max y coordinate values", {});
1196 
1197  if (x_range.is_set() and y_range.is_set()) {
1198  if (x_range->size() != 2 or y_range->size() != 2) {
1199  throw RuntimeError(PISM_ERROR_LOCATION, "-x_range and/or -y_range argument is invalid.");
1200  }
1201  x0 = (x_range[0] + x_range[1]) / 2.0;
1202  y0 = (y_range[0] + y_range[1]) / 2.0;
1203  Lx = (x_range[1] - x_range[0]) / 2.0;
1204  Ly = (y_range[1] - y_range[0]) / 2.0;
1205  }
1206  }
1207 }
1208 
1209 void Parameters::vertical_grid_from_options(Config::ConstPtr config) {
1210  double Lz = (not z.empty()) ? z.back() : config->get_number("grid.Lz");
1211  int Mz = (not z.empty()) ? z.size() : config->get_number("grid.Mz");
1212 
1214  string_to_spacing(config->get_string("grid.ice_vertical_spacing")),
1215  config->get_number("grid.lambda"));
1216 }
1217 
1218 void Parameters::validate() const {
1219  if (Mx < 3) {
1221  "Mx = %d is invalid (has to be 3 or greater)", Mx);
1222  }
1223 
1224  if (My < 3) {
1226  "My = %d is invalid (has to be 3 or greater)", My);
1227  }
1228 
1229  if (Lx <= 0.0) {
1230  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "Lx = %f is invalid (negative)", Lx);
1231  }
1232 
1233  if (Ly <= 0.0) {
1234  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "Ly = %f is invalid (negative)", Ly);
1235  }
1236 
1237  if (not is_increasing(z)) {
1238  throw RuntimeError(PISM_ERROR_LOCATION, "z levels are not increasing");
1239  }
1240 
1241  if (z[0] > 1e-6) {
1242  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "first z level is not zero: %f", z[0]);
1243  }
1244 
1245  if (z.back() < 0.0) {
1246  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "last z level is negative: %f", z.back());
1247  }
1248 
1249  if (std::accumulate(procs_x.begin(), procs_x.end(), 0.0) != Mx) {
1250  throw RuntimeError(PISM_ERROR_LOCATION, "procs_x don't sum up to Mx");
1251  }
1252 
1253  if (std::accumulate(procs_y.begin(), procs_y.end(), 0.0) != My) {
1254  throw RuntimeError(PISM_ERROR_LOCATION, "procs_y don't sum up to My");
1255  }
1256 }
1257 
1258 } // namespace grid
1259 
1260 //! Create a grid using command-line options and (possibly) an input file.
1261 /** Processes options -i, -bootstrap, -Mx, -My, -Mz, -Lx, -Ly, -Lz, -x_range, -y_range.
1262  */
1263 std::shared_ptr<Grid> Grid::FromOptions(std::shared_ptr<const Context> ctx) {
1264  auto config = ctx->config();
1265 
1266  auto input_file = config->get_string("input.file");
1267  bool bootstrap = config->get_flag("input.bootstrap");
1268 
1269  auto r = grid::string_to_registration(config->get_string("grid.registration"));
1270 
1271  auto log = ctx->log();
1272 
1273  if (not input_file.empty() and (not bootstrap)) {
1274  // get grid from a PISM input file
1275  return Grid::FromFile(ctx, input_file, { "enthalpy", "temp" }, r);
1276  }
1277 
1278  if (not input_file.empty() and bootstrap) {
1279  // bootstrapping; get domain size defaults from an input file, allow overriding all grid
1280  // parameters using command-line options
1281 
1282  grid::Parameters input_grid(*config);
1283 
1284  bool grid_info_found = false;
1285 
1286  File file(ctx->com(), input_file, io::PISM_NETCDF3, io::PISM_READONLY);
1287 
1288  for (const auto *name : { "land_ice_thickness", "bedrock_altitude", "thk", "topg" }) {
1289 
1290  grid_info_found = file.find_variable(name);
1291  if (not grid_info_found) {
1292  // Failed to find using a short name. Try using name as a
1293  // standard name...
1294  grid_info_found = file.find_variable("unlikely_name", name).exists;
1295  }
1296 
1297  if (grid_info_found) {
1298  input_grid = grid::Parameters(*ctx, file, name, r);
1299  break;
1300  }
1301  }
1302 
1303  if (not grid_info_found) {
1304  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "no geometry information found in '%s'",
1305  input_file.c_str());
1306  }
1307 
1308  // process all possible options controlling grid parameters, overriding values read
1309  // from a file
1310  input_grid.horizontal_size_from_options();
1311  input_grid.horizontal_extent_from_options(ctx->unit_system());
1312  input_grid.vertical_grid_from_options(config);
1313  input_grid.ownership_ranges_from_options(ctx->size());
1314 
1315  auto result = std::make_shared<Grid>(ctx, input_grid);
1316 
1317  units::System::Ptr sys = ctx->unit_system();
1318  units::Converter km(sys, "m", "km");
1319 
1320  // report on resulting computational box
1321  log->message(2,
1322  " setting computational box for ice from '%s' and\n"
1323  " user options: [%6.2f km, %6.2f km] x [%6.2f km, %6.2f km] x [0 m, %6.2f m]\n",
1324  input_file.c_str(), km(result->x0() - result->Lx()),
1325  km(result->x0() + result->Lx()), km(result->y0() - result->Ly()),
1326  km(result->y0() + result->Ly()), result->Lz());
1327 
1328  return result;
1329  }
1330 
1331  {
1332  // This covers the two remaining cases "-i is not set, -bootstrap is set" and "-i is
1333  // not set, -bootstrap is not set either".
1334 
1335  // Use defaults from the configuration database
1336  grid::Parameters P(*ctx->config());
1338  P.horizontal_extent_from_options(ctx->unit_system());
1339  P.vertical_grid_from_options(ctx->config());
1341 
1342  return std::make_shared<Grid>(ctx, P);
1343  }
1344 }
1345 
1347  return m_impl->mapping_info;
1348 }
1349 
1351  m_impl->mapping_info = info;
1352  // FIXME: re-compute lat/lon coordinates
1353 }
1354 
1355 /*!
1356  * initialize an I/O decomposition
1357  *
1358  * @param[in] dof size of the last dimension (usually z)
1359  * @param[in] output_datatype an integer specifying a data type (`PIO_DOUBLE`, etc)
1360  */
1361 int Grid::pio_io_decomposition(int dof, int output_datatype) const {
1362  int result = 0;
1363 #if (Pism_USE_PIO == 1)
1364  {
1365  std::array<int, 2> key{ dof, output_datatype };
1366 
1367  result = m_impl->io_decompositions[key];
1368 
1369  if (result == 0) {
1370 
1371  int ndims = dof < 2 ? 2 : 3;
1372 
1373  // the last element is not used if ndims == 2
1374  std::vector<int> gdimlen{(int)My(), (int)Mx(), dof};
1375  std::vector<long int> start{ys(), xs(), 0}, count{ym(), xm(), dof};
1376 
1377  int stat = PIOc_InitDecomp_bc(m_impl->ctx->pio_iosys_id(),
1378  output_datatype, ndims, gdimlen.data(),
1379  start.data(), count.data(), &result);
1380  m_impl->io_decompositions[key] = result;
1381  if (stat != PIO_NOERR) {
1383  "Failed to create a ParallelIO I/O decomposition");
1384  }
1385  }
1386  }
1387 #else
1388  (void) dof;
1389  (void) output_datatype;
1390 #endif
1391  return result;
1392 }
1393 
1394 PointsWithGhosts::PointsWithGhosts(const Grid &grid, unsigned int stencil_width) {
1395  m_i_first = grid.xs() - stencil_width;
1396  m_i_last = grid.xs() + grid.xm() + stencil_width - 1;
1397  m_j_first = grid.ys() - stencil_width;
1398  m_j_last = grid.ys() + grid.ym() + stencil_width - 1;
1399 
1400  m_i = m_i_first;
1401  m_j = m_j_first;
1402  m_done = false;
1403 }
1404 
1405 namespace grid {
1406 
1407 double radius(const Grid &grid, int i, int j) {
1408  return sqrt(grid.x(i) * grid.x(i) + grid.y(j) * grid.y(j));
1409 }
1410 
1411 } // namespace grid
1412 
1413 } // end of namespace pism
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
std::shared_ptr< const Config > ConstPtr
std::string get_string(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
A class for storing and accessing PISM configuration flags and parameters.
AxisType dimension_type(const std::string &name, units::System::Ptr unit_system) const
Get the "type" of a dimension.
Definition: File.cc:493
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
Definition: File.cc:361
std::string filename() const
Definition: File.cc:307
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition: File.cc:454
std::vector< double > read_dimension(const std::string &name) const
Get dimension data (a coordinate variable).
Definition: File.cc:603
std::vector< std::string > dimensions(const std::string &variable_name) const
Definition: File.cc:425
High-level PISM I/O class.
Definition: File.hh:56
double y0() const
Y-coordinate of the center of the domain.
Definition: Grid.cc:850
const std::vector< double > & x() const
X-coordinates.
Definition: Grid.cc:766
const std::vector< double > & y() const
Y-coordinates.
Definition: Grid.cc:776
unsigned int kBelowHeight(double height) const
Return the index k into zlevels[] so that zlevels[k] <= height < zlevels[k+1] and k < Mz.
Definition: Grid.cc:291
unsigned int Mz() const
Number of vertical levels.
Definition: Grid.cc:761
double cell_area() const
Definition: Grid.cc:805
double Ly() const
Half-width of the computational domain.
Definition: Grid.cc:835
static std::shared_ptr< Grid > FromFile(std::shared_ptr< const Context > ctx, const std::string &filename, const std::vector< std::string > &var_names, grid::Registration r)
Create a grid using one of variables in var_names in file.
Definition: Grid.cc:255
int ys() const
Global starting index of this processor's subset.
Definition: Grid.cc:736
Grid(std::shared_ptr< const Context > context, const grid::Parameters &p)
Create a PISM distributed computational grid.
Definition: Grid.cc:162
grid::Periodicity periodicity() const
Return grid periodicity.
Definition: Grid.cc:674
int max_patch_size() const
Definition: Grid.cc:857
double Lx() const
Half-width of the computational domain.
Definition: Grid.cc:830
~Grid()
Definition: Grid.cc:274
double dx() const
Horizontal grid spacing.
Definition: Grid.cc:796
const std::vector< double > & z() const
Z-coordinates within the ice.
Definition: Grid.cc:786
int rank() const
MPI rank.
Definition: Grid.cc:711
static std::shared_ptr< Grid > Shallow(std::shared_ptr< const Context > ctx, double Lx, double Ly, double x0, double y0, unsigned int Mx, unsigned int My, grid::Registration r, grid::Periodicity p)
Initialize a uniform, shallow (3 z-levels) grid with half-widths (Lx,Ly) and Mx by My nodes.
Definition: Grid.cc:134
Vars & variables()
Dictionary of variables (2D and 3D fields) associated with this grid.
Definition: Grid.cc:721
std::shared_ptr< petsc::DM > get_dm(unsigned int dm_dof, unsigned int stencil_width) const
Get a PETSc DM ("distributed array manager") object for given dof (number of degrees of freedom per g...
Definition: Grid.cc:653
unsigned int My() const
Total grid size in the Y direction.
Definition: Grid.cc:756
void compute_point_neighbors(double X, double Y, int &i_left, int &i_right, int &j_bottom, int &j_top) const
Computes indices of grid points to the lower left and upper right from (X,Y).
Definition: Grid.cc:601
int pio_io_decomposition(int dof, int output_datatype) const
Definition: Grid.cc:1361
void report_parameters() const
Report grid parameters.
Definition: Grid.cc:524
std::vector< double > interpolation_weights(double x, double y) const
Compute 4 interpolation weights necessary for linear interpolation from the current grid....
Definition: Grid.cc:631
double x0() const
X-coordinate of the center of the domain.
Definition: Grid.cc:845
grid::Registration registration() const
Definition: Grid.cc:678
Impl * m_impl
Definition: Grid.hh:367
std::shared_ptr< const Context > ctx() const
Return execution context this grid corresponds to.
Definition: Grid.cc:683
unsigned int Mx() const
Total grid size in the X direction.
Definition: Grid.cc:751
const MappingInfo & get_mapping_info() const
Definition: Grid.cc:1346
unsigned int size() const
MPI communicator size.
Definition: Grid.cc:716
std::vector< int > point_neighbors(double X, double Y) const
Definition: Grid.cc:622
const MPI_Comm com
Definition: Grid.hh:355
double Lz() const
Height of the computational domain.
Definition: Grid.cc:840
double dz_min() const
Minimum vertical spacing.
Definition: Grid.cc:810
double dz_max() const
Maximum vertical spacing.
Definition: Grid.cc:820
double dy() const
Horizontal grid spacing.
Definition: Grid.cc:801
int xs() const
Global starting index of this processor's subset.
Definition: Grid.cc:731
void set_mapping_info(const MappingInfo &info)
Definition: Grid.cc:1350
static std::shared_ptr< Grid > FromOptions(std::shared_ptr< const Context > ctx)
Create a grid using command-line options and (possibly) an input file.
Definition: Grid.cc:1263
int xm() const
Width of this processor's sub-domain.
Definition: Grid.cc:741
int ym() const
Width of this processor's sub-domain.
Definition: Grid.cc:746
Describes the PISM grid and the distribution of data across processors.
Definition: Grid.hh:282
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
PointsWithGhosts(const Grid &grid, unsigned int stencil_width=1)
Definition: Grid.cc:1394
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
A class for passing PISM variables from the core to other parts of the code (such as climate couplers...
Definition: Vars.hh:40
std::vector< double > y
y coordinates
Definition: Grid.hh:88
double Lx
domain half-width
Definition: Grid.hh:77
double Ly
domain half-height
Definition: Grid.hh:79
double y0
y-coordinate of the domain center
Definition: Grid.hh:75
double x0
x-coordinate of the domain center
Definition: Grid.hh:73
std::vector< double > z
z coordinates
Definition: Grid.hh:90
std::vector< double > x
x coordinates
Definition: Grid.hh:86
Contains parameters of an input file grid.
Definition: Grid.hh:65
Periodicity periodicity
Grid periodicity.
Definition: Grid.hh:156
std::vector< double > z
Vertical levels.
Definition: Grid.hh:158
void horizontal_extent_from_options(std::shared_ptr< units::System > unit_system)
Process -Lx, -Ly, -x0, -y0, -x_range, -y_range; set Lx, Ly, x0, y0.
Definition: Grid.cc:1182
void vertical_grid_from_options(std::shared_ptr< const Config > config)
Process -Mz and -Lz; set z;.
Definition: Grid.cc:1209
double y0
Domain center in the Y direction.
Definition: Grid.hh:148
double x0
Domain center in the X direction.
Definition: Grid.hh:146
std::vector< unsigned int > procs_y
Processor ownership ranges in the Y direction.
Definition: Grid.hh:162
void ownership_ranges_from_options(unsigned int size)
Re-compute ownership ranges. Uses current values of Mx and My.
Definition: Grid.cc:1142
std::vector< unsigned int > procs_x
Processor ownership ranges in the X direction.
Definition: Grid.hh:160
Registration registration
Grid registration.
Definition: Grid.hh:154
void validate() const
Validate data members.
Definition: Grid.cc:1218
unsigned int Mx
Number of grid points in the X direction.
Definition: Grid.hh:150
double Ly
Domain half-width in the Y direction.
Definition: Grid.hh:144
double Lx
Domain half-width in the X direction.
Definition: Grid.hh:142
unsigned int My
Number of grid points in the Y direction.
Definition: Grid.hh:152
void horizontal_size_from_options()
Process -Mx and -My; set Mx and My.
Definition: Grid.cc:1177
Grid parameters; used to collect defaults before an Grid is allocated.
Definition: Grid.hh:117
std::shared_ptr< System > Ptr
Definition: Units.hh:47
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
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
VerticalSpacing
Definition: Grid.hh:50
@ EQUAL
Definition: Grid.hh:50
@ QUADRATIC
Definition: Grid.hh:50
std::string periodicity_to_string(Periodicity p)
Convert Periodicity to a STL string.
Definition: Grid.cc:947
VerticalSpacing string_to_spacing(const std::string &keyword)
Convert an STL string to SpacingType.
Definition: Grid.cc:962
Periodicity
Definition: Grid.hh:51
@ XY_PERIODIC
Definition: Grid.hh:51
@ Y_PERIODIC
Definition: Grid.hh:51
@ X_PERIODIC
Definition: Grid.hh:51
@ NOT_PERIODIC
Definition: Grid.hh:51
Registration string_to_registration(const std::string &keyword)
Definition: Grid.cc:986
Registration
Definition: Grid.hh:53
@ CELL_CENTER
Definition: Grid.hh:53
@ CELL_CORNER
Definition: Grid.hh:53
Periodicity string_to_periodicity(const std::string &keyword)
Convert a string to Periodicity.
Definition: Grid.cc:925
std::string spacing_to_string(VerticalSpacing s)
Convert SpacingType to an STL string.
Definition: Grid.cc:976
std::string registration_to_string(Registration registration)
Definition: Grid.cc:999
std::vector< double > compute_vertical_levels(double new_Lz, unsigned int new_Mz, grid::VerticalSpacing spacing, double lambda)
Set the vertical levels in the ice according to values in Mz (number of levels), Lz (domain height),...
Definition: Grid.cc:879
@ PISM_NETCDF3
Definition: IO_Flags.hh:57
@ PISM_READONLY
open an existing file for reading only
Definition: IO_Flags.hh:72
static std::vector< AxisType > dimension_types(const File &file, const std::string &var_name, std::shared_ptr< units::System > unit_system)
Definition: io_helpers.cc:385
bool is_increasing(const std::vector< double > &a)
Checks if a vector of doubles is strictly increasing.
AxisType
Definition: IO_Flags.hh:33
@ UNKNOWN_AXIS
Definition: IO_Flags.hh:33
@ T_AXIS
Definition: IO_Flags.hh:33
@ X_AXIS
Definition: IO_Flags.hh:33
@ Z_AXIS
Definition: IO_Flags.hh:33
@ Y_AXIS
Definition: IO_Flags.hh:33
static OwnershipRanges compute_ownership_ranges(unsigned int Mx, unsigned int My, unsigned int size)
Definition: Grid.cc:391
static void compute_nprocs(unsigned int Mx, unsigned int My, unsigned int size, unsigned int &Nx, unsigned int &Ny)
Computes the number of processors in the X- and Y-directions.
Definition: Grid.cc:312
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
static std::vector< double > compute_coordinates(unsigned int M, double delta, double v_min, double v_max, bool cell_centered)
Compute grid coordinates for one direction (X or Y).
Definition: Grid.cc:468
static double compute_horizontal_spacing(double half_width, unsigned int M, bool cell_centered)
Compute horizontal grid spacing. See compute_horizontal_coordinates() for more.
Definition: Grid.cc:459
static const double k
Definition: exactTestP.cc:42
double vector_min(const std::vector< double > &input)
static std::shared_ptr< Grid > Grid_FromFile(std::shared_ptr< const Context > ctx, const File &file, const std::string &var_name, grid::Registration r)
Create a grid from a file, get information from variable var_name.
Definition: Grid.cc:223
static std::vector< unsigned int > ownership_ranges(unsigned int Mx, unsigned int Nx)
Computes processor ownership ranges corresponding to equal area distribution among processors.
Definition: Grid.cc:357
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.
double vector_max(const std::vector< double > &input)
Vars variables
A dictionary with pointers to array::Arrays, for passing them from the one component to another (e....
Definition: Grid.cc:117
MappingInfo mapping_info
Definition: Grid.cc:61
double cell_area
cell area (meters^2)
Definition: Grid.cc:89
void set_ownership_ranges(const std::vector< unsigned int > &procs_x, const std::vector< unsigned int > &procs_y)
Set processor ownership ranges. Takes care of type conversion (unsigned int -> PetscInt).
Definition: Grid.cc:368
std::shared_ptr< petsc::DM > create_dm(int da_dof, int stencil_width) const
Create a DM with the given number of dof (degrees of freedom per grid point) and stencil width.
Definition: Grid.cc:689
std::vector< PetscInt > procs_y
array containing lenghts (in the y-direction) of processor sub-domains
Definition: Grid.cc:70
std::map< std::array< int, 2 >, int > io_decompositions
ParallelIO I/O decompositions.
Definition: Grid.cc:123
std::shared_ptr< const Context > ctx
Definition: Grid.cc:59
std::vector< double > z
vertical grid levels in the ice; correspond to the storage grid
Definition: Grid.cc:81
double dx
horizontal grid spacing
Definition: Grid.cc:85
unsigned int Mx
number of grid points in the x-direction
Definition: Grid.cc:91
std::shared_ptr< petsc::DM > dm_scalar_global
Definition: Grid.cc:112
std::vector< double > y
y-coordinates of grid points
Definition: Grid.cc:79
std::vector< PetscInt > procs_x
array containing lenghts (in the x-direction) of processor sub-domains
Definition: Grid.cc:68
grid::Periodicity periodicity
Definition: Grid.cc:72
int max_patch_size
Definition: Grid.cc:95
void compute_horizontal_coordinates()
Compute horizontal spacing parameters dx and dy and grid coordinates using Mx, My,...
Definition: Grid.cc:504
std::map< std::array< unsigned int, 2 >, std::weak_ptr< petsc::DM > > dms
Definition: Grid.cc:107
double x0
x-coordinate of the grid center
Definition: Grid.cc:98
double dy
horizontal grid spacing
Definition: Grid.cc:87
grid::Registration registration
Definition: Grid.cc:74
double Ly
half width of the ice model grid in y-direction (m)
Definition: Grid.cc:105
unsigned int My
number of grid points in the y-direction
Definition: Grid.cc:93
double Lx
half width of the ice model grid in x-direction (m)
Definition: Grid.cc:103
std::vector< double > x
x-coordinates of grid points
Definition: Grid.cc:77
Impl(std::shared_ptr< const Context > context)
Definition: Grid.cc:126
double y0
y-coordinate of the grid center
Definition: Grid.cc:100
gsl_interp_accel * bsearch_accel
GSL binary search accelerator used to speed up kBelowHeight().
Definition: Grid.cc:120
Internal structures of Grid.
Definition: Grid.cc:50
std::vector< unsigned int > y
Definition: Grid.cc:386
std::vector< unsigned int > x
Definition: Grid.cc:386
const double radius
Definition: test_cube.c:18
int count
Definition: test_cube.c:16