PISM, A Parallel Ice Sheet Model 2.2.1-cd005eec8 committed by Constantine Khrulev on 2025-03-07
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GeometryEvolution.cc
Go to the documentation of this file.
1/* Copyright (C) 2016--2025 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/Interpolation1D.hh"
34#include "pism/util/pism_utilities.hh"
35
36#include "pism/geometry/flux_limiter.hh"
37
38namespace pism {
39
42using mask::ice_free;
45using mask::icy;
46
48 Impl(std::shared_ptr<const Grid> g);
49
51
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
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
94GeometryEvolution::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("m^2 s^-1")
130 .output_units("m^2 year^-1");
132 .long_name("fluxes through cell interfaces (sides) on the staggered grid (y-offset)")
133 .units("m^2 s^-1")
134 .output_units("m^2 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("meters^3 / meters^2");
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("meters^3 / meters^2");
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("meters^3 / meters^2");
186
187 thickness.metadata(0).long_name("thickness (temporary storage)").units("meters");
188 }
189}
190
192 m_impl = new Impl(grid);
193}
194
198
200 this->init_impl(opts);
201}
202
204 (void)opts;
205 // empty: the default implementation has no state
206}
207
211
215
219
223
227
231
235
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 */
250void 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");
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
347void 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 */
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 */
415static 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 */
493static 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.as_int(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.as_int(i_n, j_n);
599
600 // advective velocity at the current interface
601 double v = 0.0;
602 {
603 const Vector2d& V_n = velocity(i_n, j_n);
604 int W = static_cast<int>(icy(M)), W_n = static_cast<int>(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.as_int(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.as_int(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 */
698void 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) {
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(
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 = (int)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
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_int(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 done = remaining_residual <= 0.0;
933
934 ice_thickness.update_ghosts();
935}
936
937/*!
938 * Correct `thickness_change` and `area_specific_volume_change` so that applying them will not
939 * result in negative `ice_thickness` and `area_specific_volume`.
940 *
941 * Compute the the amount of ice that is added to preserve non-negativity of ice thickness.
942 *
943 * @param[in] ice_thickness ice thickness (m)
944 * @param[in] area_specific_volume area-specific volume (m3/m2)
945 * @param[in,out] thickness_change "proposed" thickness change (m)
946 * @param[in,out] area_specific_volume_change "proposed" area-specific volume change (m3/m2)
947 * @param[in,out] conservation_error computed conservation error (m)
948 *
949 * This computation is purely local.
950 */
952 const array::Scalar &area_specific_volume,
953 array::Scalar &thickness_change,
954 array::Scalar &area_specific_volume_change,
955 array::Scalar &conservation_error) {
956
957 array::AccessScope list{ &ice_thickness, &area_specific_volume, &thickness_change,
958 &area_specific_volume_change, &conservation_error };
959
960 ParallelSection loop(m_grid->com);
961 try {
962 for (auto p = m_grid->points(); p; p.next()) {
963 const int i = p.i(), j = p.j();
964
965 const double H = ice_thickness(i, j), dH = thickness_change(i, j);
966
967 // applying thickness_change will lead to negative thickness
968 if (H + dH < 0.0) {
969 thickness_change(i, j) = -H;
970 conservation_error(i, j) += -(H + dH);
971 }
972
973 const double V = area_specific_volume(i, j), dV = area_specific_volume_change(i, j);
974
975 if (V + dV < 0.0) {
976 area_specific_volume_change(i, j) = -V;
977 conservation_error(i, j) += -(V + dV);
978 }
979 }
980 } catch (...) {
981 loop.failed();
982 }
983 loop.check();
984}
985
986/*!
987 * Given ice thickness `H` and the "proposed" change `dH`, compute the corrected change preserving
988 * non-negativity.
989 */
990static inline double effective_change(double H, double dH) {
991 if (H + dH <= 0) {
992 return -H;
993 }
994 return dH;
995}
996
997/*!
998 * Compute effective surface and basal mass balance.
999 *
1000 * @param[in] dt time step, seconds
1001 * @param[in] thickness_bc_mask mask specifying ice thickness Dirichlet B.C. locations
1002 * @param[in] ice_thickness ice thickness, m
1003 * @param[in] thickness_change thickness change due to flow, m
1004 * @param[in] cell_type cell type mask
1005 * @param[in] smb_rate top surface mass balance rate, kg m-2 s-1
1006 * @param[in] basal_melt_rate basal melt rate, m s-1
1007 * @param[out] effective_smb effective top surface mass balance, m
1008 * @param[out] effective_bmb effective basal mass balance, m
1009 *
1010 * This computation is purely local.
1011 *
1012 * Positive outputs correspond to mass gain.
1013 */
1015 double dt, const array::Scalar &thickness_bc_mask, const array::Scalar &ice_thickness,
1016 const array::CellType &cell_type, const array::Scalar &surface_mass_flux,
1017 const array::Scalar &basal_melt_rate, array::Scalar &effective_SMB,
1018 array::Scalar &effective_BMB) {
1019
1020 array::AccessScope list{ &ice_thickness, &surface_mass_flux, &basal_melt_rate, &cell_type,
1021 &thickness_bc_mask, &effective_SMB, &effective_BMB };
1022
1023 ParallelSection loop(m_grid->com);
1024 try {
1025 for (auto p = m_grid->points(); p; p.next()) {
1026 const int i = p.i(), j = p.j();
1027
1028 // Don't modify ice thickness at Dirichlet B.C. locations and in the ice-free ocean.
1029 if (thickness_bc_mask.as_int(i, j) == 1 or cell_type.ice_free_ocean(i, j)) {
1030 effective_SMB(i, j) = 0.0;
1031 effective_BMB(i, j) = 0.0;
1032 continue;
1033 }
1034
1035 const double H = ice_thickness(i, j);
1036
1037 // Thickness change due to the surface mass balance
1038 //
1039 // Note that here we convert surface mass balance from [kg m-2 s-1] to [m s-1].
1040 double dH_SMB = effective_change(H, dt * surface_mass_flux(i, j) / m_impl->ice_density);
1041
1042 // Thickness change due to the basal mass balance
1043 //
1044 // Note that basal_melt_rate is in [m s-1]. Here the negative sign converts the melt rate into
1045 // mass balance.
1046 double dH_BMB =
1047 effective_change(H + dH_SMB, dt * (m_impl->use_bmr ? -basal_melt_rate(i, j) : 0.0));
1048
1049 effective_SMB(i, j) = dH_SMB;
1050 effective_BMB(i, j) = dH_BMB;
1051 }
1052 } catch (...) {
1053 loop.failed();
1054 }
1055 loop.check();
1056}
1057
1058namespace diagnostics {
1059
1060/*! @brief Report the divergence of the ice flux. */
1061class FluxDivergence : public Diag<GeometryEvolution> {
1062public:
1066
1067protected:
1068 std::shared_ptr<array::Array> compute_impl() const {
1069 auto result = allocate<array::Scalar>("flux_divergence");
1070
1071 result->copy_from(model->flux_divergence());
1072
1073 return result;
1074 }
1075};
1076
1077/*! @brief Report mass flux on the staggered grid. */
1078class FluxStaggered : public Diag<GeometryEvolution> {
1079public:
1083
1084protected:
1085 std::shared_ptr<array::Array> compute_impl() const {
1086 auto result = allocate<array::Staggered>("flux_staggered");
1087
1088 result->copy_from(model->flux_staggered());
1089
1090 return result;
1091 }
1092};
1093
1094} // end of namespace diagnostics
1095
1097 using namespace diagnostics;
1098 typedef Diagnostic::Ptr Ptr;
1099
1100 std::map<std::string, Ptr> result;
1101 result = {
1102 { "flux_staggered", Ptr(new FluxStaggered(this)) },
1103 { "flux_divergence", Ptr(new FluxDivergence(this)) },
1104 };
1105 return result;
1106}
1107
1109 : GeometryEvolution(grid), m_no_model_mask(m_grid, "no_model_mask") {
1110
1112
1113 m_no_model_mask.metadata(0).long_name("'no model' mask");
1114}
1115
1119
1120/*!
1121 * Disable ice flow in "no model" areas.
1122 */
1124 const array::Scalar &ice_thickness,
1125 const array::Vector &velocity,
1126 const array::Staggered &diffusive_flux,
1127 array::Staggered &output) {
1128
1129 GeometryEvolution::compute_interface_fluxes(cell_type, ice_thickness,
1130 velocity, diffusive_flux,
1131 output);
1132
1133 array::AccessScope list{&m_no_model_mask, &output};
1134
1135 ParallelSection loop(m_grid->com);
1136 try {
1137 for (auto p = m_grid->points(); p; p.next()) {
1138 const int i = p.i(), j = p.j();
1139
1140 const int M = m_no_model_mask.as_int(i, j);
1141
1142 for (int n : {0, 1}) {
1143 const int
1144 oi = 1 - n, // offset in the i direction
1145 oj = n, // offset in the j direction
1146 i_n = i + oi, // i index of a neighbor
1147 j_n = j + oj; // j index of a neighbor
1148
1149 const int M_n = m_no_model_mask.as_int(i_n, j_n);
1150
1151 if (not (M == 0 and M_n == 0)) {
1152 output(i, j, n) = 0.0;
1153 }
1154 }
1155 }
1156 } catch (...) {
1157 loop.failed();
1158 }
1159 loop.check();
1160}
1161
1162/*!
1163 * Set surface and basal mass balance to zero in "no model" areas.
1164 */
1166 const array::Scalar &thickness_bc_mask,
1167 const array::Scalar &ice_thickness,
1168 const array::CellType &cell_type,
1169 const array::Scalar &surface_mass_flux,
1170 const array::Scalar &basal_melt_rate,
1171 array::Scalar &effective_SMB,
1172 array::Scalar &effective_BMB) {
1174 thickness_bc_mask,
1175 ice_thickness,
1176 cell_type,
1177 surface_mass_flux,
1178 basal_melt_rate,
1179 effective_SMB,
1180 effective_BMB);
1181
1182 array::AccessScope list{&m_no_model_mask, &effective_SMB, &effective_BMB};
1183
1184 ParallelSection loop(m_grid->com);
1185 try {
1186 for (auto p = m_grid->points(); p; p.next()) {
1187 const int i = p.i(), j = p.j();
1188
1189 if (m_no_model_mask(i, j) > 0.5) {
1190 effective_SMB(i, j) = 0.0;
1191 effective_BMB(i, j) = 0.0;
1192 }
1193 }
1194 } catch (...) {
1195 loop.failed();
1196 }
1197 loop.check();
1198}
1199
1203
1205 (void) mask;
1206 // the default implementation is a no-op
1207}
1208/*!
1209 * Return the volumetric ice flow rate from land to water (across the grounding line), in
1210 * m^3 / s. Positive values correspond to ice moving from the grounded side to the
1211 * floating (ocean) side.
1212 *
1213 * Mass continuity equation without source terms:
1214 *
1215 * dH/dt = - div(Q)
1216 *
1217 * Approximating the flux divergence, we get
1218 *
1219 * - div(Q) ~= - ((Q.e - Q.w) / dx + (Q.n - Q.s) / dy)
1220 *
1221 * Multiplying by the cell area we get the volume flow rate
1222 *
1223 * dV/dt = - dx *dy * div(Q)
1224 *
1225 * which can be approximated by
1226 *
1227 * - (dy * (Q.e - Q.w) + dx * (Q.n - Q.s)) = dy * (Q.w - Q.e) + dx * (Q.s - Q.n)
1228 */
1230 const stencils::Star<double> &flux, double dx,
1231 double dy) {
1232 using mask::grounded;
1233
1234 auto Q = flux; // units: m^2 / s
1235
1236 // zero out fluxes between the current (floating or ocean) cell and other (floating or
1237 // ocean) cells
1238 Q.n *= (int)grounded(cell_type.n);
1239 Q.e *= (int)grounded(cell_type.e);
1240 Q.s *= (int)grounded(cell_type.s);
1241 Q.w *= (int)grounded(cell_type.w);
1242
1243 return dy * (Q.w - Q.e) + dx * (Q.s - Q.n); // units: m^3 / s
1244}
1245
1246/*!
1247 * Compute the ice flow rate across the grounding line, adding to `output` to accumulate
1248 * contributions from multiple time steps.
1249 *
1250 * When `unit_conversion_factor` is 1 the units of `output` are "m^3 / s".
1251 *
1252 * Negative flux corresponds to ice moving into the ocean, i.e. from grounded to floating
1253 * areas. (This convention makes it easier to compare this quantity to the surface mass
1254 * balance or calving fluxes.)
1255 *
1256 * Different choices of the `unit_conversion_factor` make it possible to use this in
1257 *
1258 * - the grounding line flux diagnostic (in kg / (m^2 s)),
1259 * - the volume flow rate diagnostic (in m^3 / s),
1260 * - or the mass flow rate diagnostic (in kg / s).
1261 */
1263 const array::Staggered1 &flux,
1264 double unit_conversion_factor, array::Scalar &output) {
1265 auto grid = output.grid();
1266
1267 const double dx = grid->dx(), // units: m
1268 dy = grid->dy(); // units: m
1269
1270 array::AccessScope list{ &cell_type, &flux, &output };
1271
1272 ParallelSection loop(grid->com);
1273 try {
1274 for (auto p = grid->points(); p; p.next()) {
1275 const int i = p.i(), j = p.j();
1276
1277 double result = 0.0;
1278
1279 if (cell_type.ocean(i, j)) {
1280 auto M = cell_type.star_int(i, j);
1281 auto Q = flux.star(i, j); // units: m^2 / s
1282
1283 // note the sign change: here *negative* values correspond to ice moving from
1284 // grounded to floating areas since it can sometimes be interpreted as "mass loss"
1285 result = -volume_flow_rate_from_land_to_water(M, Q, dx, dy); // units: m^3 / s
1286
1287 // convert from "m^3 / s" to "kg"
1288 result *= unit_conversion_factor;
1289 }
1290
1291 output(i, j) += result;
1292 }
1293 } catch (...) {
1294 loop.failed();
1295 }
1296 loop.check();
1297}
1298
1300 const array::Staggered1 &flux,
1301 double dt) {
1302 auto grid = cell_type.grid();
1303
1304 const double
1305 dx = grid->dx(), // units: m
1306 dy = grid->dy(); // units: m
1307
1308 auto ice_density = grid->ctx()->config()->get_number("constants.ice.density"); // units: kg / m^3
1309
1310 double conversion_factor = dt * ice_density;
1311
1312 double total_flux = 0.0;
1313
1314 array::AccessScope list{&cell_type, &flux};
1315
1316 ParallelSection loop(grid->com);
1317 try {
1318 for (auto p = grid->points(); p; p.next()) {
1319 const int i = p.i(), j = p.j();
1320
1321 if (cell_type.ocean(i, j)) {
1322 auto M = cell_type.star_int(i, j);
1323 auto Q = flux.star(i, j); // units: m^2 / s
1324
1325 // note the sign change: here *negative* values correspond to ice moving from
1326 // grounded to floating areas since it can sometimes be interpreted as "mass loss"
1327 double volume_flux = -volume_flow_rate_from_land_to_water(M, Q, dx, dy); // units: m^3 / s
1328 // convert from "m^3 / s" to "kg" and sum up
1329 total_flux += volume_flux * conversion_factor;
1330 }
1331 }
1332 } catch (...) {
1333 loop.failed();
1334 }
1335 loop.check();
1336
1337 return GlobalSum(grid->com, total_flux);
1338}
1339
1340} // 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
A template derived from Diagnostic, adding a "Model".
std::vector< SpatialVariableMetadata > m_vars
metadata corresponding to NetCDF variables
std::shared_ptr< Diagnostic > Ptr
Definition Diagnostic.hh:65
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
virtual void set_no_model_mask_impl(const array::Scalar &mask)
void source_term_step(const Geometry &geometry, double dt, const array::Scalar &thickness_bc_mask, const array::Scalar &surface_mass_balance_rate, const array::Scalar &basal_melt_rate)
const array::Scalar & flux_divergence() const
void update_in_place(double dt, const array::Scalar &bed_topography, 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)
const array::Scalar & top_surface_mass_balance() const
void apply_flux_divergence(Geometry &geometry) const
const array::Scalar & area_specific_volume_change_due_to_flow() const
virtual void compute_flux_divergence(double dt, const array::Staggered1 &flux, const array::Scalar &thickness_bc_mask, array::Scalar &conservation_error, array::Scalar &output)
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 &area_specific_volume, array::Scalar &residual, bool &done)
Perform one iteration of the residual mass redistribution.
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 & units(const std::string &input)
VariableMetadata & output_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:64
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:178
std::shared_ptr< const Grid > grid() const
Definition Array.cc:131
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:629
void update_ghosts()
Updates ghost points.
Definition Array.cc:615
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition Array.cc:476
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:75
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:37
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
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
static double volume_flow_rate_from_land_to_water(const stencils::Star< int > &cell_type, const stencils::Star< double > &flux, double dx, double dy)
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 ice_flow_rate_across_grounding_line(const array::CellType1 &cell_type, const array::Staggered1 &flux, double unit_conversion_factor, 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)
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.
Star stencil points (in the map-plane).
Definition stencils.hh:30