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