PISM, A Parallel Ice Sheet Model  stable v2.1.1 committed by Constantine Khrulev on 2024-12-04 13:36:58 -0900
GeometryEvolution.cc
Go to the documentation of this file.
1 /* Copyright (C) 2016--2023 PISM Authors
2  *
3  * This file is part of PISM.
4  *
5  * PISM is free software; you can redistribute it and/or modify it under the
6  * terms of the GNU General Public License as published by the Free Software
7  * Foundation; either version 3 of the License, or (at your option) any later
8  * version.
9  *
10  * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with PISM; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 #include "pism/geometry/GeometryEvolution.hh"
21 
22 #include "pism/util/Grid.hh"
23 #include "pism/util/Mask.hh"
24 #include "pism/util/array/CellType.hh"
25 #include "pism/util/array/Scalar.hh"
26 #include "pism/util/array/Staggered.hh"
27 #include "pism/util/array/Vector.hh"
28 
29 #include "pism/geometry/part_grid_threshold_thickness.hh"
30 #include "pism/util/Context.hh"
31 #include "pism/util/Logger.hh"
32 #include "pism/util/Profiling.hh"
33 #include "pism/util/interpolation.hh"
34 #include "pism/util/pism_utilities.hh"
35 
36 #include "pism/geometry/flux_limiter.hh"
37 
38 namespace pism {
39 
40 using mask::floating_ice;
41 using mask::grounded_ice;
42 using mask::ice_free;
45 using mask::icy;
46 
48  Impl(std::shared_ptr<const Grid> g);
49 
51 
52  double ice_density;
53 
54  //! True if the basal melt rate contributes to geometry evolution.
55  bool use_bmr;
56 
57  //! True if the part-grid scheme is enabled.
59 
60  //! Flux divergence (used to track thickness changes due to flow).
62 
63  //! Conservation error due to enforcing non-negativity of ice thickness and enforcing
64  //! thickness BC.
66 
67  //! Effective surface mass balance.
69 
70  //! Effective basal mass balance.
72 
73  //! Change in ice thickness due to flow during the last time step.
75 
76  //! Change in the ice area-specific volume due to flow during the last time step.
78 
79  //! Flux through cell interfaces. Ghosted.
81 
82  // Work space
83  array::Vector1 input_velocity; // a ghosted copy; not modified
84  array::Scalar1 bed_elevation; // a copy; not modified
85  array::Scalar1 sea_level; // a copy; not modified
86  array::Scalar1 ice_thickness; // updated in place
87  array::Scalar1 area_specific_volume; // updated in place
88  array::Scalar1 surface_elevation; // updated to maintain consistency
89  array::CellType1 cell_type; // updated to maintain consistency
90  array::Scalar1 residual; // temporary storage
91  array::Scalar1 thickness; // temporary storage
92 };
93 
94 GeometryEvolution::Impl::Impl(std::shared_ptr<const Grid> grid)
95  : gc(*grid->ctx()->config()),
96  flux_divergence(grid, "flux_divergence"),
97  conservation_error(grid, "conservation_error"),
98  effective_SMB(grid, "effective_SMB"),
99  effective_BMB(grid, "effective_BMB"),
100  thickness_change(grid, "thickness_change"),
101  ice_area_specific_volume_change(grid, "ice_area_specific_volume_change"),
102  flux_staggered(grid, "flux_staggered"),
103  input_velocity(grid, "input_velocity"),
104  bed_elevation(grid, "bed_elevation"),
105  sea_level(grid, "sea_level"),
106  ice_thickness(grid, "ice_thickness"),
107  area_specific_volume(grid, "area_specific_volume"),
108  surface_elevation(grid, "surface_elevation"),
109  cell_type(grid, "cell_type"),
110  residual(grid, "residual"),
111  thickness(grid, "thickness") {
112 
113  Config::ConstPtr config = grid->ctx()->config();
114 
115  gc.set_icefree_thickness(config->get_number("geometry.ice_free_thickness_standard"));
116 
117  // constants
118  {
119  ice_density = config->get_number("constants.ice.density");
120  use_bmr = config->get_flag("geometry.update.use_basal_melt_rate");
121  use_part_grid = config->get_flag("geometry.part_grid.enabled");
122  }
123 
124  // reported quantities
125  {
126  // This is the only reported field that is ghosted (we need ghosts to compute flux divergence).
128  .long_name("fluxes through cell interfaces (sides) on the staggered grid (x-offset)")
129  .units("m2 s-1")
130  .output_units("m2 year-1");
132  .long_name("fluxes through cell interfaces (sides) on the staggered grid (y-offset)")
133  .units("m2 s-1")
134  .output_units("m2 year-1");
135 
137  .long_name("flux divergence")
138  .units("m s-1")
139  .output_units("m year-1");
140 
142  .long_name(
143  "conservation error due to enforcing non-negativity of ice thickness (over the last time step)")
144  .units("meters");
145 
147  .long_name("effective surface mass balance over the last time step")
148  .units("meters");
149 
151  .long_name("effective basal mass balance over the last time step")
152  .units("meters");
153 
154  thickness_change.metadata(0).long_name("change in thickness due to flow").units("meters");
155 
157  .long_name("change in area-specific volume due to flow")
158  .units("meters3 / meters2");
159  }
160 
161  // internal storage
162  {
164  .long_name("ghosted copy of the input velocity")
165  .units("meters / second");
166 
167  bed_elevation.metadata(0).long_name("ghosted copy of the bed elevation").units("meters");
168 
169  sea_level.metadata(0).long_name("ghosted copy of the sea level elevation").units("meters");
170 
172  .long_name("working (ghosted) copy of the ice thickness")
173  .units("meters");
174 
176  .long_name("working (ghosted) copy of the area specific volume")
177  .units("meters3 / meters2");
178 
180  .long_name("working (ghosted) copy of the surface elevation")
181  .units("meters");
182 
183  cell_type.metadata(0).long_name("working (ghosted) copy of the cell type mask");
184 
185  residual.metadata(0).long_name("residual area specific volume").units("meters3 / meters2");
186 
187  thickness.metadata(0).long_name("thickness (temporary storage)").units("meters");
188  }
189 }
190 
191 GeometryEvolution::GeometryEvolution(std::shared_ptr<const Grid> grid) : Component(grid) {
192  m_impl = new Impl(grid);
193 }
194 
196  delete m_impl;
197 }
198 
200  this->init_impl(opts);
201 }
202 
204  (void)opts;
205  // empty: the default implementation has no state
206 }
207 
210 }
211 
213  return m_impl->flux_divergence;
214 }
215 
217  return m_impl->flux_staggered;
218 }
219 
221  return m_impl->effective_SMB;
222 }
223 
225  return m_impl->effective_BMB;
226 }
227 
229  return m_impl->thickness_change;
230 }
231 
234 }
235 
237  return m_impl->conservation_error;
238 }
239 
240 /*!
241  * @param[in] geometry ice geometry
242  * @param[in] dt time step, seconds
243  * @param[in] advective_velocity advective (SSA) velocity
244  * @param[in] diffusive_flux diffusive (SIA) flux
245  * @param[in] velocity_bc_values advective velocity Dirichlet B.C. values
246  * @param[in] thickness_bc_mask ice thickness Dirichlet B.C. mask
247  *
248  * Results are stored in internal fields accessible using getters.
249  */
250 void GeometryEvolution::flow_step(const Geometry &geometry, double dt,
251  const array::Vector &advective_velocity,
252  const array::Staggered &diffusive_flux,
253  const array::Scalar &thickness_bc_mask) {
254 
255  profiling().begin("ge.update_ghosted_copies");
256  {
257  // make ghosted copies of input fields
262  m_impl->input_velocity.copy_from(advective_velocity);
263 
264  // Compute cell_type and surface_elevation. Ghosts of results are updated.
265  m_impl->gc.compute(m_impl->sea_level, // in (uses ghosts)
266  m_impl->bed_elevation, // in (uses ghosts)
267  m_impl->ice_thickness, // in (uses ghosts)
268  m_impl->cell_type, // out (ghosts are updated)
269  m_impl->surface_elevation); // out (ghosts are updated)
270  }
271  profiling().end("ge.update_ghosted_copies");
272 
273  // Derived classes can include modifications for regional runs.
274  profiling().begin("ge.interface_fluxes");
275  compute_interface_fluxes(m_impl->cell_type, // in (uses ghosts)
276  m_impl->ice_thickness, // in (uses ghosts)
277  m_impl->input_velocity, // in (uses ghosts)
278  diffusive_flux, // in
279  m_impl->flux_staggered); // out
280  profiling().end("ge.interface_fluxes");
281 
283 
284  {
285  // allocate temporary storage (FIXME: at some point I should evaluate whether it's OK
286  // to allocate this every time step)
287  array::Staggered flux_limited(m_grid, "limited_ice_flux");
288 
290  m_impl->ice_thickness, // in (uses ghosts)
291  m_impl->flux_staggered, // in (uses ghosts)
292  flux_limited);
293 
294  m_impl->flux_staggered.copy_from(flux_limited);
295  }
296 
297  profiling().begin("ge.flux_divergence");
298  compute_flux_divergence(dt, // in
299  m_impl->flux_staggered, // in (uses ghosts)
300  thickness_bc_mask, // in
301  m_impl->conservation_error, // in/out
302  m_impl->flux_divergence); // out
303  profiling().end("ge.flux_divergence");
304 
305  // This is where part_grid is implemented.
306  profiling().begin("ge.update_in_place");
307  update_in_place(dt, // in
308  m_impl->bed_elevation, // in
309  m_impl->sea_level, // in
310  m_impl->flux_divergence, // in
311  m_impl->ice_thickness, // in/out
312  m_impl->area_specific_volume); // in/out
313  profiling().end("ge.update_in_place");
314 
315  // Compute ice thickness and area specific volume changes.
316  profiling().begin("ge.compute_changes");
317  {
321  }
322  profiling().end("ge.compute_changes");
323 
324  // Computes the numerical conservation error and corrects ice_thickness_change and
325  // ice_area_specific_volume_change. We can do this here because
326  // compute_surface_and_basal_mass_balance() preserves non-negativity.
327  //
328  // Note that here we use the "old" ice geometry.
329  //
330  // This computation is purely local.
331  profiling().begin("ge.ensure_nonnegativity");
332  ensure_nonnegativity(geometry.ice_thickness, // in
333  geometry.ice_area_specific_volume, // in
334  m_impl->thickness_change, // in/out
336  m_impl->conservation_error); // in/out
337  profiling().end("ge.ensure_nonnegativity");
338 
339  // Now the caller can compute
340  //
341  // H_new = H_old + thickness_change
342  // Href_new = Href_old + ice_area_specific_volume_change.
343 
344  // calving is a separate issue
345 }
346 
347 void GeometryEvolution::source_term_step(const Geometry &geometry, double dt,
348  const array::Scalar &thickness_bc_mask,
349  const array::Scalar &surface_mass_balance_rate,
350  const array::Scalar &basal_melt_rate) {
351 
352  profiling().begin("ge.source_terms");
354  thickness_bc_mask, // in
355  geometry.ice_thickness, // in
356  geometry.cell_type, // in
357  surface_mass_balance_rate, // in
358  basal_melt_rate, // in
359  m_impl->effective_SMB, // out
360  m_impl->effective_BMB); // out
361  profiling().end("ge.source_terms");
362 }
363 
364 /*!
365  * Apply changes due to flow to ice geometry and ice area specific volume.
366  */
368  geometry.ice_thickness.add(1.0, m_impl->thickness_change);
370 }
371 
372 /*!
373  * Update geometry by applying changes due to surface and basal mass fluxes.
374  *
375  * Note: This method performs these changes in the same order as the code ensuring
376  * non-negativity. This is important.
377  */
379 
381  array::Scalar &H = geometry.ice_thickness;
382 
383  array::AccessScope list{ &H, &dH_SMB, &dH_BMB };
384  ParallelSection loop(m_grid->com);
385  try {
386  for (auto p = m_grid->points(); p; p.next()) {
387  const int i = p.i(), j = p.j();
388 
389  // To preserve non-negativity of thickness we need to apply changes in this exact order.
390  // (Recall that floating-point arithmetic is not associative.)
391  const double H_new = (H(i, j) + dH_SMB(i, j)) + dH_BMB(i, j);
392 
393 #if (Pism_DEBUG == 1)
394  if (H_new < 0.0) {
395  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "H = %f (negative) at i=%d, j=%d", H_new,
396  i, j);
397  }
398 #endif
399 
400  H(i, j) = H_new;
401  }
402  } catch (...) {
403  loop.failed();
404  }
405  loop.check();
406 }
407 
408 
409 /*!
410  * Prevent advective ice flow from floating ice to ice-free land, as well as in the
411  * ice-free areas.
412  *
413  * Note: positive `input` corresponds to the flux from `current` to `neighbor`.
414  */
415 static double limit_advective_flux(int current, int neighbor, double input) {
416 
417  // No flow from ice-free ocean:
418  if ((ice_free_ocean(current) and input > 0.0) or (ice_free_ocean(neighbor) and input < 0.0)) {
419  return 0.0;
420  }
421 
422  // No flow from ice-free land:
423  if ((ice_free_land(current) and input > 0.0) or (ice_free_land(neighbor) and input < 0.0)) {
424  return 0.0;
425  }
426 
427  // Case 1: Flow between grounded_ice and grounded_ice.
428  if (grounded_ice(current) and grounded_ice(neighbor)) {
429  return input;
430  }
431 
432  // Cases 2 and 3: Flow between grounded_ice and floating_ice.
433  if ((grounded_ice(current) and floating_ice(neighbor)) or
434  (floating_ice(current) and grounded_ice(neighbor))) {
435  return input;
436  }
437 
438  // Cases 4 and 5: Flow between grounded_ice and ice_free_land.
439  if ((grounded_ice(current) and ice_free_land(neighbor)) or
440  (ice_free_land(current) and grounded_ice(neighbor))) {
441  return input;
442  }
443 
444  // Cases 6 and 7: Flow between grounded_ice and ice_free_ocean.
445  if ((grounded_ice(current) and ice_free_ocean(neighbor)) or
446  (ice_free_ocean(current) and grounded_ice(neighbor))) {
447  return input;
448  }
449 
450  // Case 8: Flow between floating_ice and floating_ice.
451  if (floating_ice(current) and floating_ice(neighbor)) {
452  return input;
453  }
454 
455  // Cases 9 and 10: Flow between floating_ice and ice_free_land.
456  if ((floating_ice(current) and ice_free_land(neighbor)) or
457  (ice_free_land(current) and floating_ice(neighbor))) {
458  // Disable all flow. This ensures that an ice shelf does not climb up a cliff.
459  return 0.0;
460  }
461 
462  // Cases 11 and 12: Flow between floating_ice and ice_free_ocean.
463  if ((floating_ice(current) and ice_free_ocean(neighbor)) or
464  (ice_free_ocean(current) and floating_ice(neighbor))) {
465  return input;
466  }
467 
468  // Case 13: Flow between ice_free_land and ice_free_land.
469  if (ice_free_land(current) and ice_free_land(neighbor)) {
470  return 0.0;
471  }
472 
473  // Cases 14 and 15: Flow between ice_free_land and ice_free_ocean.
474  if ((ice_free_land(current) and ice_free_ocean(neighbor)) or
475  (ice_free_ocean(current) and ice_free_land(neighbor))) {
476  return 0.0;
477  }
478 
479  // Case 16: Flow between ice_free_ocean and ice_free_ocean.
480  if (ice_free_ocean(current) and ice_free_ocean(neighbor)) {
481  return 0.0;
482  }
483 
485  PISM_ERROR_LOCATION, "cannot handle the case current=%d, neighbor=%d", current, neighbor);
486 }
487 
488 /*!
489  * Prevent SIA-driven flow in ice shelves and ice-free areas.
490  *
491  * Note: positive `flux` corresponds to the flux from `current` to `neighbor`.
492  */
493 static double limit_diffusive_flux(int current, int neighbor, double flux) {
494 
495  // No flow from ice-free ocean:
496  if ((ice_free_ocean(current) and flux > 0.0) or (ice_free_ocean(neighbor) and flux < 0.0)) {
497  return 0.0;
498  }
499 
500  // No flow from ice-free land:
501  if ((ice_free_land(current) and flux > 0.0) or (ice_free_land(neighbor) and flux < 0.0)) {
502  return 0.0;
503  }
504 
505  // Case 1: Flow between grounded_ice and grounded_ice.
506  if (grounded_ice(current) and grounded_ice(neighbor)) {
507  return flux;
508  }
509 
510  // Cases 2 and 3: Flow between grounded_ice and floating_ice.
511  if ((grounded_ice(current) and floating_ice(neighbor)) or
512  (floating_ice(current) and grounded_ice(neighbor))) {
513  return flux;
514  }
515 
516  // Cases 4 and 5: Flow between grounded_ice and ice_free_land.
517  if ((grounded_ice(current) and ice_free_land(neighbor)) or
518  (ice_free_land(current) and grounded_ice(neighbor))) {
519  return flux;
520  }
521 
522  // Cases 6 and 7: Flow between grounded_ice and ice_free_ocean.
523  if ((grounded_ice(current) and ice_free_ocean(neighbor)) or
524  (ice_free_ocean(current) and grounded_ice(neighbor))) {
525  return flux;
526  }
527 
528  // Case 8: Flow between floating_ice and floating_ice.
529  if (floating_ice(current) and floating_ice(neighbor)) {
530  // no diffusive flux in ice shelves
531  return 0.0;
532  }
533 
534  // Cases 9 and 10: Flow between floating_ice and ice_free_land.
535  if ((floating_ice(current) and ice_free_land(neighbor)) or
536  (ice_free_land(current) and floating_ice(neighbor))) {
537  // Disable all flow. This ensures that an ice shelf does not climb up a cliff.
538  return 0.0;
539  }
540 
541  // Cases 11 and 12: Flow between floating_ice and ice_free_ocean.
542  if ((floating_ice(current) and ice_free_ocean(neighbor)) or
543  (ice_free_ocean(current) and floating_ice(neighbor))) {
544  return 0.0;
545  }
546 
547  // Case 13: Flow between ice_free_land and ice_free_land.
548  if (ice_free_land(current) and ice_free_land(neighbor)) {
549  return 0.0;
550  }
551 
552  // Cases 14 and 15: Flow between ice_free_land and ice_free_ocean.
553  if ((ice_free_land(current) and ice_free_ocean(neighbor)) or
554  (ice_free_ocean(current) and ice_free_land(neighbor))) {
555  return 0.0;
556  }
557 
558  // Case 16: Flow between ice_free_ocean and ice_free_ocean.
559  if (ice_free_ocean(current) and ice_free_ocean(neighbor)) {
560  return 0.0;
561  }
562 
564  PISM_ERROR_LOCATION, "cannot handle the case current=%d, neighbor=%d", current, neighbor);
565 }
566 
567 /*!
568  * Combine advective velocity and the diffusive flux on the staggered grid with the ice thickness to
569  * compute the total flux through cell interfaces.
570  *
571  * Uses first-order upwinding to compute the advective flux.
572  *
573  * Limits the diffusive flux to prevent SIA-driven flow in the ocean and ice-free areas.
574  */
576  const array::Scalar &ice_thickness,
577  const array::Vector &velocity,
578  const array::Staggered &diffusive_flux,
579  array::Staggered &output) {
580 
581  array::AccessScope list{ &cell_type, &velocity, &ice_thickness, &diffusive_flux, &output };
582 
583  ParallelSection loop(m_grid->com);
584  try {
585  // compute advective fluxes and put them in output
586  for (auto p = m_grid->points(); p; p.next()) {
587  const int i = p.i(), j = p.j(), M = cell_type(i, j);
588 
589  const double H = ice_thickness(i, j);
590  const Vector2d V = velocity(i, j);
591 
592  for (int n = 0; n < 2; ++n) {
593  const int oi = 1 - n, // offset in the i direction
594  oj = n, // offset in the j direction
595  i_n = i + oi, // i index of a neighbor
596  j_n = j + oj; // j index of a neighbor
597 
598  const int M_n = cell_type(i_n, j_n);
599 
600  // advective velocity at the current interface
601  double v = 0.0;
602  {
603  Vector2d V_n = velocity(i_n, j_n);
604  int W = icy(M), W_n = icy(M_n);
605 
606  auto v_staggered = (W * V + W_n * V_n) / std::max(W + W_n, 1);
607  v = n == 0 ? v_staggered.u : v_staggered.v;
608  }
609 
610  // advective flux
611  const double H_n = ice_thickness(i_n, j_n),
612  Q_advective = v * (v > 0.0 ? H : H_n); // first order upwinding
613 
614  output(i, j, n) = Q_advective;
615  } // end of the loop over neighbors (n)
616  }
617 
618  // limit the advective flux and add the diffusive flux to it to get the total
619  for (auto p = m_grid->points(); p; p.next()) {
620  const int i = p.i(), j = p.j(), M = cell_type(i, j);
621 
622  for (int n = 0; n < 2; ++n) {
623  const int oi = 1 - n, // offset in the i direction
624  oj = n, // offset in the j direction
625  i_n = i + oi, // i index of a neighbor
626  j_n = j + oj; // j index of a neighbor
627 
628  const int M_n = cell_type(i_n, j_n);
629 
630  // diffusive flux
631  const double Q_diffusive = limit_diffusive_flux(M, M_n, diffusive_flux(i, j, n)),
632  Q_advective = limit_advective_flux(M, M_n, output(i, j, n));
633 
634  output(i, j, n) = Q_diffusive + Q_advective;
635  } // end of the loop over n
636  }
637 
638  } catch (...) {
639  loop.failed();
640  }
641  loop.check();
642 }
643 
644 /*!
645  * Compute flux divergence using cell interface fluxes on the staggered grid.
646  *
647  * The flux divergence at *ice thickness* Dirichlet B.C. locations is set to zero.
648  */
650  const array::Scalar &thickness_bc_mask,
651  array::Scalar &conservation_error,
652  array::Scalar &output) {
653  const double dx = m_grid->dx(), dy = m_grid->dy();
654 
655  array::AccessScope list{ &flux, &thickness_bc_mask, &conservation_error, &output };
656 
657  ParallelSection loop(m_grid->com);
658  try {
659  for (auto p = m_grid->points(); p; p.next()) {
660  const int i = p.i(), j = p.j();
661 
662  auto Q = flux.star(i, j);
663 
664  double divQ = (Q.e - Q.w) / dx + (Q.n - Q.s) / dy;
665 
666  if (thickness_bc_mask(i, j) > 0.5) {
667  // the thickness change would have been equal to -divQ*dt. By keeping ice
668  // thickness fixed we *add* divQ*dt meters of ice.
669  conservation_error(i, j) += divQ * dt; // units: meters
670  output(i, j) = 0.0;
671  } else {
672  output(i, j) = divQ;
673  }
674  }
675  } catch (...) {
676  loop.failed();
677  }
678  loop.check();
679 }
680 
681 /*!
682  * Update ice thickness and area_specific_volume *in place*.
683  *
684  * It would be better to compute the change in ice thickness and area_specific_volume and then apply
685  * them, but it would require re-writing all the part-grid code from scratch. So, I make copies of
686  * ice thickness and area_specific_volume, use this old code, then compute differences to get changes.
687  * Compute ice thickness changes due to the flow of the ice.
688  *
689  * @param[in] dt time step, seconds
690  * @param[in] bed_elevation bed elevation, meters
691  * @param[in] sea_level sea level elevation
692  * @param[in] ice_thickness ice thickness
693  * @param[in] area_specific_volume area-specific volume (m3/m2)
694  * @param[in] flux_divergence flux divergence
695  * @param[out] thickness_change ice thickness change due to flow
696  * @param[out] area_specific_volume_change area specific volume change due to flow
697  */
698 void GeometryEvolution::update_in_place(double dt, const array::Scalar &bed_topography,
699  const array::Scalar &sea_level,
700  const array::Scalar &flux_divergence,
701  array::Scalar &ice_thickness,
702  array::Scalar &area_specific_volume) {
703 
704  m_impl->gc.compute(sea_level, bed_topography, ice_thickness, m_impl->cell_type,
706 
707  array::AccessScope list{ &ice_thickness, &flux_divergence };
708 
709  if (m_impl->use_part_grid) {
710  m_impl->residual.set(0.0);
711 
712  // Store ice thickness. We need this copy to make sure that modifying ice_thickness in the loop
713  // below does not affect the computation of the threshold thickness. (Note that
714  // part_grid_threshold_thickness uses neighboring values of the mask, ice thickness, and surface
715  // elevation.)
716  m_impl->thickness.copy_from(ice_thickness);
717 
718  list.add({ &area_specific_volume, &m_impl->residual, &m_impl->thickness,
719  &m_impl->surface_elevation, &bed_topography, &m_impl->cell_type });
720  }
721 
722  const double Lz = m_grid->Lz();
723 
724  ParallelSection loop(m_grid->com);
725  try {
726  for (auto p = m_grid->points(); p; p.next()) {
727  const int i = p.i(), j = p.j();
728 
729  double divQ = flux_divergence(i, j);
730 
731  if (m_impl->use_part_grid) {
732  if (m_impl->cell_type.ice_free_ocean(i, j) and m_impl->cell_type.next_to_ice(i, j)) {
733  assert(divQ <= 0.0);
734  // Add the flow contribution to this partially filled cell.
735  area_specific_volume(i, j) += -divQ * dt;
736 
737  double threshold = part_grid_threshold_thickness(
738  m_impl->cell_type.star_int(i, j), m_impl->thickness.star(i, j),
739  m_impl->surface_elevation.star(i, j), bed_topography(i, j));
740 
741  // if threshold is zero, turn all the area specific volume into ice thickness, with zero
742  // residual
743  if (threshold == 0.0) {
744  threshold = area_specific_volume(i, j);
745  }
746 
747  if (area_specific_volume(i, j) >= threshold) {
748  ice_thickness(i, j) += threshold;
749  m_impl->residual(i, j) = area_specific_volume(i, j) - threshold;
750  area_specific_volume(i, j) = 0.0;
751  }
752 
753  // In this case the flux goes into the area_specific_volume variable and does not directly
754  // contribute to ice thickness at this location.
755  divQ = 0.0;
756  }
757  } // end of if (use_part_grid)
758 
759  ice_thickness(i, j) += -dt * divQ;
760 
761  if (ice_thickness(i, j) > Lz) {
763  "ice thickness would exceed Lz at i=%d, j=%d (H=%f, Lz=%f)",
764  i, j, ice_thickness(i, j), Lz);
765  }
766  }
767  } catch (...) {
768  loop.failed();
769  }
770  loop.check();
771 
772  ice_thickness.update_ghosts();
773 
774  // Compute the mask corresponding to the new thickness.
775  m_impl->gc.compute_mask(sea_level, bed_topography, ice_thickness, m_impl->cell_type);
776 
777  /*
778  Redistribute residual ice mass from subgrid-scale parameterization.
779 
780  See [@ref Albrechtetal2011].
781  */
782  if (m_impl->use_part_grid) {
783  const int max_n_iterations = m_config->get_number("geometry.part_grid.max_iterations");
784 
785  bool done = false;
786  for (int i = 0; i < max_n_iterations and not done; ++i) {
787  m_log->message(4, "redistribution iteration %d\n", i);
788 
789  // this call may set done to true
790  residual_redistribution_iteration(bed_topography, sea_level, m_impl->surface_elevation,
791  ice_thickness, m_impl->cell_type, area_specific_volume,
792  m_impl->residual, done);
793  }
794 
795  if (not done) {
796  m_log->message(
797  2,
798  "WARNING: not done redistributing mass after %d iterations, remaining residual: %f m^3.\n",
799  max_n_iterations, sum(m_impl->residual) * m_grid->cell_area());
800 
801  // Add residual to ice thickness, preserving total ice mass. (This is not great, but
802  // better than losing mass.)
803  ice_thickness.add(1.0, m_impl->residual);
804  m_impl->residual.set(0.0);
805  }
806 
807  if (max(ice_thickness) > m_grid->Lz()) {
808  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "ice thickness would exceed Lz "
809  "after part_grid residual redistribution");
810  }
811  }
812 }
813 
814 //! @brief Perform one iteration of the residual mass redistribution.
815 /*!
816  @param[in] bed_topography bed elevation
817  @param[in] sea_level sea level elevation
818  @param[in,out] ice_surface_elevation surface elevation; used as temp. storage
819  @param[in,out] ice_thickness ice thickness; updated
820  @param[in,out] cell_type cell type mask; used as temp. storage
821  @param[in,out] area_specific_volume area specific volume; updated
822  @param[in,out] residual ice volume that still needs to be distributed; updated
823  @param[in,out] done result flag: true if this iteration should be the last one
824  */
826  const array::Scalar &sea_level,
827  array::Scalar1 &ice_surface_elevation,
828  array::Scalar &ice_thickness,
829  array::CellType1 &cell_type,
830  array::Scalar &area_specific_volume,
831  array::Scalar &residual, bool &done) {
832 
833  m_impl->gc.compute_mask(sea_level, bed_topography, ice_thickness, cell_type);
834 
835  // First step: distribute residual mass
836  {
837  // will be destroyed at the end of the block
838  array::AccessScope list{ &cell_type, &ice_thickness, &area_specific_volume, &residual };
839 
840  for (auto p = m_grid->points(); p; p.next()) {
841  const int i = p.i(), j = p.j();
842 
843  if (residual(i, j) <= 0.0) {
844  continue;
845  }
846 
847  auto m = cell_type.star(i, j);
848 
849  int N = 0; // number of empty or partially filled neighbors
850  for (auto d : { North, East, South, West }) {
851  N += ice_free_ocean(m[d]) ? 1 : 0;
852  }
853 
854  if (N > 0) {
855  // Remaining ice mass will be redistributed equally among all adjacent
856  // ice-free-ocean cells (is there a more physical way?)
857  residual(i, j) /= N;
858  } else {
859  // Conserve mass, but (possibly) create a "ridge" at the shelf
860  // front
861  ice_thickness(i, j) += residual(i, j);
862  residual(i, j) = 0.0;
863  }
864  }
865 
866  residual.update_ghosts();
867 
868  // update area_specific_volume using adjusted residuals
869  for (auto p = m_grid->points(); p; p.next()) {
870  const int i = p.i(), j = p.j();
871 
872  if (cell_type.ice_free_ocean(i, j)) {
873  area_specific_volume(i, j) +=
874  (residual(i + 1, j) + residual(i - 1, j) + residual(i, j + 1) + residual(i, j - 1));
875  }
876  }
877 
878  residual.set(0.0);
879  }
880 
881  ice_thickness.update_ghosts();
882 
883  // Store ice thickness. We need this copy to make sure that modifying ice_thickness in the loop
884  // below does not affect the computation of the threshold thickness. (Note that
885  // part_grid_threshold_thickness uses neighboring values of the mask, ice thickness, and surface
886  // elevation.)
887  m_impl->thickness.copy_from(ice_thickness);
888 
889  // The loop above updated ice_thickness, so we need to re-calculate the mask and the
890  // surface elevation:
891  m_impl->gc.compute(sea_level, bed_topography, ice_thickness, cell_type, ice_surface_elevation);
892 
893  double remaining_residual = 0.0;
894 
895  // Second step: we need to redistribute residual ice volume if
896  // neighbors which gained redistributed ice also become full.
897  {
898  // will be destroyed at the end of the block
899  array::AccessScope list{ &m_impl->thickness, &ice_thickness, &ice_surface_elevation,
900  &bed_topography, &cell_type };
901 
902  for (auto p = m_grid->points(); p; p.next()) {
903  const int i = p.i(), j = p.j();
904 
905  if (area_specific_volume(i, j) <= 0.0) {
906  continue;
907  }
908 
909  double threshold =
911  ice_surface_elevation.star(i, j), bed_topography(i, j));
912 
913  // if threshold is zero, turn all the area specific volume into ice thickness, with zero
914  // residual
915  if (threshold == 0.0) {
916  threshold = area_specific_volume(i, j);
917  }
918 
919  if (area_specific_volume(i, j) >= threshold) {
920  ice_thickness(i, j) += threshold;
921  residual(i, j) = area_specific_volume(i, j) - threshold;
922  area_specific_volume(i, j) = 0.0;
923 
924  remaining_residual += residual(i, j);
925  }
926  }
927  }
928 
929  // check if redistribution should be run once more
930  remaining_residual = GlobalSum(m_grid->com, remaining_residual);
931 
932  if (remaining_residual > 0.0) {
933  done = false;
934  } else {
935  done = true;
936  }
937 
938  ice_thickness.update_ghosts();
939 }
940 
941 /*!
942  * Correct `thickness_change` and `area_specific_volume_change` so that applying them will not
943  * result in negative `ice_thickness` and `area_specific_volume`.
944  *
945  * Compute the the amount of ice that is added to preserve non-negativity of ice thickness.
946  *
947  * @param[in] ice_thickness ice thickness (m)
948  * @param[in] area_specific_volume area-specific volume (m3/m2)
949  * @param[in,out] thickness_change "proposed" thickness change (m)
950  * @param[in,out] area_specific_volume_change "proposed" area-specific volume change (m3/m2)
951  * @param[in,out] conservation_error computed conservation error (m)
952  *
953  * This computation is purely local.
954  */
956  const array::Scalar &area_specific_volume,
957  array::Scalar &thickness_change,
958  array::Scalar &area_specific_volume_change,
959  array::Scalar &conservation_error) {
960 
961  array::AccessScope list{ &ice_thickness, &area_specific_volume, &thickness_change,
962  &area_specific_volume_change, &conservation_error };
963 
964  ParallelSection loop(m_grid->com);
965  try {
966  for (auto p = m_grid->points(); p; p.next()) {
967  const int i = p.i(), j = p.j();
968 
969  const double H = ice_thickness(i, j), dH = thickness_change(i, j);
970 
971  // applying thickness_change will lead to negative thickness
972  if (H + dH < 0.0) {
973  thickness_change(i, j) = -H;
974  conservation_error(i, j) += -(H + dH);
975  }
976 
977  const double V = area_specific_volume(i, j), dV = area_specific_volume_change(i, j);
978 
979  if (V + dV < 0.0) {
980  area_specific_volume_change(i, j) = -V;
981  conservation_error(i, j) += -(V + dV);
982  }
983  }
984  } catch (...) {
985  loop.failed();
986  }
987  loop.check();
988 }
989 
990 /*!
991  * Given ice thickness `H` and the "proposed" change `dH`, compute the corrected change preserving
992  * non-negativity.
993  */
994 static inline double effective_change(double H, double dH) {
995  if (H + dH <= 0) {
996  return -H;
997  }
998  return dH;
999 }
1000 
1001 /*!
1002  * Compute effective surface and basal mass balance.
1003  *
1004  * @param[in] dt time step, seconds
1005  * @param[in] thickness_bc_mask mask specifying ice thickness Dirichlet B.C. locations
1006  * @param[in] ice_thickness ice thickness, m
1007  * @param[in] thickness_change thickness change due to flow, m
1008  * @param[in] cell_type cell type mask
1009  * @param[in] smb_rate top surface mass balance rate, kg m-2 s-1
1010  * @param[in] basal_melt_rate basal melt rate, m s-1
1011  * @param[out] effective_smb effective top surface mass balance, m
1012  * @param[out] effective_bmb effective basal mass balance, m
1013  *
1014  * This computation is purely local.
1015  *
1016  * Positive outputs correspond to mass gain.
1017  */
1019  double dt, const array::Scalar &thickness_bc_mask, const array::Scalar &ice_thickness,
1020  const array::CellType &cell_type, const array::Scalar &smb_flux,
1021  const array::Scalar &basal_melt_rate, array::Scalar &effective_SMB,
1022  array::Scalar &effective_BMB) {
1023 
1024  array::AccessScope list{ &ice_thickness, &smb_flux, &basal_melt_rate, &cell_type,
1025  &thickness_bc_mask, &effective_SMB, &effective_BMB };
1026 
1027  ParallelSection loop(m_grid->com);
1028  try {
1029  for (auto p = m_grid->points(); p; p.next()) {
1030  const int i = p.i(), j = p.j();
1031 
1032  // Don't modify ice thickness at Dirichlet B.C. locations and in the ice-free ocean.
1033  if (thickness_bc_mask.as_int(i, j) == 1 or cell_type.ice_free_ocean(i, j)) {
1034  effective_SMB(i, j) = 0.0;
1035  effective_BMB(i, j) = 0.0;
1036  continue;
1037  }
1038 
1039  const double H = ice_thickness(i, j);
1040 
1041  // Thickness change due to the surface mass balance
1042  //
1043  // Note that here we convert surface mass balance from [kg m-2 s-1] to [m s-1].
1044  double dH_SMB = effective_change(H, dt * smb_flux(i, j) / m_impl->ice_density);
1045 
1046  // Thickness change due to the basal mass balance
1047  //
1048  // Note that basal_melt_rate is in [m s-1]. Here the negative sign converts the melt rate into
1049  // mass balance.
1050  double dH_BMB =
1051  effective_change(H + dH_SMB, dt * (m_impl->use_bmr ? -basal_melt_rate(i, j) : 0.0));
1052 
1053  effective_SMB(i, j) = dH_SMB;
1054  effective_BMB(i, j) = dH_BMB;
1055  }
1056  } catch (...) {
1057  loop.failed();
1058  }
1059  loop.check();
1060 }
1061 
1062 namespace diagnostics {
1063 
1064 /*! @brief Report the divergence of the ice flux. */
1065 class FluxDivergence : public Diag<GeometryEvolution> {
1066 public:
1068  m_vars = { model->flux_divergence().metadata() };
1069  }
1070 
1071 protected:
1072  std::shared_ptr<array::Array> compute_impl() const {
1073  auto result = allocate<array::Scalar>("flux_divergence");
1074 
1075  result->copy_from(model->flux_divergence());
1076 
1077  return result;
1078  }
1079 };
1080 
1081 /*! @brief Report mass flux on the staggered grid. */
1082 class FluxStaggered : public Diag<GeometryEvolution> {
1083 public:
1086  }
1087 
1088 protected:
1089  std::shared_ptr<array::Array> compute_impl() const {
1090  auto result = allocate<array::Staggered>("flux_staggered");
1091 
1092  const array::Staggered &input = model->flux_staggered();
1093  array::Staggered &output = *result;
1094 
1095  // FIXME: implement array::Staggered::copy_from()
1096 
1097  array::AccessScope list{ &input, &output };
1098 
1099  ParallelSection loop(m_grid->com);
1100  try {
1101  for (auto p = m_grid->points(); p; p.next()) {
1102  const int i = p.i(), j = p.j();
1103 
1104  output(i, j, 0) = input(i, j, 0);
1105  output(i, j, 1) = input(i, j, 1);
1106  }
1107  } catch (...) {
1108  loop.failed();
1109  }
1110  loop.check();
1111 
1112  return result;
1113  }
1114 };
1115 
1116 } // end of namespace diagnostics
1117 
1119  using namespace diagnostics;
1120  typedef Diagnostic::Ptr Ptr;
1121 
1122  std::map<std::string, Ptr> result;
1123  result = {
1124  { "flux_staggered", Ptr(new FluxStaggered(this)) },
1125  { "flux_divergence", Ptr(new FluxDivergence(this)) },
1126  };
1127  return result;
1128 }
1129 
1131  : GeometryEvolution(grid), m_no_model_mask(m_grid, "no_model_mask") {
1132 
1134 
1135  m_no_model_mask.metadata(0).long_name("'no model' mask");
1136 }
1137 
1139  m_no_model_mask.copy_from(mask);
1140 }
1141 
1142 /*!
1143  * Disable ice flow in "no model" areas.
1144  */
1146  const array::Scalar &ice_thickness,
1147  const array::Vector &velocity,
1148  const array::Staggered &diffusive_flux,
1149  array::Staggered &output) {
1150 
1151  GeometryEvolution::compute_interface_fluxes(cell_type, ice_thickness,
1152  velocity, diffusive_flux,
1153  output);
1154 
1155  array::AccessScope list{&m_no_model_mask, &output};
1156 
1157  ParallelSection loop(m_grid->com);
1158  try {
1159  for (auto p = m_grid->points(); p; p.next()) {
1160  const int i = p.i(), j = p.j();
1161 
1162  const int M = m_no_model_mask.as_int(i, j);
1163 
1164  for (int n : {0, 1}) {
1165  const int
1166  oi = 1 - n, // offset in the i direction
1167  oj = n, // offset in the j direction
1168  i_n = i + oi, // i index of a neighbor
1169  j_n = j + oj; // j index of a neighbor
1170 
1171  const int M_n = m_no_model_mask.as_int(i_n, j_n);
1172 
1173  if (not (M == 0 and M_n == 0)) {
1174  output(i, j, n) = 0.0;
1175  }
1176  }
1177  }
1178  } catch (...) {
1179  loop.failed();
1180  }
1181  loop.check();
1182 }
1183 
1184 /*!
1185  * Set surface and basal mass balance to zero in "no model" areas.
1186  */
1188  const array::Scalar &thickness_bc_mask,
1189  const array::Scalar &ice_thickness,
1190  const array::CellType &cell_type,
1191  const array::Scalar &surface_mass_flux,
1192  const array::Scalar &basal_melt_rate,
1193  array::Scalar &effective_SMB,
1194  array::Scalar &effective_BMB) {
1196  thickness_bc_mask,
1197  ice_thickness,
1198  cell_type,
1199  surface_mass_flux,
1200  basal_melt_rate,
1201  effective_SMB,
1202  effective_BMB);
1203 
1204  array::AccessScope list{&m_no_model_mask, &effective_SMB, &effective_BMB};
1205 
1206  ParallelSection loop(m_grid->com);
1207  try {
1208  for (auto p = m_grid->points(); p; p.next()) {
1209  const int i = p.i(), j = p.j();
1210 
1211  if (m_no_model_mask(i, j) > 0.5) {
1212  effective_SMB(i, j) = 0.0;
1213  effective_BMB(i, j) = 0.0;
1214  }
1215  }
1216  } catch (...) {
1217  loop.failed();
1218  }
1219  loop.check();
1220 }
1221 
1223  this->set_no_model_mask_impl(mask);
1224 }
1225 
1227  (void) mask;
1228  // the default implementation is a no-op
1229 }
1230 
1232  const array::Staggered1 &flux,
1233  double dt,
1234  bool add_values,
1235  array::Scalar &output) {
1236 
1237  using mask::grounded;
1238 
1239  auto grid = output.grid();
1240 
1241  const double
1242  dx = grid->dx(),
1243  dy = grid->dy();
1244 
1245  auto cell_area = grid->cell_area();
1246 
1247  auto ice_density = grid->ctx()->config()->get_number("constants.ice.density");
1248 
1249  array::AccessScope list{&cell_type, &flux, &output};
1250 
1251  ParallelSection loop(grid->com);
1252  try {
1253  for (auto p = grid->points(); p; p.next()) {
1254  const int i = p.i(), j = p.j();
1255 
1256  double result = 0.0;
1257 
1258  if (cell_type.ocean(i ,j)) {
1259  auto M = cell_type.star(i, j);
1260  auto Q = flux.star(i, j);
1261 
1262  if (grounded(M.n)) {
1263  result += Q.n * dx;
1264  }
1265 
1266  if (grounded(M.e)) {
1267  result += Q.e * dy;
1268  }
1269 
1270  if (grounded(M.s)) {
1271  result -= Q.s * dx;
1272  }
1273 
1274  if (grounded(M.w)) {
1275  result -= Q.w * dy;
1276  }
1277 
1278  // convert from "m^3 / s" to "kg / m^2"
1279  result *= dt * (ice_density / cell_area);
1280  }
1281 
1282  if (add_values) {
1283  output(i, j) += result;
1284  } else {
1285  output(i, j) = result;
1286  }
1287  }
1288  } catch (...) {
1289  loop.failed();
1290  }
1291  loop.check();
1292 }
1293 
1294 /*!
1295  * Compute the total grounding line flux over a time step, in kg.
1296  */
1298  const array::Staggered1 &flux,
1299  double dt) {
1300  using mask::grounded;
1301 
1302  auto grid = cell_type.grid();
1303 
1304  const double
1305  dx = grid->dx(),
1306  dy = grid->dy();
1307 
1308  auto ice_density = grid->ctx()->config()->get_number("constants.ice.density");
1309 
1310  double total_flux = 0.0;
1311 
1312  array::AccessScope list{&cell_type, &flux};
1313 
1314  ParallelSection loop(grid->com);
1315  try {
1316  for (auto p = grid->points(); p; p.next()) {
1317  const int i = p.i(), j = p.j();
1318 
1319  double volume_flux = 0.0;
1320 
1321  if (cell_type.ocean(i ,j)) {
1322  auto M = cell_type.star(i, j);
1323  auto Q = flux.star(i, j); // m^2 / s
1324 
1325  if (grounded(M.n)) {
1326  volume_flux += Q.n * dx;
1327  }
1328 
1329  if (grounded(M.e)) {
1330  volume_flux += Q.e * dy;
1331  }
1332 
1333  if (grounded(M.s)) {
1334  volume_flux -= Q.s * dx;
1335  }
1336 
1337  if (grounded(M.w)) {
1338  volume_flux -= Q.w * dy;
1339  }
1340  }
1341 
1342  // convert from "m^3 / s" to "kg" and sum up
1343  total_flux += volume_flux * dt * ice_density;
1344  }
1345  } catch (...) {
1346  loop.failed();
1347  }
1348  loop.check();
1349 
1350  return GlobalSum(grid->com, total_flux);
1351 }
1352 
1353 } // end of namespace pism
std::shared_ptr< const Grid > grid() const
Definition: Component.cc:105
const Config::ConstPtr m_config
configuration database used by this component
Definition: Component.hh:158
const Logger::ConstPtr m_log
logger (for easy access)
Definition: Component.hh:162
const std::shared_ptr< const Grid > m_grid
grid used by this component
Definition: Component.hh:156
DiagnosticList diagnostics() const
Definition: Component.cc:89
const Profiling & profiling() const
Definition: Component.cc:113
A class defining a common interface for most PISM sub-models.
Definition: Component.hh:118
std::shared_ptr< const Config > ConstPtr
const GeometryEvolution * model
Definition: Diagnostic.hh:172
A template derived from Diagnostic, adding a "Model".
Definition: Diagnostic.hh:166
std::vector< SpatialVariableMetadata > m_vars
metadata corresponding to NetCDF variables
Definition: Diagnostic.hh:120
std::shared_ptr< Diagnostic > Ptr
Definition: Diagnostic.hh:65
std::shared_ptr< const Grid > m_grid
the grid
Definition: Diagnostic.hh:114
void compute_mask(const array::Scalar &sea_level, const array::Scalar &bed, const array::Scalar &thickness, array::Scalar &result) const
Definition: Mask.cc:36
void set_icefree_thickness(double threshold)
Definition: Mask.hh:81
void compute(const array::Scalar &sea_level, const array::Scalar &bed, const array::Scalar &thickness, array::Scalar &out_mask, array::Scalar &out_surface) const
Definition: Mask.cc:27
const array::Scalar & thickness_change_due_to_flow() const
virtual void set_no_model_mask(const array::Scalar &mask)
virtual void init_impl(const InputOptions &opts)
virtual void compute_surface_and_basal_mass_balance(double dt, const array::Scalar &thickness_bc_mask, const array::Scalar &ice_thickness, const array::CellType &cell_type, const array::Scalar &surface_mass_flux, const array::Scalar &basal_melt_rate, array::Scalar &effective_SMB, array::Scalar &effective_BMB)
const array::Scalar & bottom_surface_mass_balance() const
void apply_mass_fluxes(Geometry &geometry) const
void residual_redistribution_iteration(const array::Scalar &bed_topography, const array::Scalar &sea_level, array::Scalar1 &ice_surface_elevation, array::Scalar &ice_thickness, array::CellType1 &cell_type, array::Scalar &Href, array::Scalar &H_residual, bool &done)
Perform one iteration of the residual mass redistribution.
virtual void set_no_model_mask_impl(const array::Scalar &mask)
const array::Scalar & flux_divergence() const
void update_in_place(double dt, const array::Scalar &bed_elevation, const array::Scalar &sea_level, const array::Scalar &flux_divergence, array::Scalar &ice_thickness, array::Scalar &area_specific_volume)
virtual void ensure_nonnegativity(const array::Scalar &ice_thickness, const array::Scalar &area_specific_volume, array::Scalar &thickness_change, array::Scalar &area_specific_volume_change, array::Scalar &conservation_error)
virtual void compute_interface_fluxes(const array::CellType1 &cell_type, const array::Scalar &ice_thickness, const array::Vector &velocity, const array::Staggered &diffusive_flux, array::Staggered &output)
GeometryEvolution(std::shared_ptr< const Grid > grid)
void flow_step(const Geometry &ice_geometry, double dt, const array::Vector &advective_velocity, const array::Staggered &diffusive_flux, const array::Scalar &thickness_bc_mask)
virtual void compute_flux_divergence(double dt, const array::Staggered1 &flux_staggered, const array::Scalar &thickness_bc_mask, array::Scalar &conservation_error, array::Scalar &flux_fivergence)
const array::Scalar & top_surface_mass_balance() const
void source_term_step(const Geometry &geometry, double dt, const array::Scalar &thickness_bc_mask, const array::Scalar &surface_mass_flux, const array::Scalar &basal_melt_rate)
void apply_flux_divergence(Geometry &geometry) const
const array::Scalar & area_specific_volume_change_due_to_flow() const
const array::Scalar & conservation_error() const
const array::Staggered1 & flux_staggered() const
void init(const InputOptions &opts)
std::map< std::string, Diagnostic::Ptr > diagnostics_impl() const
array::Scalar1 sea_level_elevation
Definition: Geometry.hh:48
array::Scalar1 ice_area_specific_volume
Definition: Geometry.hh:52
array::CellType2 cell_type
Definition: Geometry.hh:55
array::Scalar2 ice_thickness
Definition: Geometry.hh:51
array::Scalar2 bed_elevation
Definition: Geometry.hh:47
void failed()
Indicates a failure of a parallel section.
void begin(const char *name) const
Definition: Profiling.cc:75
void end(const char *name) const
Definition: Profiling.cc:91
RegionalGeometryEvolution(std::shared_ptr< const Grid > grid)
void compute_surface_and_basal_mass_balance(double dt, const array::Scalar &thickness_bc_mask, const array::Scalar &ice_thickness, const array::CellType &cell_type, const array::Scalar &surface_mass_flux, const array::Scalar &basal_melt_rate, array::Scalar &effective_SMB, array::Scalar &effective_BMB)
void compute_interface_fluxes(const array::CellType1 &cell_type, const array::Scalar &ice_thickness, const array::Vector &velocity, const array::Staggered &diffusive_flux, array::Staggered &output)
void set_no_model_mask_impl(const array::Scalar &mask)
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
VariableMetadata & long_name(const std::string &input)
VariableMetadata & output_units(const std::string &input)
VariableMetadata & units(const std::string &input)
This class represents a 2D vector field (such as ice velocity) at a certain grid point.
Definition: Vector2d.hh:29
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition: Array.hh:65
void copy_from(const Array2D< T > &source)
Definition: Array2D.hh:73
stencils::Star< T > star(int i, int j) const
Definition: Array2D.hh:79
void add(double alpha, const Array2D< T > &x)
Definition: Array2D.hh:65
void set_interpolation_type(InterpolationType type)
Definition: Array.cc:179
std::shared_ptr< const Grid > grid() const
Definition: Array.cc:132
void set(double c)
Result: v[j] <- c for all j.
Definition: Array.cc:707
void update_ghosts()
Updates ghost points.
Definition: Array.cc:693
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition: Array.cc:553
bool next_to_ice(int i, int j) const
Ice-free margin (at least one of four neighbors has ice).
Definition: CellType.hh:87
stencils::Star< int > star_int(int i, int j) const
Definition: Scalar.hh:72
bool ice_free_ocean(int i, int j) const
Definition: CellType.hh:58
bool ocean(int i, int j) const
Definition: CellType.hh:34
"Cell type" mask. Adds convenience methods to array::Scalar.
Definition: CellType.hh:30
int as_int(int i, int j) const
Definition: Scalar.hh:45
stencils::Star< double > star(int i, int j) const
Returns the values at interfaces of the cell i,j using the staggered grid.
Definition: Staggered.hh:73
void copy_from(const array::Staggered &input)
Definition: Staggered.cc:42
A class for storing and accessing internal staggered-grid 2D fields. Uses dof=2 storage....
Definition: Staggered.hh:35
std::shared_ptr< array::Array > compute_impl() const
FluxDivergence(const GeometryEvolution *m)
Report the divergence of the ice flux.
std::shared_ptr< array::Array > compute_impl() const
FluxStaggered(const GeometryEvolution *m)
Report mass flux on the staggered grid.
#define PISM_ERROR_LOCATION
#define n
Definition: exactTestM.c:37
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:165
double sum(const array::Scalar &input)
Sums up all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:150
bool icy(int M)
Ice-filled cell (grounded or floating).
Definition: Mask.hh:48
bool grounded_ice(int M)
Definition: Mask.hh:51
bool ice_free_land(int M)
Definition: Mask.hh:64
bool ice_free_ocean(int M)
Definition: Mask.hh:61
bool grounded(int M)
Grounded cell (grounded ice or ice-free).
Definition: Mask.hh:44
bool ice_free(int M)
Ice-free cell (grounded or ocean).
Definition: Mask.hh:58
bool floating_ice(int M)
Definition: Mask.hh:54
static double effective_change(double H, double dH)
static const double g
Definition: exactTestP.cc:36
static double limit_advective_flux(int current, int neighbor, double input)
std::map< std::string, Diagnostic::Ptr > DiagnosticList
Definition: Diagnostic.hh:125
double part_grid_threshold_thickness(stencils::Star< int > cell_type, stencils::Star< double > ice_thickness, stencils::Star< double > surface_elevation, double bed_elevation)
Compute threshold thickness used when deciding if a partially-filled cell should be considered 'full'...
double total_grounding_line_flux(const array::CellType1 &cell_type, const array::Staggered1 &flux, double dt)
@ North
Definition: stencils.hh:24
@ East
Definition: stencils.hh:24
@ South
Definition: stencils.hh:24
@ West
Definition: stencils.hh:24
void grounding_line_flux(const array::CellType1 &cell_type, const array::Staggered1 &flux, double dt, bool add_values, array::Scalar &output)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
static double limit_diffusive_flux(int current, int neighbor, double flux)
void make_nonnegative_preserving(double dt, const array::Scalar1 &x, const array::Staggered1 &flux, array::Staggered &result)
Definition: flux_limiter.cc:75
array::Scalar flux_divergence
Flux divergence (used to track thickness changes due to flow).
array::Scalar thickness_change
Change in ice thickness due to flow during the last time step.
bool use_bmr
True if the basal melt rate contributes to geometry evolution.
Impl(std::shared_ptr< const Grid > g)
array::Scalar ice_area_specific_volume_change
Change in the ice area-specific volume due to flow during the last time step.
array::Scalar effective_BMB
Effective basal mass balance.
bool use_part_grid
True if the part-grid scheme is enabled.
array::Staggered1 flux_staggered
Flux through cell interfaces. Ghosted.
array::Scalar effective_SMB
Effective surface mass balance.