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