PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
diagnostics.cc
Go to the documentation of this file.
1// Copyright (C) 2010--2026 Constantine Khroulev
2//
3// This file is part of PISM.
4//
5// PISM is free software; you can redistribute it and/or modify it under the
6// terms of the GNU General Public License as published by the Free Software
7// Foundation; either version 3 of the License, or (at your option) any later
8// version.
9//
10// PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13// details.
14//
15// You should have received a copy of the GNU General Public License
16// along with PISM; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19#include <algorithm>
20#include <cassert>
21#include <memory>
22
23#include "pism/age/AgeModel.hh"
24#include "pism/energy/EnergyModel.hh"
25#include "pism/energy/utilities.hh"
26#include "pism/geometry/grounded_cell_fraction.hh"
27#include "pism/geometry/part_grid_threshold_thickness.hh"
28#include "pism/icemodel/IceModel.hh"
29#include "pism/rheology/FlowLaw.hh"
30#include "pism/stressbalance/SSB_Modifier.hh"
31#include "pism/stressbalance/ShallowStressBalance.hh"
32#include "pism/stressbalance/StressBalance.hh"
33#include "pism/util/Diagnostic.hh"
34#include "pism/util/EnthalpyConverter.hh"
35#include "pism/util/error_handling.hh"
36#include "pism/util/pism_utilities.hh"
37#include "pism/util/projection.hh"
38#include "pism/util/io/IO_Flags.hh"
39
40#if (Pism_USE_PROJ == 1)
41#include "pism/util/Proj.hh"
42#endif
43
44// Flux balance code
45namespace pism {
46namespace diagnostics {
47
49
50//! @brief Computes tendency_of_ice_amount, the ice amount rate of change.
51class TendencyOfIceAmount : public Diag<IceModel> {
52public:
54 : Diag<IceModel>(Model),
55 m_kind(kind),
56 m_last_amount(m_grid, "last_ice_amount"),
58
59 std::string name = "tendency_of_ice_amount", long_name = "rate of change of the ice amount",
60 internal_units = "kg m^-2 second^-1", external_units = "kg m^-2 year^-1";
61 if (kind == MASS) {
62 name = "tendency_of_ice_mass";
63 long_name = "rate of change of the ice mass";
64 internal_units = "kg second^-1";
65 external_units = "Gt year^-1";
66 }
67
68 // set metadata:
69 m_vars = { { m_sys, name, *m_grid } };
70 m_vars[0].long_name(long_name).units(internal_units).output_units(external_units);
71
72 auto large_number = to_internal(1e6);
73 m_vars[0]["valid_range"] = { -large_number, large_number };
74 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
75 m_vars[0]["cell_methods"] = "time: mean";
76
78 .long_name("ice amount at the time of the last report of " + name)
79 .units(internal_units + " second");
80 }
81
82protected:
83 std::shared_ptr<array::Array> compute_impl() const {
84
85 auto result = std::make_shared<array::Scalar>(m_grid, "");
86 result->metadata() = m_vars[0];
87
88 if (m_interval_length > 0.0) {
89 double ice_density = m_config->get_number("constants.ice.density");
90
91 auto cell_area = m_grid->cell_area();
92
93 const auto &thickness = model->geometry().ice_thickness;
94 const auto &area_specific_volume = model->geometry().ice_area_specific_volume;
95
96 array::AccessScope list{ result.get(), &thickness, &area_specific_volume, &m_last_amount };
97
98 for (auto p : m_grid->points()) {
99 const int i = p.i(), j = p.j();
100
101 // m * (kg / m^3) = kg / m^2
102 double amount = (thickness(i, j) + area_specific_volume(i, j)) * ice_density;
103
104 (*result)(i, j) = (amount - m_last_amount(i, j)) / m_interval_length;
105
106 if (m_kind == MASS) {
107 // kg / m^2 * m^2 = kg
108 (*result)(i, j) *= cell_area;
109 }
110 }
111 } else {
112 result->set(m_fill_value);
113 }
114
115 return result;
116 }
117
118 void reset_impl() {
119 m_interval_length = 0.0;
120
121 const array::Scalar &thickness = model->geometry().ice_thickness;
122 const array::Scalar &area_specific_volume = model->geometry().ice_area_specific_volume;
123
124 double ice_density = m_config->get_number("constants.ice.density");
125
126 array::AccessScope list{ &m_last_amount, &thickness, &area_specific_volume };
127
128 for (auto p : m_grid->points()) {
129 const int i = p.i(), j = p.j();
130
131 // m * (kg / m^3) = kg / m^2
132 m_last_amount(i, j) = (thickness(i, j) + area_specific_volume(i, j)) * ice_density;
133 }
134 }
135
136 void update_impl(double dt) {
137 m_interval_length += dt;
138 }
139
143};
144
145//! @brief Computes tendency_of_ice_amount_due_to_flow, the rate of change of ice amount due to
146//! flow.
147/*! @brief Report rate of change of ice amount due to flow. */
149public:
151 : DiagAverageRate<IceModel>(Model,
152 kind == AMOUNT ? "tendency_of_ice_amount_due_to_flow" :
153 "tendency_of_ice_mass_due_to_flow",
155 m_kind(kind) {
156
157 std::string name = "tendency_of_ice_amount_due_to_flow",
158 long_name = "rate of change of ice amount due to flow",
159 accumulator_units = "kg m^-2", internal_units = "kg m^-2 second^-1",
160 external_units = "kg m^-2 year^-1";
161
162 if (kind == MASS) {
163 name = "tendency_of_ice_mass_due_to_flow";
164 long_name = "rate of change of ice mass due to flow";
165 accumulator_units = "kg";
166 internal_units = "kg second^-1";
167 external_units = "Gt year^-1";
168 }
169
170 m_factor = m_config->get_number("constants.ice.density");
171
172 m_accumulator.metadata().units(accumulator_units);
173
174 m_vars = { { m_sys, name, *m_grid } };
175 m_vars[0].long_name(long_name).units(internal_units).output_units(external_units);
176 m_vars[0]["cell_methods"] = "time: mean";
177 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
178 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
179 }
180
181protected:
182 void update_impl(double dt) {
183 const array::Scalar &dH = model->geometry_evolution().thickness_change_due_to_flow(),
184 &dV = model->geometry_evolution().area_specific_volume_change_due_to_flow();
185
186 auto cell_area = m_grid->cell_area();
187
188 array::AccessScope list{ &m_accumulator, &dH, &dV };
189
190 for (auto p : m_grid->points()) {
191 const int i = p.i(), j = p.j();
192
193 double C = m_factor * (m_kind == AMOUNT ? 1.0 : cell_area);
194
195 m_accumulator(i, j) += C * (dH(i, j) + dV(i, j));
196 }
197
198 m_interval_length += dt;
199 }
201};
202
203/*! @brief Report surface mass balance flux, averaged over the reporting interval */
204class SurfaceFlux : public DiagAverageRate<IceModel> {
205public:
208 kind == AMOUNT ?
209 "tendency_of_ice_amount_due_to_surface_mass_flux" :
210 "tendency_of_ice_mass_due_to_surface_mass_flux",
212 m_kind(kind) {
213 m_factor = m_config->get_number("constants.ice.density");
214
215 auto ismip6 = m_config->get_flag("output.ISMIP6");
216
217 std::string name = ismip6 ? "acabf" : "tendency_of_ice_amount_due_to_surface_mass_flux",
218 accumulator_units = "kg m^-2",
219 long_name = "average surface mass flux over reporting interval",
220 standard_name = "land_ice_surface_specific_mass_balance_flux",
221 internal_units = "kg m^-2 s^-1", external_units = "kg m^-2 year^-1";
222 if (kind == MASS) {
223 name = "tendency_of_ice_mass_due_to_surface_mass_flux", accumulator_units = "kg",
224 long_name = "average surface mass flux over reporting interval", standard_name = "",
225 internal_units = "kg second^-1", external_units = "Gt year^-1";
226 }
227
228 m_accumulator.metadata()["units"] = accumulator_units;
229
230 m_vars = { { m_sys, name, *m_grid } };
231 m_vars[0]
232 .long_name(long_name)
233 .standard_name(standard_name)
234 .units(internal_units)
235 .output_units(external_units);
236 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
237 m_vars[0]["cell_methods"] = "time: mean";
238 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
239 }
240
241protected:
242 void update_impl(double dt) {
243 const array::Scalar &SMB = model->geometry_evolution().top_surface_mass_balance();
244
245 auto cell_area = m_grid->cell_area();
246
248
249 for (auto p : m_grid->points()) {
250 const int i = p.i(), j = p.j();
251
252 double C = m_factor * (m_kind == AMOUNT ? 1.0 : cell_area);
253
254 m_accumulator(i, j) += C * SMB(i, j);
255 }
256
257 m_interval_length += dt;
258 }
260};
261
262/*! @brief Report basal mass balance flux, averaged over the reporting interval */
263class BasalFlux : public DiagAverageRate<IceModel> {
264public:
267 kind == AMOUNT ? "tendency_of_ice_amount_due_to_basal_mass_flux" :
268 "tendency_of_ice_mass_due_to_basal_mass_flux",
270 m_kind(kind) {
271 m_factor = m_config->get_number("constants.ice.density");
272
273 std::string name = "tendency_of_ice_amount_due_to_basal_mass_flux",
274 accumulator_units = "kg m^-2",
275 long_name = "average basal mass flux over reporting interval", standard_name,
276 internal_units = "kg m^-2 second^-1", external_units = "kg m^-2 year^-1";
277 if (kind == MASS) {
278 name = "tendency_of_ice_mass_due_to_basal_mass_flux", accumulator_units = "kg",
279 long_name = "average basal mass flux over reporting interval",
280 standard_name = "tendency_of_land_ice_mass_due_to_basal_mass_balance",
281 internal_units = "kg second^-1", external_units = "Gt year^-1";
282 }
283 m_accumulator.metadata()["units"] = accumulator_units;
284
285 m_vars = { { m_sys, name, *m_grid } };
286 m_vars[0]
287 .long_name(long_name)
288 .standard_name(standard_name)
289 .units(internal_units)
290 .output_units(external_units);
291 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
292 m_vars[0]["cell_methods"] = "time: mean";
293 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
294 }
295
296protected:
297 void update_impl(double dt) {
298 const array::Scalar &BMB = model->geometry_evolution().bottom_surface_mass_balance();
299
300 auto cell_area = m_grid->cell_area();
301
303
304 for (auto p : m_grid->points()) {
305 const int i = p.i(), j = p.j();
306
307 double C = m_factor * (m_kind == AMOUNT ? 1.0 : cell_area);
308
309 m_accumulator(i, j) += C * BMB(i, j);
310 }
311
312 m_interval_length += dt;
313 }
315};
316
317class ConservationErrorFlux : public DiagAverageRate<IceModel> {
318public:
321 kind == AMOUNT ?
322 "tendency_of_ice_amount_due_to_conservation_error" :
323 "tendency_of_ice_mass_due_to_conservation_error",
325 m_kind(kind) {
326 m_factor = m_config->get_number("constants.ice.density");
327
328 std::string name = "tendency_of_ice_amount_due_to_conservation_error",
329 accumulator_units = "kg m^-2",
330 long_name = "average mass conservation error flux over reporting interval",
331 internal_units = "kg m^-2 second^-1", external_units = "kg m^-2 year^-1";
332 if (kind == MASS) {
333 name = "tendency_of_ice_mass_due_to_conservation_error", accumulator_units = "kg",
334 long_name = "average mass conservation error flux over reporting interval",
335 internal_units = "kg second^-1", external_units = "Gt year^-1";
336 }
337
338 m_accumulator.metadata()["units"] = accumulator_units;
339
340 m_vars = { { m_sys, name, *m_grid } };
341 m_vars[0]
342 .long_name(long_name)
343 .units(internal_units)
344 .output_units(external_units);
345 m_vars[0]["_FillValue"] = {to_internal(m_fill_value)};
346 m_vars[0]["cell_methods"] = "time: mean";
347 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
348 }
349
350protected:
351 void update_impl(double dt) {
352 const array::Scalar
353 &error = model->geometry_evolution().conservation_error();
354
355 array::AccessScope list{&m_accumulator, &error};
356
357 auto cell_area = m_grid->cell_area();
358
359 for (auto p : m_grid->points()) {
360 const int i = p.i(), j = p.j();
361
362 double C = m_factor * (m_kind == AMOUNT ? 1.0 : cell_area);
363
364 m_accumulator(i, j) += C * error(i, j);
365 }
366
367 m_interval_length += dt;
368 }
370};
371
373
374static void accumulate_changes(const IceModel *model, double factor, ChangeKind kind,
375 array::Scalar &accumulator) {
376
377 const auto &calving = model->calving();
378 const auto &frontal_melt = model->frontal_melt();
379 const auto &forced_retreat = model->forced_retreat();
380
381 auto grid = accumulator.grid();
382
383 bool add_calving = (kind == CALVING or kind == TOTAL_DISCHARGE);
384 bool add_frontal_melt = (kind == FRONTAL_MELT or kind == TOTAL_DISCHARGE);
385 bool add_forced_retreat = (kind == FORCED_RETREAT or kind == TOTAL_DISCHARGE);
386
387 array::AccessScope scope{ &accumulator };
388 if (add_calving) {
389 scope.add(calving);
390 }
391 if (add_frontal_melt) {
392 scope.add(frontal_melt);
393 }
394 if (add_forced_retreat) {
395 scope.add(forced_retreat);
396 }
397
398 for (auto p : grid->points()) {
399 const int i = p.i(), j = p.j();
400
401 if (add_calving) {
402 accumulator(i, j) += factor * calving(i, j);
403 }
404 if (add_frontal_melt) {
405 accumulator(i, j) += factor * frontal_melt(i, j);
406 }
407 if (add_forced_retreat) {
408 accumulator(i, j) += factor * forced_retreat(i, j);
409 }
410 }
411}
412
413
414/*! @brief Report discharge (calving and frontal melt) flux. */
415class DischargeFlux : public DiagAverageRate<IceModel> {
416public:
419 kind == AMOUNT ? "tendency_of_ice_amount_due_to_discharge" :
420 "tendency_of_ice_mass_due_to_discharge",
422 m_kind(kind) {
423
424 m_factor = m_config->get_number("constants.ice.density");
425
426 auto ismip6 = m_config->get_flag("output.ISMIP6");
427
428 std::string name = ismip6 ? "lifmassbf" : "tendency_of_ice_amount_due_to_discharge",
429 long_name = "discharge flux (calving, frontal melt, forced retreat)",
430 accumulator_units = "kg m^-2",
431 standard_name = "land_ice_specific_mass_flux_due_to_calving_and_ice_front_melting",
432 internal_units = "kg m^-2 s^-1", external_units = "kg m^-2 year^-1";
433 if (kind == MASS) {
434 name = "tendency_of_ice_mass_due_to_discharge";
435 long_name = "discharge flux (calving, frontal melt, forced retreat)";
436 accumulator_units = "kg";
437 standard_name = "";
438 internal_units = "kg second^-1";
439 external_units = "Gt year^-1";
440 }
441
442 m_accumulator.metadata()["units"] = accumulator_units;
443
444 m_vars = { { m_sys, name, *m_grid } };
445 m_vars[0]
446 .long_name(long_name)
447 .standard_name(standard_name)
448 .units(internal_units)
449 .output_units(external_units);
450 m_vars[0]["cell_methods"] = "time: mean";
451 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
452 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
453 }
454
455protected:
456 void update_impl(double dt) {
458 m_factor * (m_kind == AMOUNT ? 1.0 : m_grid->cell_area()),
461
462 m_interval_length += dt;
463 }
465};
466
467/*! @brief Report the calving flux. */
468class CalvingFlux : public DiagAverageRate<IceModel>
469{
470public:
473 kind == AMOUNT
474 ? "tendency_of_ice_amount_due_to_calving"
475 : "tendency_of_ice_mass_due_to_calving",
477 m_kind(kind) {
478
479 m_factor = m_config->get_number("constants.ice.density");
480
481 auto ismip6 = m_config->get_flag("output.ISMIP6");
482
483 std::string
484 name = ismip6 ? "licalvf" : "tendency_of_ice_amount_due_to_calving",
485 long_name = "calving flux",
486 accumulator_units = "kg m^-2",
487 standard_name = "land_ice_specific_mass_flux_due_to_calving",
488 internal_units = "kg m^-2 s^-1",
489 external_units = "kg m^-2 year^-1";
490 if (kind == MASS) {
491 name = "tendency_of_ice_mass_due_to_calving";
492 long_name = "calving flux";
493 accumulator_units = "kg";
494 standard_name = "";
495 internal_units = "kg second^-1";
496 external_units = "Gt year^-1";
497 }
498
499 m_accumulator.metadata().units(accumulator_units);
500
501 m_vars = { { m_sys, name, *m_grid } };
502 m_vars[0]
503 .long_name(long_name)
504 .standard_name(standard_name)
505 .units(internal_units)
506 .output_units(external_units);
507 m_vars[0]["cell_methods"] = "time: mean";
508
509 m_vars[0]["_FillValue"] = {to_internal(m_fill_value)};
510 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
511 }
512
513protected:
514 void update_impl(double dt) {
515
517 m_factor * (m_kind == AMOUNT ? 1.0 : m_grid->cell_area()),
518 CALVING,
520
521 m_interval_length += dt;
522 }
524};
525
526/*! @brief Report the frontal melt flux. */
527class FrontalMeltFlux : public DiagAverageRate<IceModel>
528{
529public:
532 kind == AMOUNT
533 ? "tendency_of_ice_amount_due_to_frontal_melt"
534 : "tendency_of_ice_mass_due_to_frontal_melt",
536 m_kind(kind) {
537
538 m_factor = m_config->get_number("constants.ice.density");
539
540 std::string name = "tendency_of_ice_amount_due_to_frontal_melt", accumulator_units = "kg m^-2",
541 internal_units = "kg m^-2 s^-1", external_units = "kg m^-2 year^-1";
542 if (kind == MASS) {
543 name = "tendency_of_ice_mass_due_to_frontal_melt";
544 accumulator_units = "kg";
545 internal_units = "kg second^-1";
546 external_units = "Gt year^-1";
547 }
548
549 m_accumulator.metadata().units(accumulator_units);
550
551 m_vars = { { m_sys, name, *m_grid } };
552 m_vars[0].long_name("frontal melt flux").units(internal_units).output_units(external_units);
553 m_vars[0]["cell_methods"] = "time: mean";
554 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
555 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
556 }
557
558protected:
559 void update_impl(double dt) {
560
562 m_factor * (m_kind == AMOUNT ? 1.0 : m_grid->cell_area()),
565
566 m_interval_length += dt;
567 }
569};
570
571/*! @brief Report the frontal melt flux. */
572class ForcedRetreatFlux : public DiagAverageRate<IceModel>
573{
574public:
577 kind == AMOUNT ? "tendency_of_ice_amount_due_to_forced_retreat" :
578 "tendency_of_ice_mass_due_to_forced_retreat",
580 m_kind(kind) {
581
582 m_factor = m_config->get_number("constants.ice.density");
583
584 std::string name = "tendency_of_ice_amount_due_to_forced_retreat", accumulator_units = "kg m^-2",
585 internal_units = "kg m^-2 s^-1", external_units = "kg m^-2 year^-1";
586 if (kind == MASS) {
587 name = "tendency_of_ice_mass_due_to_forced_retreat";
588 accumulator_units = "kg";
589 internal_units = "kg second^-1";
590 external_units = "Gt year^-1";
591 }
592
593 m_accumulator.metadata().units(accumulator_units);
594
595 m_vars = { { m_sys, name, *m_grid } };
596 m_vars[0]
597 .long_name("forced (prescribed) retreat flux")
598 .units(internal_units)
599 .output_units(external_units);
600 m_vars[0]["cell_methods"] = "time: mean";
601 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
602 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
603 }
604
605protected:
606 void update_impl(double dt) {
607
609 m_factor * (m_kind == AMOUNT ? 1.0 : m_grid->cell_area()),
612
613 m_interval_length += dt;
614 }
616};
617
618} // end of namespace diagnostics
619} // end of namespace pism
620
621namespace pism {
622
623namespace details {
625
626static double ice_volume(const array::Scalar &ice_thickness,
627 const array::Array3D &ice_enthalpy,
628 IceKind kind,
629 double thickness_threshold) {
630
631 auto grid = ice_thickness.grid();
632 auto ctx = grid->ctx();
633 auto EC = ctx->enthalpy_converter();
634
635 auto cell_area = grid->cell_area();
636 const auto& z = grid->z();
637
638 double volume = 0.0;
639
640 // count the volume of a 3D grid cell if
641 //
642 // - it is temperate and we're asked for the temperate ice volume
643 // - it is cold and we're asked for the cold ice volume
644 //
645 // return zero otherwise
646 //
647 // uses the depth at the *bottom* of a cell to compute pressure
648 auto volume_counter = [EC, kind, cell_area](double z_min, double z_max, double H, double E) {
649 double depth = H - z_min;
650 double P = EC->pressure(depth);
651 double V = cell_area * (z_max - z_min);
652 bool temperate = EC->is_temperate_relaxed(E, P); // FIXME issue #15
653
654 switch (kind) {
655 case ICE_TEMPERATE:
656 return temperate ? V : 0.0;
657 default:
658 case ICE_COLD:
659 return (not temperate) ? V : 0.0;
660 }
661 };
662
663 array::AccessScope list{&ice_thickness, &ice_enthalpy};
664 ParallelSection loop(grid->com);
665 try {
666 for (auto p : grid->points()) {
667 const int i = p.i(), j = p.j();
668
669 double H = ice_thickness(i, j);
670
671 if (H >= thickness_threshold) {
672 auto ks = grid->kBelowHeight(H);
673 const double *E = ice_enthalpy.get_column(i, j);
674
675 for (unsigned int k = 0; k < ks; ++k) {
676 volume += volume_counter(z[k], z[k + 1], H, E[k]);
677 }
678
679 volume += volume_counter(z[ks], H, H, E[ks]);
680 }
681 }
682 } catch (...) {
683 loop.failed();
684 }
685 loop.check();
686
687 return GlobalSum(grid->com, volume);
688}
689
690static double base_area(const array::Scalar &ice_thickness,
691 const array::Array3D &ice_enthalpy,
692 IceKind kind,
693 double thickness_threshold) {
694
695 auto grid = ice_thickness.grid();
696 auto ctx = grid->ctx();
697 auto EC = ctx->enthalpy_converter();
698
699 auto cell_area = grid->cell_area();
700
701 double area = 0.0;
702
703 array::AccessScope list{&ice_thickness, &ice_enthalpy};
704 ParallelSection loop(grid->com);
705 try {
706 for (auto p : grid->points()) {
707 const int i = p.i(), j = p.j();
708
709 double thickness = ice_thickness(i, j);
710
711 if (thickness >= thickness_threshold) {
712 double basal_enthalpy = ice_enthalpy.get_column(i, j)[0];
713
714 bool temperate = EC->is_temperate_relaxed(basal_enthalpy,
715 EC->pressure(thickness)); // FIXME issue #15
716
717 switch (kind) {
718 case ICE_TEMPERATE:
719 area += temperate ? cell_area : 0.0;
720 break;
721 default:
722 case ICE_COLD:
723 area += (not temperate) ? cell_area : 0.0;
724 }
725 }
726 }
727 } catch (...) {
728 loop.failed();
729 }
730 loop.check();
731
732 return GlobalSum(grid->com, area);
733}
734
735} // end of namespace details
736
737// Horrendous names used by InitMIP (and ISMIP6, and CMIP5). Ugh.
738static const char* land_ice_area_fraction_name = "sftgif";
739static const char* grounded_ice_sheet_area_fraction_name = "sftgrf";
740static const char* floating_ice_sheet_area_fraction_name = "sftflf";
741
742namespace diagnostics {
743
745
747
748/*! @brief Ocean pressure difference at calving fronts. Used to debug CF boundary conditins. */
749class IceMarginPressureDifference : public Diag<IceModel>
750{
751public:
753
754protected:
755 std::shared_ptr<array::Array> compute_impl() const;
756};
757
759
760 /* set metadata: */
761 m_vars = { { m_sys, "ice_margin_pressure_difference", *m_grid } };
762 m_vars[0]["_FillValue"] = { m_fill_value };
763 m_vars[0]
764 .long_name(
765 "vertically-averaged pressure difference at ice margins (including calving fronts)")
766 .units("Pa");
767}
768
769std::shared_ptr<array::Array> IceMarginPressureDifference::compute_impl() const {
770
771 auto result = allocate<array::Scalar>("ice_margin_pressure_difference");
772
773 array::CellType1 mask(m_grid, "mask");
774
775 const auto &H = model->geometry().ice_thickness;
776 const auto &bed = model->geometry().bed_elevation;
777 const auto &sea_level = model->geometry().sea_level_elevation;
778
779 {
780 const double H_threshold = m_config->get_number("stress_balance.ice_free_thickness_standard");
782 gc.set_icefree_thickness(H_threshold);
783
784 gc.compute_mask(sea_level, bed, H, mask);
785 }
786
787 const double rho_ice = m_config->get_number("constants.ice.density"),
788 rho_ocean = m_config->get_number("constants.sea_water.density"),
789 g = m_config->get_number("constants.standard_gravity");
790
791 array::AccessScope list{ &H, &bed, &mask, &sea_level, result.get() };
792
793 ParallelSection loop(m_grid->com);
794 try {
795 for (auto p : m_grid->points()) {
796 const int i = p.i(), j = p.j();
797
798 double delta_p = 0.0;
799 if (mask.grounded_ice(i, j) and grid::domain_edge(*m_grid, i, j)) {
800 delta_p = 0.0;
801 } else if (mask.icy(i, j) and mask.next_to_ice_free_ocean(i, j)) {
802 double P_ice = 0.5 * rho_ice * g * H(i, j),
803 P_water = average_water_column_pressure(H(i, j), bed(i, j), sea_level(i, j), rho_ice,
804 rho_ocean, g);
805
806 delta_p = P_ice - P_water;
807 } else {
808 delta_p = m_fill_value;
809 }
810
811 (*result)(i, j) = delta_p;
812 }
813 } catch (...) {
814 loop.failed();
815 }
816 loop.check();
817
818
819 return result;
820}
821
822/*! @brief Report average basal mass balance flux over the reporting interval (grounded or floating
823 areas) */
824class BMBSplit : public DiagAverageRate<IceModel> {
825public:
826 BMBSplit(const IceModel *m, AreaType flag)
828 m, flag == GROUNDED ? "basal_mass_flux_grounded" : "basal_mass_flux_floating",
830 m_kind(flag) {
831 assert(flag != BOTH);
832
833 auto ismip6 = m_config->get_flag("output.ISMIP6");
834
835 std::string name, description, standard_name;
836 if (m_kind == GROUNDED) {
837 name = ismip6 ? "libmassbfgr" : "basal_mass_flux_grounded";
838 description = "average basal mass flux over the reporting interval (grounded areas)";
839 standard_name = ismip6 ? "land_ice_basal_specific_mass_balance_flux" : "";
840 } else {
841 name = ismip6 ? "libmassbffl" : "basal_mass_flux_floating";
842 description = "average basal mass flux over the reporting interval (floating areas)";
843 standard_name = ismip6 ? "land_ice_basal_specific_mass_balance_flux" : "";
844 }
845
846 m_accumulator.metadata()["units"] = "kg m^-2";
847
848 m_vars = { { m_sys, name, *m_grid } };
849 m_vars[0]
850 .long_name(description)
851 .standard_name(standard_name)
852 .units("kg m^-2 s^-1")
853 .output_units("kg m^-2 year^-1");
854 m_vars[0]["cell_methods"] = "time: mean";
855 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
856 m_vars[0]["comment"] = "positive flux corresponds to ice gain";
857 }
858
859protected:
861 void update_impl(double dt) {
862 const array::Scalar &input = model->geometry_evolution().bottom_surface_mass_balance();
863 const auto &cell_type = model->geometry().cell_type;
864
865 double ice_density = m_config->get_number("constants.ice.density");
866
867 // the accumulator has the units of kg/m^2, computed as
868 //
869 // accumulator += BMB (m) * ice_density (kg / m^3)
870
871 array::AccessScope list{ &input, &cell_type, &m_accumulator };
872
873 for (auto p : m_grid->points()) {
874 const int i = p.i(), j = p.j();
875
876 if (m_kind == GROUNDED and cell_type.grounded(i, j)) {
877 m_accumulator(i, j) += input(i, j) * ice_density;
878 } else if (m_kind == SHELF and cell_type.ocean(i, j)) {
879 m_accumulator(i, j) += input(i, j) * ice_density;
880 } else {
881 m_accumulator(i, j) = 0.0;
882 }
883 }
884
885 m_interval_length += dt;
886 }
887};
888
889//! \brief Computes vertically-averaged ice hardness.
890class HardnessAverage : public Diag<IceModel> {
891public:
892 HardnessAverage(const IceModel *m);
893
894protected:
895 virtual std::shared_ptr<array::Array> compute_impl() const;
896};
897
899
900 // set metadata:
901 m_vars = { { m_sys, "hardav", *m_grid } };
902 m_vars[0]
903 .long_name("vertical average of ice hardness")
904 .set_units_without_validation(
905 "Pa s^(1/n)"); // n is the Glen exponent used by the SSA (shallow stress balance) flow law
906 m_vars[0]["valid_min"] = { 0.0 };
907 m_vars[0]["_FillValue"] = { m_fill_value };
908 m_vars[0]["comment"] = "units depend on the Glen exponent used by the flow law";
909}
910
911//! \brief Computes vertically-averaged ice hardness.
912std::shared_ptr<array::Array> HardnessAverage::compute_impl() const {
913
914 const rheology::FlowLaw *flow_law = model->stress_balance()->shallow()->flow_law().get();
915 if (flow_law == NULL) {
916 flow_law = model->stress_balance()->modifier()->flow_law().get();
917 if (flow_law == NULL) {
919 "Can't compute vertically-averaged hardness: no flow law is used.");
920 }
921 }
922
923 auto result = std::make_shared<array::Scalar>(m_grid, "hardav");
924 result->metadata() = m_vars[0];
925
926 const auto &cell_type = model->geometry().cell_type;
927
928 const array::Array3D &ice_enthalpy = model->energy_balance_model()->enthalpy();
929 const array::Scalar &ice_thickness = model->geometry().ice_thickness;
930
931 array::AccessScope list{ &cell_type, &ice_enthalpy, &ice_thickness, result.get() };
932 ParallelSection loop(m_grid->com);
933 try {
934 for (auto p : m_grid->points()) {
935 const int i = p.i(), j = p.j();
936
937 const double *Eij = ice_enthalpy.get_column(i, j);
938 const double H = ice_thickness(i, j);
939 if (cell_type.icy(i, j)) {
940 (*result)(i, j) = rheology::averaged_hardness(*flow_law, H, m_grid->kBelowHeight(H),
941 m_grid->z().data(), Eij);
942 } else { // put negative value below valid range
943 (*result)(i, j) = m_fill_value;
944 }
945 }
946 } catch (...) {
947 loop.failed();
948 }
949 loop.check();
950
951 return result;
952}
953
954//! \brief Computes a diagnostic field filled with processor rank values.
955class Rank : public Diag<IceModel> {
956public:
957 Rank(const IceModel *m);
958
959protected:
960 virtual std::shared_ptr<array::Array> compute_impl() const;
961};
962
964 m_vars = { { m_sys, "rank", *m_grid } };
965 m_vars[0]
966 .long_name("processor rank")
967 .units("1")
968 .set_time_dependent(false)
969 .set_output_type(io::PISM_INT);
970}
971
972std::shared_ptr<array::Array> Rank::compute_impl() const {
973
974 auto result = std::make_shared<array::Scalar>(m_grid, "rank");
975 result->metadata() = m_vars[0];
976
977 array::AccessScope list{ result.get() };
978
979 for (auto p : m_grid->points()) {
980 (*result)(p.i(), p.j()) = m_grid->rank();
981 }
982
983 return result;
984}
985
986//! \brief Computes CTS, CTS = E/E_s(p).
987class CTS : public Diag<IceModel> {
988public:
989 CTS(const IceModel *m);
990
991protected:
992 virtual std::shared_ptr<array::Array> compute_impl() const;
993};
994
996 m_vars = { { m_sys, "cts", *m_grid, m_grid->z() } };
997 m_vars[0]
998 .long_name("cts = E/E_s(p), so cold-temperate transition surface is at cts = 1")
999 .units("1");
1000}
1001
1002std::shared_ptr<array::Array> CTS::compute_impl() const {
1003
1004 std::shared_ptr<array::Array3D> result(new array::Array3D(m_grid, "cts", array::WITHOUT_GHOSTS, m_grid->z()));
1005 result->metadata() = m_vars[0];
1006
1008 *result);
1009
1010 return result;
1011}
1012
1013//! \brief Computes ice temperature from enthalpy.
1014class Temperature : public Diag<IceModel> {
1015public:
1016 Temperature(const IceModel *m);
1017
1018protected:
1019 virtual std::shared_ptr<array::Array> compute_impl() const;
1020};
1021
1023 m_vars = { { m_sys, "temp", *m_grid, m_grid->z() } };
1024 m_vars[0]
1025 .long_name("ice temperature")
1026 .standard_name("land_ice_temperature")
1027 .units("kelvin");
1028 m_vars[0]["valid_min"] = { 0.0 };
1029}
1030
1031std::shared_ptr<array::Array> Temperature::compute_impl() const {
1032
1033 std::shared_ptr<array::Array3D> result(
1035 result->metadata() = m_vars[0];
1036
1037 const auto &thickness = model->geometry().ice_thickness;
1038 const auto &enthalpy = model->energy_balance_model()->enthalpy();
1039
1040 auto EC = model->ctx()->enthalpy_converter();
1041
1042 double *Tij;
1043 const double *Enthij; // columns of these values
1044
1045 array::AccessScope list{result.get(), &enthalpy, &thickness};
1046
1047 ParallelSection loop(m_grid->com);
1048 try {
1049 for (auto p : m_grid->points()) {
1050 const int i = p.i(), j = p.j();
1051
1052 Tij = result->get_column(i,j);
1053 Enthij = enthalpy.get_column(i,j);
1054 for (unsigned int k=0; k <m_grid->Mz(); ++k) {
1055 const double depth = thickness(i,j) - m_grid->z(k);
1056 Tij[k] = EC->temperature(Enthij[k], EC->pressure(depth));
1057 }
1058 }
1059 } catch (...) {
1060 loop.failed();
1061 }
1062 loop.check();
1063
1064 return result;
1065}
1066
1067//! \brief Compute the pressure-adjusted temperature in degrees C corresponding
1068//! to ice temperature.
1069class TemperaturePA : public Diag<IceModel>
1070{
1071public:
1072 TemperaturePA(const IceModel *m);
1073protected:
1074 virtual std::shared_ptr<array::Array> compute_impl() const;
1075};
1076
1077
1079 : Diag<IceModel>(m) {
1080 m_vars = { { m_sys, "temp_pa", *m_grid, m_grid->z() } };
1081 m_vars[0]
1082 .long_name("pressure-adjusted ice temperature (degrees above pressure-melting point)")
1083 .units("deg_C");
1084 m_vars[0]["valid_max"] = {0};
1085}
1086
1087std::shared_ptr<array::Array> TemperaturePA::compute_impl() const {
1088 bool cold_mode = set_member(m_config->get_string("energy.model"), {"cold", "none"});
1089 double melting_point_temp = m_config->get_number("constants.fresh_water.melting_point_temperature");
1090
1091 auto result = std::make_shared<array::Array3D>(m_grid, "temp_pa", array::WITHOUT_GHOSTS, m_grid->z());
1092 result->metadata() = m_vars[0];
1093
1094 const auto &thickness = model->geometry().ice_thickness;
1095 const auto &enthalpy = model->energy_balance_model()->enthalpy();
1096
1097 auto EC = model->ctx()->enthalpy_converter();
1098
1099 double *Tij;
1100 const double *Enthij; // columns of these values
1101
1102 array::AccessScope list{result.get(), &enthalpy, &thickness};
1103
1104 ParallelSection loop(m_grid->com);
1105 try {
1106 for (auto pt : m_grid->points()) {
1107 const int i = pt.i(), j = pt.j();
1108
1109 Tij = result->get_column(i,j);
1110 Enthij = enthalpy.get_column(i,j);
1111 for (unsigned int k=0; k < m_grid->Mz(); ++k) {
1112 const double depth = thickness(i,j) - m_grid->z(k),
1113 p = EC->pressure(depth);
1114 Tij[k] = EC->pressure_adjusted_temperature(Enthij[k], p);
1115
1116 if (cold_mode) { // if ice is temperate then its pressure-adjusted temp
1117 // is 273.15
1118 if (EC->is_temperate_relaxed(Enthij[k],p) && (thickness(i,j) > 0)) {
1119 Tij[k] = melting_point_temp;
1120 }
1121 }
1122
1123 }
1124 }
1125 } catch (...) {
1126 loop.failed();
1127 }
1128 loop.check();
1129
1130 result->shift(-melting_point_temp);
1131
1132 return result;
1133}
1134
1135//! \brief Computes basal values of the pressure-adjusted temperature.
1136class TemperaturePABasal : public Diag<IceModel>
1137{
1138public:
1139 TemperaturePABasal(const IceModel *m);
1140protected:
1141 virtual std::shared_ptr<array::Array> compute_impl() const;
1142};
1143
1145 : Diag<IceModel>(m) {
1146 m_vars = { { m_sys, "temppabase", *m_grid } };
1147 m_vars[0].long_name("pressure-adjusted ice temperature at the base of ice").units("degree_Celsius");
1148}
1149
1150std::shared_ptr<array::Array> TemperaturePABasal::compute_impl() const {
1151
1152 bool cold_mode = set_member(m_config->get_string("energy.model"), {"cold", "none"});
1153 double melting_point_temp = m_config->get_number("constants.fresh_water.melting_point_temperature");
1154
1155 auto result = std::make_shared<array::Scalar>(m_grid, "temp_pa_base");
1156 result->metadata() = m_vars[0];
1157
1158 const auto &thickness = model->geometry().ice_thickness;
1159 const auto &enthalpy = model->energy_balance_model()->enthalpy();
1160
1161 auto EC = model->ctx()->enthalpy_converter();
1162
1163 array::AccessScope list{result.get(), &enthalpy, &thickness};
1164
1165 ParallelSection loop(m_grid->com);
1166 try {
1167 for (auto pt : m_grid->points()) {
1168 const int i = pt.i(), j = pt.j();
1169
1170 const auto *Enthij = enthalpy.get_column(i,j);
1171
1172 const double depth = thickness(i,j),
1173 p = EC->pressure(depth);
1174 (*result)(i,j) = EC->pressure_adjusted_temperature(Enthij[0], p);
1175
1176 if (cold_mode) { // if ice is temperate then its pressure-adjusted temp
1177 // is 273.15
1178 if (EC->is_temperate_relaxed(Enthij[0],p) && (thickness(i,j) > 0)) {
1179 (*result)(i,j) = melting_point_temp;
1180 }
1181 }
1182 }
1183 } catch (...) {
1184 loop.failed();
1185 }
1186 loop.check();
1187
1188 result->shift(-melting_point_temp);
1189
1190 return result;
1191}
1192
1193//! \brief Computes surface values of ice enthalpy.
1194class IceEnthalpySurface : public Diag<IceModel>
1195{
1196public:
1197 IceEnthalpySurface(const IceModel *m);
1198protected:
1199 virtual std::shared_ptr<array::Array> compute_impl() const;
1200};
1201
1203 : Diag<IceModel>(m) {
1204 m_vars = { { m_sys, "enthalpysurf", *m_grid } };
1205 m_vars[0].long_name("ice enthalpy at 1m below the ice surface").units("J kg^-1");
1206 m_vars[0]["_FillValue"] = {m_fill_value};
1207}
1208
1209std::shared_ptr<array::Array> IceEnthalpySurface::compute_impl() const {
1210
1211 auto result = std::make_shared<array::Scalar>(m_grid, "enthalpysurf");
1212 result->metadata() = m_vars[0];
1213
1214 // compute levels corresponding to 1 m below the ice surface:
1215
1216 const array::Array3D& ice_enthalpy = model->energy_balance_model()->enthalpy();
1217 const array::Scalar& ice_thickness = model->geometry().ice_thickness;
1218
1219 array::AccessScope list{&ice_thickness, result.get()};
1220
1221 for (auto p : m_grid->points()) {
1222 const int i = p.i(), j = p.j();
1223
1224 (*result)(i,j) = std::max(ice_thickness(i,j) - 1.0, 0.0);
1225 }
1226
1227 extract_surface(ice_enthalpy, *result, *result); // slice at 1 m below the surface
1228
1229 for (auto p : m_grid->points()) {
1230 const int i = p.i(), j = p.j();
1231
1232 if (ice_thickness(i,j) <= 1.0) {
1233 (*result)(i,j) = m_fill_value;
1234 }
1235 }
1236
1237 return result;
1238}
1239
1240//! \brief Computes enthalpy at the base of the ice.
1241class IceEnthalpyBasal : public Diag<IceModel>
1242{
1243public:
1244 IceEnthalpyBasal(const IceModel *m);
1245protected:
1246 virtual std::shared_ptr<array::Array> compute_impl() const;
1247};
1248
1250 : Diag<IceModel>(m) {
1251 m_vars = { { m_sys, "enthalpybase", *m_grid } };
1252 m_vars[0].long_name("ice enthalpy at the base of ice").units("J kg^-1");
1253 m_vars[0]["_FillValue"] = {m_fill_value};
1254}
1255
1256std::shared_ptr<array::Array> IceEnthalpyBasal::compute_impl() const {
1257
1258 auto result = std::make_shared<array::Scalar>(m_grid, "enthalpybase");
1259 result->metadata() = m_vars[0];
1260
1261 extract_surface(model->energy_balance_model()->enthalpy(), 0.0, *result); // z=0 slice
1262
1263 apply_mask(model->geometry().ice_thickness, m_fill_value, *result);
1264
1265 return result;
1266}
1267
1268//! \brief Computes ice temperature at the base of the ice.
1269class TemperatureBasal : public Diag<IceModel>
1270{
1271public:
1272 TemperatureBasal(const IceModel *m, AreaType area_type);
1273private:
1274 std::shared_ptr<array::Array> compute_impl() const;
1275
1277};
1278
1280 : Diag<IceModel>(m), m_area_type(area_type) {
1281
1282 std::string name, long_name, standard_name;
1283 switch (area_type) {
1284 case GROUNDED:
1285 name = "litempbotgr";
1286 long_name = "ice temperature at the bottom surface of grounded ice";
1287 standard_name = "temperature_at_base_of_ice_sheet_model";
1288 break;
1289 case SHELF:
1290 name = "litempbotfl";
1291 long_name = "ice temperature at the bottom surface of floating ice";
1292 standard_name = "temperature_at_base_of_ice_sheet_model";
1293 break;
1294 case BOTH:
1295 name = "tempbase";
1296 long_name = "ice temperature at the base of ice";
1297 standard_name = "land_ice_basal_temperature";
1298 break;
1299 }
1300
1301 m_vars = { { m_sys, name, *m_grid } };
1302 m_vars[0].long_name(long_name).standard_name(standard_name).units("kelvin");
1303 m_vars[0]["_FillValue"] = { m_fill_value };
1304}
1305
1306std::shared_ptr<array::Array> TemperatureBasal::compute_impl() const {
1307
1308 auto result = allocate<array::Scalar>("basal_temperature");
1309
1310 const auto &thickness = model->geometry().ice_thickness;
1311
1312 auto EC = model->ctx()->enthalpy_converter();
1313
1314 extract_surface(model->energy_balance_model()->enthalpy(), 0.0, *result); // z=0 (basal) slice
1315 // Now result contains basal enthalpy.
1316
1317 const auto &cell_type = model->geometry().cell_type;
1318
1319 array::AccessScope list{ &cell_type, result.get(), &thickness };
1320
1321 ParallelSection loop(m_grid->com);
1322 try {
1323 for (auto p : m_grid->points()) {
1324 const int i = p.i(), j = p.j();
1325
1326 double depth = thickness(i, j), pressure = EC->pressure(depth),
1327 T = EC->temperature((*result)(i, j), pressure);
1328
1329 if ((m_area_type == BOTH and cell_type.icy(i, j)) or
1330 (m_area_type == GROUNDED and cell_type.grounded_ice(i, j)) or
1331 (m_area_type == SHELF and cell_type.floating_ice(i, j))) {
1332 (*result)(i, j) = T;
1333 } else {
1334 (*result)(i, j) = m_fill_value;
1335 }
1336 }
1337 } catch (...) {
1338 loop.failed();
1339 }
1340 loop.check();
1341
1342 return result;
1343}
1344
1345//! \brief Computes ice temperature at the surface of the ice.
1346class TemperatureSurface : public Diag<IceModel> {
1347public:
1348 TemperatureSurface(const IceModel *m);
1349
1350protected:
1351 virtual std::shared_ptr<array::Array> compute_impl() const;
1352};
1353
1355 m_vars = { { m_sys, "tempsurf", *m_grid } };
1356 m_vars[0]
1357 .long_name("ice temperature at 1m below the ice surface")
1358 .standard_name("temperature_at_ground_level_in_snow_or_firn") // InitMIP "standard" name
1359 .units("kelvin");
1360 m_vars[0]["_FillValue"] = { m_fill_value };
1361}
1362
1363std::shared_ptr<array::Array> TemperatureSurface::compute_impl() const {
1364
1365 const array::Scalar &thickness = model->geometry().ice_thickness;
1366
1367 auto enth = IceEnthalpySurface(model).compute();
1368 auto result = array::cast<array::Scalar>(enth);
1369
1370 auto EC = model->ctx()->enthalpy_converter();
1371
1372 // result contains surface enthalpy; note that it is allocated by
1373 // IceEnthalpySurface::compute().
1374
1375 array::AccessScope list{ result.get(), &thickness };
1376
1377 double depth = 1.0, pressure = EC->pressure(depth);
1378 ParallelSection loop(m_grid->com);
1379 try {
1380 for (auto p : m_grid->points()) {
1381 const int i = p.i(), j = p.j();
1382
1383 if (thickness(i, j) > 1) {
1384 (*result)(i, j) = EC->temperature((*result)(i, j), pressure);
1385 } else {
1386 (*result)(i, j) = m_fill_value;
1387 }
1388 }
1389 } catch (...) {
1390 loop.failed();
1391 }
1392 loop.check();
1393
1394 result->metadata(0) = m_vars[0];
1395 return result;
1396}
1397
1398//! \brief Computes the liquid water fraction.
1399class LiquidFraction : public Diag<IceModel> {
1400public:
1401 LiquidFraction(const IceModel *m);
1402
1403protected:
1404 virtual std::shared_ptr<array::Array> compute_impl() const;
1405};
1406
1408 m_vars = { { m_sys, "liqfrac", *m_grid, m_grid->z() } };
1409 m_vars[0].long_name("liquid water fraction in ice (between 0 and 1)").units("1");
1410 m_vars[0]["valid_range"] = { 0.0, 1.0 };
1411}
1412
1413std::shared_ptr<array::Array> LiquidFraction::compute_impl() const {
1414
1415 std::shared_ptr<array::Array3D> result(
1416 new array::Array3D(m_grid, "liqfrac", array::WITHOUT_GHOSTS, m_grid->z()));
1417 result->metadata(0) = m_vars[0];
1418
1419 bool cold_mode = set_member(m_config->get_string("energy.model"), {"cold", "none"});
1420
1421 if (cold_mode) {
1422 result->set(0.0);
1423 } else {
1425 model->geometry().ice_thickness, *result);
1426 }
1427
1428 return result;
1429}
1430
1431//! \brief Computes the total thickness of temperate ice in a column.
1432class TemperateIceThickness : public Diag<IceModel> {
1433public:
1435
1436protected:
1437 virtual std::shared_ptr<array::Array> compute_impl() const;
1438};
1439
1441 m_vars = { { m_sys, "tempicethk", *m_grid } };
1442 m_vars[0].long_name("temperate ice thickness (total column content)").units("m");
1443 m_vars[0]["_FillValue"] = { m_fill_value };
1444}
1445
1446std::shared_ptr<array::Array> TemperateIceThickness::compute_impl() const {
1447
1448 auto result = allocate<array::Scalar>("tempicethk");
1449
1450 const auto &cell_type = model->geometry().cell_type;
1451 const auto &ice_enthalpy = model->energy_balance_model()->enthalpy();
1452 const auto &ice_thickness = model->geometry().ice_thickness;
1453
1454 array::AccessScope list{ &cell_type, result.get(), &ice_enthalpy, &ice_thickness };
1455
1456 auto EC = model->ctx()->enthalpy_converter();
1457
1458 ParallelSection loop(m_grid->com);
1459 try {
1460 for (auto p : m_grid->points()) {
1461 const int i = p.i(), j = p.j();
1462
1463 if (cell_type.icy(i, j)) {
1464 const double *Enth = ice_enthalpy.get_column(i, j);
1465 double H_temperate = 0.0;
1466 const double H = ice_thickness(i, j);
1467 const unsigned int ks = m_grid->kBelowHeight(H);
1468
1469 for (unsigned int k = 0; k < ks; ++k) { // FIXME issue #15
1470 double pressure = EC->pressure(H - m_grid->z(k));
1471
1472 if (EC->is_temperate_relaxed(Enth[k], pressure)) {
1473 H_temperate += m_grid->z(k + 1) - m_grid->z(k);
1474 }
1475 }
1476
1477 double pressure = EC->pressure(H - m_grid->z(ks));
1478 if (EC->is_temperate_relaxed(Enth[ks], pressure)) {
1479 H_temperate += H - m_grid->z(ks);
1480 }
1481
1482 (*result)(i, j) = H_temperate;
1483 } else {
1484 // ice-free
1485 (*result)(i, j) = m_fill_value;
1486 }
1487 }
1488 } catch (...) {
1489 loop.failed();
1490 }
1491 loop.check();
1492
1493 return result;
1494}
1495
1496//! \brief Computes the thickness of the basal layer of temperate ice.
1497class TemperateIceThicknessBasal : public Diag<IceModel> {
1498public:
1500
1501protected:
1502 virtual std::shared_ptr<array::Array> compute_impl() const;
1503};
1504
1506 m_vars = { { m_sys, "tempicethk_basal", *m_grid } };
1507 m_vars[0].long_name("thickness of the basal layer of temperate ice").units("m");
1508 m_vars[0]["_FillValue"] = { m_fill_value };
1509}
1510
1511/*!
1512 * Uses linear interpolation to go beyond vertical grid resolution.
1513 */
1514std::shared_ptr<array::Array> TemperateIceThicknessBasal::compute_impl() const {
1515
1516 auto result = allocate<array::Scalar>("tempicethk_basal");
1517
1518 auto EC = model->ctx()->enthalpy_converter();
1519
1520 const auto &cell_type = model->geometry().cell_type;
1521 const auto &ice_enthalpy = model->energy_balance_model()->enthalpy();
1522 const auto &ice_thickness = model->geometry().ice_thickness;
1523
1524 array::AccessScope list{ &cell_type, result.get(), &ice_thickness, &ice_enthalpy };
1525
1526 ParallelSection loop(m_grid->com);
1527 try {
1528 for (auto p : m_grid->points()) {
1529 const int i = p.i(), j = p.j();
1530
1531 double H = ice_thickness(i, j);
1532
1533 // if we have no ice, go on to the next grid point (this cell will be
1534 // marked as "missing" later)
1535 if (cell_type.ice_free(i, j)) {
1536 (*result)(i, j) = m_fill_value;
1537 continue;
1538 }
1539
1540 const double *Enth = ice_enthalpy.get_column(i, j);
1541
1542 unsigned int ks = m_grid->kBelowHeight(H);
1543
1544 unsigned int k = 0;
1545 double pressure = EC->pressure(H - m_grid->z(k));
1546 while (k <= ks) { // FIXME issue #15
1547 pressure = EC->pressure(H - m_grid->z(k));
1548
1549 if (EC->is_temperate_relaxed(Enth[k], pressure)) {
1550 k++;
1551 } else {
1552 break;
1553 }
1554 }
1555 // after this loop 'pressure' is equal to the pressure at the first level
1556 // that is cold
1557
1558 // no temperate ice at all; go to the next grid point
1559 if (k == 0) {
1560 (*result)(i, j) = 0.0;
1561 continue;
1562 }
1563
1564 // the whole column is temperate (except, possibly, some ice between
1565 // z(ks) and the total thickness; we ignore it)
1566 if (k == ks + 1) {
1567 (*result)(i, j) = m_grid->z(ks);
1568 continue;
1569 }
1570
1571 double pressure_0 = EC->pressure(H - m_grid->z(k - 1)), dz = m_grid->z(k) - m_grid->z(k - 1),
1572 slope1 = (Enth[k] - Enth[k - 1]) / dz,
1573 slope2 = (EC->enthalpy_cts(pressure) - EC->enthalpy_cts(pressure_0)) / dz;
1574
1575 if (slope1 != slope2) {
1576 (*result)(i, j) =
1577 m_grid->z(k - 1) + (EC->enthalpy_cts(pressure_0) - Enth[k - 1]) / (slope1 - slope2);
1578
1579 // check if the resulting thickness is valid:
1580 (*result)(i, j) = std::max((*result)(i, j), m_grid->z(k - 1));
1581 (*result)(i, j) = std::min((*result)(i, j), m_grid->z(k));
1582 } else {
1584 "Linear interpolation of the thickness of"
1585 " the basal temperate layer failed:\n"
1586 "(i=%d, j=%d, k=%d, ks=%d)\n",
1587 i, j, k, ks);
1588 }
1589 }
1590 } catch (...) {
1591 loop.failed();
1592 }
1593 loop.check();
1594
1595
1596 return result;
1597}
1598
1599namespace scalar {
1600
1601//! \brief Computes the total ice volume in glacierized areas.
1602class IceVolumeGlacierized : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1603public:
1605 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_volume_glacierized") {
1606
1607 set_units("m^3", "m^3");
1608 m_variable["long_name"] = "volume of the ice in glacierized areas";
1609 m_variable["valid_min"] = { 0.0 };
1610 }
1611 double compute() {
1612 return ice_volume(model->geometry(),
1613 m_config->get_number("output.ice_free_thickness_standard"));
1614 }
1615};
1616
1617//! \brief Computes the total ice volume.
1618class IceVolume : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1619public:
1621
1622 set_units("m^3", "m^3");
1623 m_variable["long_name"] = "volume of the ice, including seasonal cover";
1624 m_variable["valid_min"] = { 0.0 };
1625 }
1626
1627 double compute() {
1628 return ice_volume(model->geometry(), 0.0);
1629 }
1630};
1631
1632//! \brief Computes the total ice volume which is relevant for sea-level
1633class SeaLevelRisePotential : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1634public:
1636 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "sea_level_rise_potential") {
1637
1638 set_units("m", "m");
1639 m_variable["long_name"] = "the sea level rise that would result if all the ice were melted";
1640 m_variable["valid_min"] = { 0.0 };
1641 }
1642
1643 double compute() {
1645 m_config->get_number("output.ice_free_thickness_standard"));
1646 }
1647};
1648
1649//! \brief Computes the rate of change of the total ice volume in glacierized areas.
1650class IceVolumeRateOfChangeGlacierized : public TSDiag<TSRateDiagnostic, IceModel> {
1651public:
1653 : TSDiag<TSRateDiagnostic, IceModel>(m, "tendency_of_ice_volume_glacierized") {
1654
1655 set_units("m^3 s^-1", "m^3 year^-1");
1656 m_variable["long_name"] = "rate of change of the ice volume in glacierized areas";
1657 }
1658
1659 double compute() {
1660 return ice_volume(model->geometry(),
1661 m_config->get_number("output.ice_free_thickness_standard"));
1662 }
1663};
1664
1665//! \brief Computes the rate of change of the total ice volume.
1666class IceVolumeRateOfChange : public TSDiag<TSRateDiagnostic, IceModel> {
1667public:
1669 : TSDiag<TSRateDiagnostic, IceModel>(m, "tendency_of_ice_volume") {
1670
1671 set_units("m^3 s^-1", "m^3 year^-1");
1672 m_variable["long_name"] = "rate of change of the ice volume, including seasonal cover";
1673 }
1674
1675 double compute() {
1676 return ice_volume(model->geometry(), 0.0);
1677 }
1678};
1679
1680//! \brief Computes the total ice area.
1681class IceAreaGlacierized : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1682public:
1684 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_area_glacierized") {
1685
1686 set_units("m^2", "m^2");
1687 m_variable["long_name"] = "glacierized area";
1688 m_variable["valid_min"] = { 0.0 };
1689 }
1690
1691 double compute() {
1692 return ice_area(model->geometry(), m_config->get_number("output.ice_free_thickness_standard"));
1693 }
1694};
1695
1696//! \brief Computes the total mass of the ice not displacing sea water.
1697class IceMassNotDisplacingSeaWater : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1698public:
1700 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "limnsw") {
1701
1702 set_units("kg", "kg");
1703 m_variable["long_name"] = "mass of the ice not displacing sea water";
1704 m_variable["standard_name"] = "land_ice_mass_not_displacing_sea_water";
1705 m_variable["valid_min"] = { 0.0 };
1706 }
1707
1708 double compute() {
1709
1710 const double thickness_standard = m_config->get_number("output.ice_free_thickness_standard"),
1711 ice_density = m_config->get_number("constants.ice.density"),
1712 ice_volume =
1713 ice_volume_not_displacing_seawater(model->geometry(), thickness_standard),
1714 ice_mass = ice_volume * ice_density;
1715
1716 return ice_mass;
1717 }
1718};
1719
1720//! \brief Computes the total ice mass in glacierized areas.
1721class IceMassGlacierized : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1722public:
1724 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_mass_glacierized") {
1725
1726 set_units("kg", "kg");
1727 m_variable["long_name"] = "mass of the ice in glacierized areas";
1728 m_variable["valid_min"] = { 0.0 };
1729 }
1730
1731 double compute() {
1732 double ice_density = m_config->get_number("constants.ice.density"),
1733 thickness_standard = m_config->get_number("output.ice_free_thickness_standard");
1734 return ice_volume(model->geometry(), thickness_standard) * ice_density;
1735 }
1736};
1737
1738//! \brief Computes the total ice mass.
1739class IceMass : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1740public:
1742
1743 if (m_config->get_flag("output.ISMIP6")) {
1744 m_variable.set_name("lim");
1745 }
1746
1747 set_units("kg", "kg");
1748 m_variable["long_name"] = "mass of the ice, including seasonal cover";
1749 m_variable["standard_name"] = "land_ice_mass";
1750 m_variable["valid_min"] = { 0.0 };
1751 }
1752
1753 double compute() {
1754 return (ice_volume(model->geometry(), 0.0) * m_config->get_number("constants.ice.density"));
1755 }
1756};
1757
1758//! \brief Computes the rate of change of the total ice mass in glacierized areas.
1759class IceMassRateOfChangeGlacierized : public TSDiag<TSRateDiagnostic, IceModel> {
1760public:
1762 : TSDiag<TSRateDiagnostic, IceModel>(m, "tendency_of_ice_mass_glacierized") {
1763
1764 set_units("kg s^-1", "Gt year^-1");
1765 m_variable["long_name"] = "rate of change of the ice mass in glacierized areas";
1766 }
1767
1768 double compute() {
1769 double ice_density = m_config->get_number("constants.ice.density"),
1770 thickness_threshold = m_config->get_number("output.ice_free_thickness_standard");
1771 return ice_volume(model->geometry(), thickness_threshold) * ice_density;
1772 }
1773};
1774
1775//! \brief Computes the rate of change of the total ice mass due to flow (influx due to
1776//! prescribed constant-in-time ice thickness).
1777/*!
1778 * This is the change in mass resulting from prescribing (fixing) ice thickness.
1779 */
1780class IceMassRateOfChangeDueToFlow : public TSDiag<TSFluxDiagnostic, IceModel> {
1781public:
1783 : TSDiag<TSFluxDiagnostic, IceModel>(m, "tendency_of_ice_mass_due_to_flow") {
1784
1785 set_units("kg s^-1", "Gt year^-1");
1786 m_variable["long_name"] = "rate of change of the mass of ice due to flow"
1787 " (i.e. prescribed ice thickness)";
1788 }
1789
1790 double compute() {
1791
1792 const double ice_density = m_config->get_number("constants.ice.density");
1793
1796
1797 auto cell_area = m_grid->cell_area();
1798
1799 array::AccessScope list{ &dH, &dV };
1800
1801 double volume_change = 0.0;
1802 for (auto p : m_grid->points()) {
1803 const int i = p.i(), j = p.j();
1804 // m * m^2 = m^3
1805 volume_change += (dH(i, j) + dV(i, j)) * cell_area;
1806 }
1807
1808 // (kg/m^3) * m^3 = kg
1809 return ice_density * GlobalSum(m_grid->com, volume_change);
1810 }
1811};
1812
1813//! \brief Computes the rate of change of the total ice mass.
1814class IceMassRateOfChange : public TSDiag<TSRateDiagnostic, IceModel> {
1815public:
1816 IceMassRateOfChange(IceModel *m) : TSDiag<TSRateDiagnostic, IceModel>(m, "tendency_of_ice_mass") {
1817
1818 set_units("kg s^-1", "Gt year^-1");
1819 m_variable["long_name"] = "rate of change of the mass of ice, including seasonal cover";
1820 }
1821
1822 double compute() {
1823 const double ice_density = m_config->get_number("constants.ice.density");
1824 return ice_volume(model->geometry(), 0.0) * ice_density;
1825 }
1826};
1827
1828
1829//! \brief Computes the total volume of the temperate ice in glacierized areas.
1830class IceVolumeGlacierizedTemperate : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1831public:
1833 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_volume_glacierized_temperate") {
1834
1835 set_units("m^3", "m^3");
1836 m_variable["long_name"] = "volume of temperate ice in glacierized areas";
1837 m_variable["valid_min"] = { 0.0 };
1838 }
1839
1840 double compute() {
1841 auto thickness_threshold = m_config->get_number("output.ice_free_thickness_standard");
1844 thickness_threshold);
1845 }
1846};
1847
1848//! \brief Computes the total volume of the temperate ice.
1849class IceVolumeTemperate : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1850public:
1852 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_volume_temperate") {
1853
1854 set_units("m^3", "m^3");
1855 m_variable["long_name"] = "volume of temperate ice, including seasonal cover";
1856 m_variable["valid_min"] = { 0.0 };
1857 }
1858
1864};
1865
1866//! \brief Computes the total volume of the cold ice in glacierized areas.
1867class IceVolumeGlacierizedCold : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1868public:
1870 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_volume_glacierized_cold") {
1871
1872 set_units("m^3", "m^3");
1873 m_variable["long_name"] = "volume of cold ice in glacierized areas";
1874 m_variable["valid_min"] = { 0.0 };
1875 }
1876
1877 double compute() {
1878 auto thickness_threshold = m_config->get_number("output.ice_free_thickness_standard");
1881 thickness_threshold);
1882 }
1883};
1884
1885//! \brief Computes the total volume of the cold ice.
1886class IceVolumeCold : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1887public:
1889
1890 set_units("m^3", "m^3");
1891 m_variable["long_name"] = "volume of cold ice, including seasonal cover";
1892 m_variable["valid_min"] = { 0.0 };
1893 }
1894
1900};
1901
1902//! \brief Computes the total area of the temperate ice.
1903class IceAreaGlacierizedTemperateBase : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1904public:
1906 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_area_glacierized_temperate_base") {
1907
1908 set_units("m^2", "m^2");
1909 m_variable["long_name"] = "glacierized area where basal ice is temperate";
1910 m_variable["valid_min"] = { 0.0 };
1911 }
1912
1913 double compute() {
1914 auto thickness_threshold = m_config->get_number("output.ice_free_thickness_standard");
1917 thickness_threshold);
1918 }
1919};
1920
1921//! \brief Computes the total area of the cold ice.
1922class IceAreaGlacierizedColdBase : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1923public:
1925 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_area_glacierized_cold_base") {
1926
1927 set_units("m^2", "m^2");
1928 m_variable["long_name"] = "glacierized area where basal ice is cold";
1929 m_variable["valid_min"] = { 0.0 };
1930 }
1931
1932 double compute() {
1933 auto thickness_threshold = m_config->get_number("output.ice_free_thickness_standard");
1936 thickness_threshold);
1937 }
1938};
1939
1940//! \brief Computes the total ice enthalpy in glacierized areas.
1941class IceEnthalpyGlacierized : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1942public:
1944 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_enthalpy_glacierized") {
1945
1946 set_units("J", "J");
1947 m_variable["long_name"] = "enthalpy of the ice in glacierized areas";
1948 m_variable["valid_min"] = { 0.0 };
1949 }
1950
1951 double compute() {
1952 return energy::total_ice_enthalpy(m_config->get_number("output.ice_free_thickness_standard"),
1955 }
1956};
1957
1958//! \brief Computes the total ice enthalpy.
1959class IceEnthalpy : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1960public:
1962
1963 set_units("J", "J");
1964 m_variable["long_name"] = "enthalpy of the ice, including seasonal cover";
1965 m_variable["valid_min"] = { 0.0 };
1966 }
1967
1972};
1973
1974//! \brief Computes the total grounded ice area.
1975class IceAreaGlacierizedGrounded : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1976public:
1978 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_area_glacierized_grounded") {
1979
1980 if (m_config->get_flag("output.ISMIP6")) {
1981 m_variable.set_name("iareagr");
1982 }
1983
1984 set_units("m^2", "m^2");
1985 m_variable["long_name"] = "area of grounded ice in glacierized areas";
1986 m_variable["standard_name"] = "grounded_ice_sheet_area";
1987 m_variable["valid_min"] = { 0.0 };
1988 }
1989
1990 double compute() {
1992 m_config->get_number("output.ice_free_thickness_standard"));
1993 }
1994};
1995
1996//! \brief Computes the total floating ice area.
1997class IceAreaGlacierizedShelf : public TSDiag<TSSnapshotDiagnostic, IceModel> {
1998public:
2000 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_area_glacierized_floating") {
2001
2002 if (m_config->get_flag("output.ISMIP6")) {
2003 m_variable.set_name("iareafl");
2004 }
2005
2006 set_units("m^2", "m^2");
2007 m_variable["long_name"] = "area of ice shelves in glacierized areas";
2008 m_variable["standard_name"] = "floating_ice_shelf_area";
2009 m_variable["valid_min"] = { 0.0 };
2010 }
2011
2012 double compute() {
2014 m_config->get_number("output.ice_free_thickness_standard"));
2015 }
2016};
2017
2018//! \brief Computes the total grounded ice volume.
2019class IceVolumeGlacierizedGrounded : public TSDiag<TSSnapshotDiagnostic, IceModel> {
2020public:
2022 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_volume_glacierized_grounded") {
2023
2024 set_units("m^3", "m^3");
2025 m_variable["long_name"] = "volume of grounded ice in glacierized areas";
2026 m_variable["valid_min"] = { 0.0 };
2027 }
2028
2029 double compute() {
2030 const auto &cell_type = model->geometry().cell_type;
2031
2032 const array::Scalar &ice_thickness = model->geometry().ice_thickness;
2033
2034 const double thickness_threshold = m_config->get_number("output.ice_free_thickness_standard"),
2035 cell_area = m_grid->cell_area();
2036
2037 array::AccessScope list{ &ice_thickness, &cell_type };
2038
2039 double volume = 0.0;
2040 for (auto p : m_grid->points()) {
2041 const int i = p.i(), j = p.j();
2042
2043 const double H = ice_thickness(i, j);
2044
2045 if (cell_type.grounded(i, j) and H >= thickness_threshold) {
2046 volume += cell_area * H;
2047 }
2048 }
2049
2050 return GlobalSum(m_grid->com, volume);
2051 }
2052};
2053
2054//! \brief Computes the total floating ice volume.
2055class IceVolumeGlacierizedShelf : public TSDiag<TSSnapshotDiagnostic, IceModel> {
2056public:
2058 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "ice_volume_glacierized_floating") {
2059
2060 set_units("m^3", "m^3");
2061 m_variable["long_name"] = "volume of ice shelves in glacierized areas";
2062 m_variable["valid_min"] = { 0.0 };
2063 }
2064
2065 double compute() {
2066 const auto &cell_type = model->geometry().cell_type;
2067
2068 const array::Scalar &ice_thickness = model->geometry().ice_thickness;
2069
2070 const double thickness_threshold = m_config->get_number("output.ice_free_thickness_standard"),
2071 cell_area = m_grid->cell_area();
2072
2073 array::AccessScope list{ &ice_thickness, &cell_type };
2074
2075 double volume = 0.0;
2076 for (auto p : m_grid->points()) {
2077 const int i = p.i(), j = p.j();
2078
2079 const double H = ice_thickness(i, j);
2080
2081 if (cell_type.ocean(i, j) and H >= thickness_threshold) {
2082 volume += cell_area * H;
2083 }
2084 }
2085
2086 return GlobalSum(m_grid->com, volume);
2087 }
2088};
2089
2090//! \brief Reports the mass continuity time step.
2091class TimeStepLength : public TSDiag<TSSnapshotDiagnostic, IceModel> {
2092public:
2094
2095 set_units("second", "year");
2096 m_variable["long_name"] = "mass continuity time step";
2097 m_variable["valid_min"] = { 0.0 };
2098 }
2099
2100 double compute() {
2101 return model->dt();
2102 }
2103};
2104
2105//! \brief Reports the mass continuity time step.
2106class TimeStepRatio : public TSDiag<TSSnapshotDiagnostic, IceModel> {
2107public:
2109
2110 set_units("1", "1");
2111 m_variable["long_name"] = "ratio of max. allowed time steps "
2112 "according to CFL and SIA diffusivity criteria";
2113 m_variable["valid_min"] = { 0.0 };
2114 m_variable["_FillValue"] = { -1.0 };
2115 }
2116
2117 double compute() {
2118
2119 const auto *stress_balance = model->stress_balance();
2120
2121 auto cfl_2d = stress_balance->max_timestep_cfl_2d();
2122 auto cfl_3d = stress_balance->max_timestep_cfl_3d();
2123
2124 auto dt_diff = max_timestep_diffusivity(stress_balance->max_diffusivity(),
2125 model->grid()->dx(),
2126 model->grid()->dy(),
2127 m_config->get_number("time_stepping.adaptive_ratio"));
2128
2129 auto dt_cfl = std::min(cfl_2d.dt_max, cfl_3d.dt_max);
2130
2131 if (dt_cfl.finite() and dt_diff.finite() and dt_diff.value() > 0.0) {
2132 return dt_cfl.value() / dt_diff.value();
2133 }
2134
2135 return -1.0;
2136 }
2137};
2138
2139//! \brief Reports maximum diffusivity.
2140class MaxDiffusivity : public TSDiag<TSSnapshotDiagnostic, IceModel> {
2141public:
2142 MaxDiffusivity(const IceModel *m) : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "max_diffusivity") {
2143
2144 set_units("m^2 s^-1", "m^2 s^-1");
2145 m_variable["long_name"] = "maximum diffusivity of the flow (usually from the SIA model)";
2146 m_variable["valid_min"] = { 0.0 };
2147 }
2148
2149 double compute() {
2151 }
2152};
2153
2154//! \brief Reports the maximum horizontal absolute velocity component over the grid.
2155/*!
2156 * This is the value used by the adaptive time-stepping code in the CFL condition
2157 * for horizontal advection (i.e. in energy and mass conservation time steps).
2158 *
2159 * This is not the maximum horizontal speed, but rather the maximum of components.
2160 *
2161 * Note that this picks up the value computed during the time-step taken at a
2162 * reporting time. (It is not the "average over the reporting interval computed using
2163 * differencing in time", as other rate-of-change diagnostics.)
2164 */
2165class MaxHorizontalVelocity : public TSDiag<TSSnapshotDiagnostic, IceModel> {
2166public:
2168 : TSDiag<TSSnapshotDiagnostic, IceModel>(m, "max_horizontal_vel") {
2169
2170 set_units("m second^-1", "m year^-1");
2171 m_variable["long_name"] = "max(max(abs(u)), max(abs(v))) for the horizontal velocity of ice"
2172 " over volume of the ice in last time step during time-series reporting interval";
2173 m_variable["valid_min"] = { 0.0 };
2174 }
2175
2176 double compute() {
2178
2179 return std::max(cfl.u_max, cfl.v_max);
2180 }
2181};
2182
2183/*!
2184 * Return total mass change due to one of the terms in the mass continuity equation.
2185 *
2186 * Possible terms are
2187 *
2188 * - SMB: surface mass balance
2189 * - BMB: basal mass balance
2190 * - FLOW: ice flow
2191 * - ERROR: numerical flux needed to preserve non-negativity of thickness
2192 *
2193 * This computation can be restricted to grounded and floating areas
2194 * using the `area` argument.
2195 *
2196 * - BOTH: include all contributions
2197 * - GROUNDED: include grounded areas only
2198 * - SHELF: include floating areas only
2199 *
2200 * When computing mass changes due to flow it is important to remember
2201 * that ice mass in a cell can be represented by its thickness *or* an
2202 * "area specific volume". Transferring mass from one representation
2203 * to the other does not change the mass in a cell. This explains the
2204 * special case used when `term == FLOW`. (Note that surface and basal
2205 * mass balances do not affect the area specific volume field.)
2206 */
2207double mass_change(const IceModel *model, TermType term, AreaType area) {
2208 const Grid &grid = *model->grid();
2209 const Config &config = *grid.ctx()->config();
2210
2211 const double ice_density = config.get_number("constants.ice.density"),
2212 cell_area = grid.cell_area();
2213
2214 const auto &cell_type = model->geometry().cell_type;
2215
2216 const array::Scalar *thickness_change = nullptr;
2217
2218 switch (term) {
2219 case FLOW:
2220 thickness_change = &model->geometry_evolution().thickness_change_due_to_flow();
2221 break;
2222 case SMB:
2223 thickness_change = &model->geometry_evolution().top_surface_mass_balance();
2224 break;
2225 case BMB:
2226 thickness_change = &model->geometry_evolution().bottom_surface_mass_balance();
2227 break;
2228 case ERROR:
2229 thickness_change = &model->geometry_evolution().conservation_error();
2230 break;
2231 default:
2232 // can't happen
2233 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "invalid term type");
2234 }
2235
2236 const array::Scalar &dV_flow =
2238
2239 array::AccessScope list{ &cell_type, thickness_change };
2240
2241 if (term == FLOW) {
2242 list.add(dV_flow);
2243 }
2244
2245 double volume_change = 0.0;
2246 for (auto p : grid.points()) {
2247 const int i = p.i(), j = p.j();
2248
2249 if ((area == BOTH) or (area == GROUNDED and cell_type.grounded(i, j)) or
2250 (area == SHELF and cell_type.ocean(i, j))) {
2251
2252 double dV = term == FLOW ? dV_flow(i, j) : 0.0;
2253
2254 // m^3 = m^2 * m
2255 volume_change += cell_area * ((*thickness_change)(i, j) + dV);
2256 }
2257 }
2258
2259 // (kg / m^3) * m^3 = kg
2260 return ice_density * GlobalSum(grid.com, volume_change);
2261}
2262
2263//! \brief Reports the total bottom surface ice flux.
2264class IceMassFluxBasal : public TSDiag<TSFluxDiagnostic, IceModel> {
2265public:
2267 : TSDiag<TSFluxDiagnostic, IceModel>(m, "tendency_of_ice_mass_due_to_basal_mass_flux") {
2268
2269 if (m_config->get_flag("output.ISMIP6")) {
2270 m_variable.set_name("tendlibmassbf");
2271 }
2272
2273 set_units("kg s^-1", "Gt year^-1");
2274 m_variable["long_name"] = "total over ice domain of bottom surface ice mass flux";
2275 m_variable["standard_name"] = "tendency_of_land_ice_mass_due_to_basal_mass_balance";
2276 m_variable["comment"] = "positive means ice gain";
2277 }
2278
2279 double compute() {
2280 return mass_change(model, BMB, BOTH);
2281 }
2282};
2283
2284//! \brief Reports the total top surface ice flux.
2285class IceMassFluxSurface : public TSDiag<TSFluxDiagnostic, IceModel> {
2286public:
2288 : TSDiag<TSFluxDiagnostic, IceModel>(m, "tendency_of_ice_mass_due_to_surface_mass_flux") {
2289
2290 if (m_config->get_flag("output.ISMIP6")) {
2291 m_variable.set_name("tendacabf");
2292 }
2293
2294 set_units("kg s^-1", "Gt year^-1");
2295 m_variable["long_name"] = "total over ice domain of top surface ice mass flux";
2296 m_variable["standard_name"] = "tendency_of_land_ice_mass_due_to_surface_mass_balance";
2297 m_variable["comment"] = "positive means ice gain";
2298 }
2299
2300 double compute() {
2301 return mass_change(model, SMB, BOTH);
2302 }
2303};
2304
2305//! \brief Reports the total basal ice flux over the grounded region.
2306class IceMassFluxBasalGrounded : public TSDiag<TSFluxDiagnostic, IceModel> {
2307public:
2309 : TSDiag<TSFluxDiagnostic, IceModel>(m, "basal_mass_flux_grounded") {
2310
2311 set_units("kg s^-1", "Gt year^-1");
2312 m_variable["long_name"] = "total over grounded ice domain of basal mass flux";
2313 m_variable["standard_name"] = "tendency_of_land_ice_mass_due_to_basal_mass_balance";
2314 m_variable["comment"] = "positive means ice gain";
2315 }
2316
2317 double compute() {
2318 return mass_change(model, BMB, GROUNDED);
2319 }
2320};
2321
2322//! \brief Reports the total sub-shelf ice flux.
2323class IceMassFluxBasalFloating : public TSDiag<TSFluxDiagnostic, IceModel> {
2324public:
2326 : TSDiag<TSFluxDiagnostic, IceModel>(m, "basal_mass_flux_floating") {
2327
2328 if (m_config->get_flag("output.ISMIP6")) {
2329 m_variable.set_name("tendlibmassbffl");
2330 }
2331
2332 set_units("kg s^-1", "Gt year^-1");
2333 m_variable["long_name"] = "total sub-shelf ice flux";
2334 m_variable["standard_name"] = "tendency_of_land_ice_mass_due_to_basal_mass_balance";
2335 m_variable["comment"] = "positive means ice gain";
2336 }
2337
2338 double compute() {
2339 return mass_change(model, BMB, SHELF);
2340 }
2341};
2342
2343//! \brief Reports the total numerical mass flux needed to preserve
2344//! non-negativity of ice thickness.
2345class IceMassFluxConservationError : public TSDiag<TSFluxDiagnostic, IceModel> {
2346public:
2348 : TSDiag<TSFluxDiagnostic, IceModel>(m, "tendency_of_ice_mass_due_to_conservation_error") {
2349
2350 set_units("kg s^-1", "Gt year^-1");
2351 m_variable["long_name"] = "total numerical flux needed to preserve non-negativity"
2352 " of ice thickness";
2353 m_variable["comment"] = "positive means ice gain";
2354 }
2355
2356 double compute() {
2357 return mass_change(model, ERROR, BOTH);
2358 }
2359};
2360
2361//! \brief Reports the total discharge flux.
2362class IceMassFluxDischarge : public TSDiag<TSFluxDiagnostic, IceModel> {
2363public:
2365 : TSDiag<TSFluxDiagnostic, IceModel>(m, "tendency_of_ice_mass_due_to_discharge") {
2366
2367 if (m_config->get_flag("output.ISMIP6")) {
2368 m_variable.set_name("tendlifmassbf");
2369 }
2370
2371 set_units("kg s^-1", "Gt year^-1");
2372 m_variable["long_name"] = "discharge flux (frontal melt, calving, forced retreat)";
2373 m_variable["standard_name"] = "tendency_of_land_ice_mass_due_to_calving_and_ice_front_melting";
2374 m_variable["comment"] = "positive means ice gain";
2375 }
2376
2377 double compute() {
2378 const double ice_density = m_config->get_number("constants.ice.density");
2379
2380 const array::Scalar &calving = model->calving();
2381 const array::Scalar &frontal_melt = model->frontal_melt();
2382 const array::Scalar &forced_retreat = model->forced_retreat();
2383
2384 auto cell_area = m_grid->cell_area();
2385
2386 double volume_change = 0.0;
2387
2388 array::AccessScope list{ &calving, &frontal_melt, &forced_retreat };
2389
2390 for (auto p : m_grid->points()) {
2391 const int i = p.i(), j = p.j();
2392 // m^2 * m = m^3
2393 volume_change += cell_area * (calving(i, j) + frontal_melt(i, j) + forced_retreat(i, j));
2394 }
2395
2396 // (kg/m^3) * m^3 = kg
2397 return ice_density * GlobalSum(m_grid->com, volume_change);
2398 }
2399};
2400
2401//! \brief Reports the total calving flux.
2402class IceMassFluxCalving : public TSDiag<TSFluxDiagnostic, IceModel> {
2403public:
2405 : TSDiag<TSFluxDiagnostic, IceModel>(m, "tendency_of_ice_mass_due_to_calving") {
2406
2407 if (m_config->get_flag("output.ISMIP6")) {
2408 m_variable.set_name("tendlicalvf");
2409 }
2410
2411 set_units("kg s^-1", "Gt year^-1");
2412 m_variable["long_name"] = "calving flux";
2413 m_variable["standard_name"] = "tendency_of_land_ice_mass_due_to_calving";
2414 m_variable["comment"] = "positive means ice gain";
2415 }
2416
2417 double compute() {
2418 const double ice_density = m_config->get_number("constants.ice.density");
2419
2420 const array::Scalar &calving = model->calving();
2421
2422 auto cell_area = m_grid->cell_area();
2423
2424 double volume_change = 0.0;
2425
2426 array::AccessScope list{ &calving };
2427
2428 for (auto p : m_grid->points()) {
2429 const int i = p.i(), j = p.j();
2430 // m^2 * m = m^3
2431 volume_change += cell_area * calving(i, j);
2432 }
2433
2434 // (kg/m^3) * m^3 = kg
2435 return ice_density * GlobalSum(m_grid->com, volume_change);
2436 }
2437};
2438
2439//! @brief Reports the total flux across the grounding line.
2440class IceMassFluxAtGroundingLine : public TSDiag<TSFluxDiagnostic, IceModel> {
2441public:
2443 : TSDiag<TSFluxDiagnostic, IceModel>(m, "grounding_line_flux") {
2444
2445 if (m_config->get_flag("output.ISMIP6")) {
2446 m_variable.set_name("tendligroundf");
2447 m_variable["standard_name"] = "tendency_of_grounded_ice_mass";
2448 }
2449
2450 set_units("kg s^-1", "Gt year^-1");
2451 m_variable["long_name"] = "total ice flux across the grounding line";
2452 m_variable["comment"] = "negative flux corresponds to ice loss into the ocean";
2453 }
2454
2459};
2460
2461} // end of namespace scalar
2462
2463
2464//! \brief Computes dHdt, the ice thickness rate of change.
2465class ThicknessRateOfChange : public Diag<IceModel> {
2466public:
2468 : Diag<IceModel>(m), m_last_thickness(m_grid, "last_ice_thickness"), m_interval_length(0.0) {
2469
2470 auto ismip6 = m_config->get_flag("output.ISMIP6");
2471
2472 // set metadata:
2473 m_vars = { { m_sys, ismip6 ? "dlithkdt" : "dHdt", *m_grid } };
2474 m_vars[0]
2475 .long_name("ice thickness rate of change")
2476 .standard_name("tendency_of_land_ice_thickness")
2477 .units("m s^-1")
2478 .output_units("m year^-1");
2479
2480 auto large_number = to_internal(1e6);
2481
2482 m_vars[0]["valid_range"] = { -large_number, large_number };
2483 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
2484 m_vars[0]["cell_methods"] = "time: mean";
2485
2487 .long_name(
2488 "ice thickness at the time of the last report of the rate of change of ice thickness")
2489 .units("m")
2490 .standard_name("land_ice_thickness");
2491 }
2492
2493protected:
2494 std::shared_ptr<array::Array> compute_impl() const {
2495
2496 auto result = std::make_shared<array::Scalar>(m_grid, "dHdt");
2497 result->metadata() = m_vars[0];
2498
2499 if (m_interval_length > 0.0) {
2500 model->geometry().ice_thickness.add(-1.0, m_last_thickness, *result);
2501 result->scale(1.0 / m_interval_length);
2502 } else {
2503 result->set(m_fill_value);
2504 }
2505
2506 return result;
2507 }
2508
2513
2514 void update_impl(double dt) {
2515 m_interval_length += dt;
2516 }
2517
2520};
2521
2522//! \brief Computes latitude and longitude bounds.
2523class LatLonBounds : public Diag<IceModel> {
2524public:
2525 LatLonBounds(const IceModel *m, const std::string &var_name, const std::string &proj_string);
2526
2527protected:
2528 virtual std::shared_ptr<array::Array> compute_impl() const;
2529
2531};
2532
2533LatLonBounds::LatLonBounds(const IceModel *m, const std::string &var_name,
2534 const std::string &proj_string)
2535 : Diag<IceModel>(m) {
2536 assert(var_name == "lat" || var_name == "lon");
2537 m_var_name = var_name;
2538
2539 // set metadata:
2540 m_vars = { { m_sys, m_var_name + "_bnds", *m_grid, { 0.0, 1.0, 2.0, 3.0 } } };
2541 m_vars[0].dimension("z").clear().set_name("nv4");
2542
2543 m_vars[0].set_time_dependent(false);
2544 if (m_var_name == "lon") {
2545 m_vars[0].long_name("longitude bounds").units("degree_east");
2546 m_vars[0]["valid_range"] = { -180, 180 };
2547 } else {
2548 m_vars[0].long_name("latitude bounds").units("degree_north");
2549 m_vars[0]["valid_range"] = { -90, 90 };
2550 }
2551
2552 m_proj_string = proj_string;
2553
2554#if (Pism_USE_PROJ == 1)
2555 // create the transformation from the provided projection to lat,lon to check if
2556 // proj_string is valid.
2557 Proj crs(m_proj_string, "EPSG:4326");
2558#endif
2559 // If PISM_USE_PROJ is not 1 we don't need to check validity of m_proj_string: this diagnostic
2560 // will not be available and so this code will not run.
2561}
2562
2563std::shared_ptr<array::Array> LatLonBounds::compute_impl() const {
2564 std::shared_ptr<array::Array3D> result(new array::Array3D(
2565 m_grid, m_var_name + "_bnds", array::WITHOUT_GHOSTS, { 0.0, 1.0, 2.0, 3.0 }));
2566 result->metadata(0) = m_vars[0];
2567
2568 if (m_var_name == "lat") {
2570 } else {
2572 }
2573
2574 return result;
2575}
2576
2577class IceAreaFraction : public Diag<IceModel> {
2578public:
2579 IceAreaFraction(const IceModel *m);
2580
2581protected:
2582 virtual std::shared_ptr<array::Array> compute_impl() const;
2583};
2584
2587 m_vars[0]
2588 .long_name("fraction of a grid cell covered by ice (grounded or floating)")
2589 .standard_name("land_ice_area_fraction") // InitMIP "standard" name
2590 .units("1");
2591}
2592
2593std::shared_ptr<array::Array> IceAreaFraction::compute_impl() const {
2594
2595 auto result = allocate<array::Scalar>(land_ice_area_fraction_name);
2596
2597 const array::Scalar1 &thickness = model->geometry().ice_thickness,
2598 &surface_elevation = model->geometry().ice_surface_elevation,
2599 &bed_topography = model->geometry().bed_elevation;
2600
2601 const array::CellType1 &cell_type = model->geometry().cell_type;
2602
2603 array::AccessScope list{ &thickness, &surface_elevation, &bed_topography, &cell_type,
2604 result.get() };
2605
2606 const bool do_part_grid = m_config->get_flag("geometry.part_grid.enabled");
2608 ;
2609 if (do_part_grid) {
2610 list.add(Href);
2611 }
2612
2613 ParallelSection loop(m_grid->com);
2614 try {
2615 for (auto p : m_grid->points()) {
2616 const int i = p.i(), j = p.j();
2617
2618 if (cell_type.icy(i, j)) {
2619 // an "icy" cell: the area fraction is one
2620 (*result)(i, j) = 1.0;
2621 } else if (cell_type.ice_free_ocean(i, j)) {
2622 // an ice-free ocean cell may be "partially-filled", in which case we need to compute its
2623 // ice area fraction by dividing Href by the threshold thickness.
2624
2625 double H_reference = do_part_grid ? Href(i, j) : 0.0;
2626
2627 if (H_reference > 0.0) {
2628 const double H_threshold =
2629 part_grid_threshold_thickness(cell_type.star_int(i, j), thickness.star(i, j),
2630 surface_elevation.star(i, j), bed_topography(i, j));
2631 // protect from a division by zero
2632 if (H_threshold > 0.0) {
2633 (*result)(i, j) = std::min(H_reference / H_threshold, 1.0);
2634 } else {
2635 (*result)(i, j) = 1.0;
2636 }
2637 } else {
2638 // H_reference is zero
2639 (*result)(i, j) = 0.0;
2640 }
2641 } else {
2642 // an ice-free-ground cell: the area fraction is zero
2643 (*result)(i, j) = 0.0;
2644 }
2645 } // end of the loop over grid points
2646 } catch (...) {
2647 loop.failed();
2648 }
2649 loop.check();
2650
2651 return result;
2652}
2653
2654class IceAreaFractionGrounded : public Diag<IceModel> {
2655public:
2657
2658protected:
2659 virtual std::shared_ptr<array::Array> compute_impl() const;
2660};
2661
2664 m_vars[0]
2665 .long_name("fraction of a grid cell covered by grounded ice")
2666 .standard_name("grounded_ice_sheet_area_fraction") // InitMIP "standard" name
2667 .units("1");
2668}
2669
2670std::shared_ptr<array::Array> IceAreaFractionGrounded::compute_impl() const {
2671 auto result = std::make_shared<array::Scalar>(m_grid, grounded_ice_sheet_area_fraction_name);
2672 result->metadata() = m_vars[0];
2673
2674 const double ice_density = m_config->get_number("constants.ice.density"),
2675 ocean_density = m_config->get_number("constants.sea_water.density");
2676
2677 const auto &ice_thickness = model->geometry().ice_thickness;
2678 const auto &sea_level = model->geometry().sea_level_elevation;
2679 const auto &bed_topography = model->geometry().bed_elevation;
2680
2681 const auto &cell_type = model->geometry().cell_type;
2682
2683 compute_grounded_cell_fraction(ice_density, ocean_density, sea_level, ice_thickness,
2684 bed_topography, *result);
2685
2686 // All grounded areas have the grounded cell fraction of one, so now we make sure that ice-free
2687 // areas get the value of 0 (they are grounded but not covered by a grounded ice sheet).
2688
2689 array::AccessScope list{ &cell_type, result.get() };
2690
2691 ParallelSection loop(m_grid->com);
2692 try {
2693 for (auto p : m_grid->points()) {
2694 const int i = p.i(), j = p.j();
2695 if (cell_type.ice_free(i, j)) {
2696 (*result)(i, j) = 0.0;
2697 }
2698 }
2699 } catch (...) {
2700 loop.failed();
2701 }
2702 loop.check();
2703
2704 return result;
2705}
2706
2707class IceAreaFractionFloating : public Diag<IceModel> {
2708public:
2710
2711protected:
2712 virtual std::shared_ptr<array::Array> compute_impl() const;
2713};
2714
2717 m_vars[0]
2718 .long_name("fraction of a grid cell covered by floating ice")
2719 .standard_name("floating_ice_shelf_area_fraction")
2720 .units("1");
2721}
2722
2723std::shared_ptr<array::Array> IceAreaFractionFloating::compute_impl() const {
2724
2725 auto ice_area_fraction = IceAreaFraction(model).compute();
2727
2728 auto result = ice_area_fraction;
2729 result->metadata() = m_vars[0];
2730
2731 // Floating area fraction is total area fraction minus grounded area fraction.
2732 result->add(-1.0, *grounded_area_fraction);
2733
2734 return result;
2735}
2736
2737//! \brief Computes the 2D height above flotation.
2738class HeightAboveFloatation : public Diag<IceModel> {
2739public:
2741
2742protected:
2743 virtual std::shared_ptr<array::Array> compute_impl() const;
2744};
2745
2747
2748 // set metadata:
2749 m_vars = { { m_sys, "height_above_flotation", *m_grid } };
2750 m_vars[0].long_name("ice thickness in excess of the maximum floating ice thickness").units("m");
2751 m_vars[0]["_FillValue"] = { m_fill_value };
2752 m_vars[0]["comment"] = "shows how close to floatation the ice is at a given location";
2753}
2754
2755std::shared_ptr<array::Array> HeightAboveFloatation::compute_impl() const {
2756
2757 auto result = allocate<array::Scalar>("height_above_flotation");
2758
2759 const auto &cell_type = model->geometry().cell_type;
2760
2761 const double ice_density = m_config->get_number("constants.ice.density"),
2762 ocean_density = m_config->get_number("constants.sea_water.density");
2763
2764 const auto &sea_level = model->geometry().sea_level_elevation;
2765 const auto &ice_thickness = model->geometry().ice_thickness;
2766 const auto &bed_topography = model->geometry().bed_elevation;
2767
2768 array::AccessScope list{ &cell_type, result.get(), &ice_thickness, &bed_topography, &sea_level };
2769
2770 ParallelSection loop(m_grid->com);
2771 try {
2772 for (auto p : m_grid->points()) {
2773 const int i = p.i(), j = p.j();
2774
2775 const double thickness = ice_thickness(i, j), bed = bed_topography(i, j),
2776 ocean_depth = sea_level(i, j) - bed;
2777
2778 if (cell_type.icy(i, j) and ocean_depth > 0.0) {
2779 const double max_floating_thickness = ocean_depth * (ocean_density / ice_density);
2780 (*result)(i, j) = thickness - max_floating_thickness;
2781 } else {
2782 (*result)(i, j) = m_fill_value;
2783 }
2784 }
2785 } catch (...) {
2786 loop.failed();
2787 }
2788 loop.check();
2789
2790 return result;
2791}
2792
2793//! \brief Computes the mass per cell.
2794class IceMass : public Diag<IceModel> {
2795public:
2796 IceMass(const IceModel *m);
2797
2798protected:
2799 virtual std::shared_ptr<array::Array> compute_impl() const;
2800};
2801
2803 m_vars = { { m_sys, "ice_mass", *m_grid } };
2804 m_vars[0].long_name("ice mass per cell").units("kg");
2805 m_vars[0]["_FillValue"] = { m_fill_value };
2806}
2807
2808std::shared_ptr<array::Array> IceMass::compute_impl() const {
2809
2810 auto result = allocate<array::Scalar>("ice_mass");
2811
2812 const auto &cell_type = model->geometry().cell_type;
2813
2814 const double ice_density = m_config->get_number("constants.ice.density");
2815
2816 const array::Scalar &ice_thickness = model->geometry().ice_thickness;
2817
2818 auto cell_area = m_grid->cell_area();
2819
2820 array::AccessScope list{ &cell_type, result.get(), &ice_thickness };
2821
2822 ParallelSection loop(m_grid->com);
2823 try {
2824 for (auto p : m_grid->points()) {
2825 const int i = p.i(), j = p.j();
2826
2827 // count all ice, including cells which have so little they
2828 // are considered "ice-free"
2829 if (ice_thickness(i, j) > 0.0) {
2830 (*result)(i, j) = ice_density * ice_thickness(i, j) * cell_area;
2831 } else {
2832 (*result)(i, j) = m_fill_value;
2833 }
2834 } // end of loop over grid points
2835
2836 } catch (...) {
2837 loop.failed();
2838 }
2839 loop.check();
2840
2841 // Add the mass of ice in Href:
2842 if (m_config->get_flag("geometry.part_grid.enabled")) {
2844 list.add(Href);
2845 for (auto p : m_grid->points()) {
2846 const int i = p.i(), j = p.j();
2847
2848 if (ice_thickness(i, j) <= 0.0 and Href(i, j) > 0.0) {
2849 (*result)(i, j) = ice_density * Href(i, j) * cell_area;
2850 }
2851 }
2852 }
2853
2854 return result;
2855}
2856
2857/*! @brief Sea-level adjusted bed topography (zero at sea level). */
2858class BedTopographySeaLevelAdjusted : public Diag<IceModel> {
2859public:
2861
2862protected:
2863 std::shared_ptr<array::Array> compute_impl() const;
2864};
2865
2867 : Diag<IceModel>(m) {
2868 m_vars = { { m_sys, "topg_sl_adjusted", *m_grid } };
2869 m_vars[0].long_name("sea-level adjusted bed topography (zero is at sea level)").units("meters");
2870}
2871
2872std::shared_ptr<array::Array> BedTopographySeaLevelAdjusted::compute_impl() const {
2873
2874 auto result = allocate<array::Scalar>("topg_sl_adjusted");
2875
2876 const auto &bed = model->geometry().bed_elevation;
2877 const auto &sea_level = model->geometry().sea_level_elevation;
2878
2879 array::AccessScope list{ &bed, &sea_level, result.get() };
2880
2881 for (auto p : m_grid->points()) {
2882 const int i = p.i(), j = p.j();
2883
2884 (*result)(i, j) = bed(i, j) - sea_level(i, j);
2885 }
2886
2887 return result;
2888}
2889
2890/*! @brief Ice hardness computed using the SIA flow law. */
2891class IceHardness : public Diag<IceModel> {
2892public:
2893 IceHardness(const IceModel *m);
2894
2895protected:
2896 std::shared_ptr<array::Array> compute_impl() const;
2897};
2898
2900 m_vars = { { m_sys, "hardness", *m_grid, m_grid->z() } };
2901 m_vars[0]
2902 .long_name("ice hardness computed using the SIA flow law")
2903 .set_units_without_validation(
2904 "Pa s^(1/n)"); // n is the Glen exponent used by the SIA (modifier) flow law
2905 m_vars[0]["comment"] = "units depend on the Glen exponent used by the flow law";
2906}
2907
2908std::shared_ptr<array::Array> IceHardness::compute_impl() const {
2909
2910 std::shared_ptr<array::Array3D> result(
2911 new array::Array3D(m_grid, "hardness", array::WITHOUT_GHOSTS, m_grid->z()));
2912 result->metadata(0) = m_vars[0];
2913
2914 auto EC = m_grid->ctx()->enthalpy_converter();
2915
2916 const array::Array3D &ice_enthalpy = model->energy_balance_model()->enthalpy();
2917 const array::Scalar &ice_thickness = model->geometry().ice_thickness;
2918
2919 const rheology::FlowLaw &flow_law = *model->stress_balance()->modifier()->flow_law();
2920
2921 array::AccessScope list{ &ice_enthalpy, &ice_thickness, result.get() };
2922
2923 const unsigned int Mz = m_grid->Mz();
2924
2925 ParallelSection loop(m_grid->com);
2926 try {
2927 for (auto p : m_grid->points()) {
2928 const int i = p.i(), j = p.j();
2929 const double *E = ice_enthalpy.get_column(i, j);
2930 const double H = ice_thickness(i, j);
2931
2932 double *hardness = result->get_column(i, j);
2933
2934 for (unsigned int k = 0; k < Mz; ++k) {
2935 const double depth = H - m_grid->z(k);
2936
2937 // EC->pressure() handles negative depths correctly
2938 const double pressure = EC->pressure(depth);
2939
2940 hardness[k] = flow_law.hardness(E[k], pressure);
2941 }
2942 }
2943 } catch (...) {
2944 loop.failed();
2945 }
2946 loop.check();
2947
2948 return result;
2949}
2950
2951/*! @brief Effective viscosity of ice (3D). */
2952class IceViscosity : public Diag<IceModel> {
2953public:
2955
2956protected:
2957 std::shared_ptr<array::Array> compute_impl() const;
2958};
2959
2961 m_vars = { { m_sys, "effective_viscosity", *m_grid, m_grid->z() } };
2962 m_vars[0]
2963 .long_name("effective viscosity of ice")
2964 .units("Pascal second")
2965 .output_units("kPascal second");
2966 m_vars[0]["valid_min"] = { 0.0 };
2967 m_vars[0]["_FillValue"] = { m_fill_value };
2968}
2969
2970static inline double square(double x) {
2971 return x * x;
2972}
2973
2974std::shared_ptr<array::Array> IceViscosity::compute_impl() const {
2975
2976 std::shared_ptr<array::Array3D> result(
2977 new array::Array3D(m_grid, "effective_viscosity", array::WITHOUT_GHOSTS, m_grid->z()));
2978 result->metadata(0) = m_vars[0];
2979
2981
2982 using mask::ice_free;
2983
2984 auto EC = m_grid->ctx()->enthalpy_converter();
2985
2986 const rheology::FlowLaw &flow_law = *model->stress_balance()->modifier()->flow_law();
2987
2988 const array::Scalar &ice_thickness = model->geometry().ice_thickness;
2989
2990 const array::Array3D &ice_enthalpy = model->energy_balance_model()->enthalpy(),
2991 &U = model->stress_balance()->velocity_u(),
2992 &V = model->stress_balance()->velocity_v(),
2993 &W_without_ghosts = model->stress_balance()->velocity_w();
2994
2995 W.copy_from(W_without_ghosts);
2996
2997 const unsigned int Mz = m_grid->Mz();
2998 const double dx = m_grid->dx(), dy = m_grid->dy();
2999 const std::vector<double> &z = m_grid->z();
3000
3001 const array::CellType1 &mask = model->geometry().cell_type;
3002
3003 array::AccessScope list{ &U, &V, &W, &ice_enthalpy, &ice_thickness, &mask, result.get() };
3004
3005 ParallelSection loop(m_grid->com);
3006 try {
3007 for (auto p : m_grid->points()) {
3008 const int i = p.i(), j = p.j();
3009
3010 const double *E = ice_enthalpy.get_column(i, j);
3011 const double H = ice_thickness(i, j);
3012
3013 const double *u = U.get_column(i, j), *u_n = U.get_column(i, j + 1),
3014 *u_e = U.get_column(i + 1, j), *u_s = U.get_column(i, j - 1),
3015 *u_w = U.get_column(i - 1, j);
3016
3017 const double *v = V.get_column(i, j), *v_n = V.get_column(i, j + 1),
3018 *v_e = V.get_column(i + 1, j), *v_s = V.get_column(i, j - 1),
3019 *v_w = V.get_column(i - 1, j);
3020
3021 const double *w = W.get_column(i, j), *w_n = W.get_column(i, j + 1),
3022 *w_e = W.get_column(i + 1, j), *w_s = W.get_column(i, j - 1),
3023 *w_w = W.get_column(i - 1, j);
3024
3025 auto m = mask.star_int(i, j);
3026 const unsigned int east = ice_free(m.e) ? 0 : 1, west = ice_free(m.w) ? 0 : 1,
3027 south = ice_free(m.s) ? 0 : 1, north = ice_free(m.n) ? 0 : 1;
3028
3029 double *viscosity = result->get_column(i, j);
3030
3031 if (ice_free(m.c)) {
3032 result->set_column(i, j, m_fill_value);
3033 continue;
3034 }
3035
3036 for (unsigned int k = 0; k < Mz; ++k) {
3037 const double depth = H - z[k];
3038
3039 if (depth < 0.0) {
3040 viscosity[k] = m_fill_value;
3041 continue;
3042 }
3043
3044 // EC->pressure() handles negative depths correctly
3045 const double pressure = EC->pressure(depth);
3046
3047 const double hardness = flow_law.hardness(E[k], pressure);
3048
3049 double u_x = 0.0, v_x = 0.0, w_x = 0.0;
3050 if (west + east > 0) {
3051 const double D = 1.0 / (dx * (west + east));
3052 u_x = D * (west * (u[k] - u_w[k]) + east * (u_e[k] - u[k]));
3053 v_x = D * (west * (v[k] - v_w[k]) + east * (v_e[k] - v[k]));
3054 w_x = D * (west * (w[k] - w_w[k]) + east * (w_e[k] - w[k]));
3055 }
3056
3057 double u_y = 0.0, v_y = 0.0, w_y = 0.0;
3058 if (south + north > 0) {
3059 const double D = 1.0 / (dy * (south + north));
3060 u_y = D * (south * (u[k] - u_s[k]) + north * (u_n[k] - u[k]));
3061 v_y = D * (south * (v[k] - v_s[k]) + north * (v_n[k] - v[k]));
3062 w_y = D * (south * (w[k] - w_s[k]) + north * (w_n[k] - w[k]));
3063 }
3064
3065 double u_z = 0.0, v_z = 0.0, w_z = 0.0;
3066
3067 if (k == 0) {
3068 const double dz = z[1] - z[0];
3069 u_z = (u[1] - u[0]) / dz;
3070 v_z = (v[1] - v[0]) / dz;
3071 w_z = (w[1] - w[0]) / dz;
3072 } else if (k == Mz - 1) {
3073 const double dz = z[Mz - 1] - z[Mz - 2];
3074 u_z = (u[Mz - 1] - u[Mz - 2]) / dz;
3075 v_z = (v[Mz - 1] - v[Mz - 2]) / dz;
3076 w_z = (w[Mz - 1] - w[Mz - 2]) / dz;
3077 } else {
3078 const double dz_p = z[k + 1] - z[k], dz_m = z[k] - z[k - 1];
3079 u_z = 0.5 * ((u[k + 1] - u[k]) / dz_p + (u[k] - u[k - 1]) / dz_m);
3080 v_z = 0.5 * ((v[k + 1] - v[k]) / dz_p + (v[k] - v[k - 1]) / dz_m);
3081 w_z = 0.5 * ((w[k + 1] - w[k]) / dz_p + (w[k] - w[k - 1]) / dz_m);
3082 }
3083
3084 // These should be "epsilon dot", but that's just too long.
3085 const double eps_xx = u_x, eps_yy = v_y, eps_zz = w_z, eps_xy = 0.5 * (u_y + v_x),
3086 eps_xz = 0.5 * (u_z + w_x), eps_yz = 0.5 * (v_z + w_y);
3087
3088 // The second invariant of the 3D strain rate tensor; see equation 4.8 in [@ref
3089 // GreveBlatter2009]. Unlike secondInvariant_2D(), this code does not make assumptions about
3090 // the input velocity field: we do not ignore w_x and w_y and do not assume that u_z and v_z
3091 // are zero.
3092 const double gamma = (square(eps_xx) + square(eps_yy) + square(eps_zz) +
3093 2.0 * (square(eps_xy) + square(eps_xz) + square(eps_yz)));
3094
3095 double nu = 0.0;
3096 // Note: in PISM gamma has an extra factor of 1/2; compare to
3097 flow_law.effective_viscosity(hardness, 0.5 * gamma, &nu, NULL);
3098
3099 viscosity[k] = nu;
3100 }
3101 }
3102 } catch (...) {
3103 loop.failed();
3104 }
3105 loop.check();
3106
3107 return result;
3108}
3109
3110/*! @brief Report ice thickness */
3111class IceThickness : public Diag<IceModel> {
3112public:
3114
3115 auto ismip6 = m_config->get_flag("output.ISMIP6");
3116
3117 m_vars = { { m_sys, ismip6 ? "lithk" : "thk", *m_grid } };
3118
3119 m_vars[0].long_name("land ice thickness").standard_name("land_ice_thickness").units("m");
3120 m_vars[0]["valid_min"] = { 0.0 };
3121 }
3122
3123protected:
3124 std::shared_ptr<array::Array> compute_impl() const {
3125
3126 auto result = allocate<array::Scalar>("thk");
3127
3128 result->copy_from(model->geometry().ice_thickness);
3129
3130 return result;
3131 }
3132};
3133
3134/*! @brief Report ice top surface elevation */
3135class IceBottomSurfaceElevation : public Diag<IceModel> {
3136public:
3138
3139 auto ismip6 = m_config->get_flag("output.ISMIP6");
3140
3141 m_vars = { { m_sys, ismip6 ? "base" : "ice_base_elevation", *m_grid } };
3142 m_vars[0].long_name("ice bottom surface elevation").units("m");
3143 }
3144
3145protected:
3146 std::shared_ptr<array::Array> compute_impl() const {
3147
3148 auto result = allocate<array::Scalar>("ice_base_elevation");
3149
3150 ice_bottom_surface(model->geometry(), *result);
3151
3152 return result;
3153 }
3154};
3155
3156/*! @brief Report ice top surface elevation */
3157class IceSurfaceElevation : public Diag<IceModel> {
3158public:
3160
3161 auto ismip6 = m_config->get_flag("output.ISMIP6");
3162
3163 m_vars = { { m_sys, ismip6 ? "orog" : "usurf", *m_grid } };
3164 m_vars[0].long_name("ice top surface elevation").standard_name("surface_altitude").units("m");
3165 }
3166
3167protected:
3168 std::shared_ptr<array::Array> compute_impl() const {
3169 auto result = allocate<array::Scalar>("usurf");
3170
3171 result->copy_from(model->geometry().ice_surface_elevation);
3172
3173 return result;
3174 }
3175};
3176
3177/*! @brief Report grounding line flux. */
3178class GroundingLineFlux : public DiagAverageRate<IceModel> {
3179public:
3180 GroundingLineFlux(const IceModel *m) : DiagAverageRate<IceModel>(m, "grounding_line_flux", RATE) {
3181
3182 m_accumulator.metadata()["units"] = "kg m^-2";
3183
3184 auto ismip6 = m_config->get_flag("output.ISMIP6");
3185
3186 m_vars = { { m_sys, ismip6 ? "ligroundf" : "grounding_line_flux", *m_grid } };
3187
3188 m_vars[0]
3189 .long_name("grounding line flux")
3190 .units("kg m^-2 second^-1")
3191 .output_units("kg m^-2 year^-1");
3192 m_vars[0]["cell_methods"] = "time: mean";
3193
3194 m_vars[0]["_FillValue"] = {to_internal(m_fill_value)};
3195 m_vars[0]["comment"] =
3196 "Positive flux corresponds to mass moving from the ocean to"
3197 " an icy grounded area. This convention makes it easier to compare"
3198 " grounding line flux to the total discharge into the ocean";
3199 }
3200
3201protected:
3202 void update_impl(double dt) {
3203 auto grid = m_accumulator.grid();
3204 auto cell_area = grid->cell_area(); // units: m^2
3205 auto ice_density =
3206 grid->ctx()->config()->get_number("constants.ice.density"); // units: kg / m^3
3207
3208 // factor used to convert from m^3/s to kg/m^2
3209 double unit_conversion_factor = dt * (ice_density / cell_area); // units: kg * s / m^5
3210
3211 ice_flow_rate_across_grounding_line(model->geometry().cell_type,
3212 model->geometry_evolution().flux_staggered(),
3213 unit_conversion_factor, m_accumulator);
3214
3215 m_interval_length += dt;
3216 }
3217};
3218
3220public:
3222 : DiagAverageRate<IceModel>(m, "ice_mass_transport_across_grounding_line", RATE) {
3223
3224 m_accumulator.metadata()["units"] = "kg";
3225
3226 m_vars = { { m_sys, "ice_mass_transport_across_grounding_line", *m_grid } };
3227
3228 m_vars[0]
3229 .long_name("ice mass flow rate across the grounding line")
3230 .units("kg s^-1")
3231 .output_units("Gt year^-1");
3232 m_vars[0]["cell_methods"] = "time: mean";
3233
3234 m_vars[0]["_FillValue"] = { to_internal(m_fill_value) };
3235 m_vars[0]["comment"] =
3236 "Negative values correspond to mass moving from an icy grounded area into a lake or ocean."
3237 " This convention makes it easier to compare to calving, frontal melt, and discharge fluxes.";
3238 }
3239
3240protected:
3241 void update_impl(double dt) {
3242 auto grid = model->grid();
3243 double ice_density =
3244 grid->ctx()->config()->get_number("constants.ice.density"); // units: kg / m^3
3245
3246 // factor used to convert from m^3/s to kg
3247 double unit_conversion_factor = dt * ice_density; // units: kg * s / m^3
3248
3249 ice_flow_rate_across_grounding_line(model->geometry().cell_type,
3250 model->geometry_evolution().flux_staggered(),
3251 unit_conversion_factor, m_accumulator);
3252
3253 m_interval_length += dt;
3254 }
3255};
3256
3257
3258} // end of namespace diagnostics
3259
3263
3264 list_diagnostics(report_type);
3265
3266 // initialize diagnostics first: we need to know which spatial and scalar variables will
3267 // be saved to determine which *state* variables for these diagnostics need to be saved
3268 // to output files that can be used for re-starting.
3271 // initialize outputs that can be used for re-starting:
3275
3277
3278 // reset: this gives diagnostics a chance to capture the current state of the model at the
3279 // beginning of the run
3280 for (auto &d : m_available_spatial_diagnostics) {
3281 d.second->reset();
3282 }
3283
3284 // read in the state (accumulators) if we are re-starting a run
3285 if (options.type == INIT_RESTART) {
3286 File file(m_grid->com, options.filename, io::PISM_GUESS, io::PISM_READONLY);
3287 for (const auto &d : m_available_spatial_diagnostics) {
3288 d.second->init(file, options.record);
3289 }
3290 }
3291
3292 // Tell the output writer about all the variables we may need to write:
3293 {
3294 std::set<VariableMetadata> all_variables;
3295 all_variables = pism::combine(all_variables, m_output_file_contents);
3296 all_variables = pism::combine(all_variables, m_snapshot_file_contents);
3297 all_variables = pism::combine(all_variables, m_spatial_file_contents);
3298 all_variables = pism::combine(all_variables, m_checkpoint_file_contents);
3299
3300 m_output_writer->initialize(all_variables);
3301
3303 m_snapshot_writer->initialize(all_variables);
3304 }
3305
3306 // note: no need to call m_spatial_writer->initialize() because it is equal to
3307 // m_snapshot_writer.
3308 }
3309}
3310
3311std::map<std::string, Diagnostic::Ptr> IceModel::allocate_spatial_diagnostics() {
3312 std::map<std::string, Diagnostic::Ptr> result;
3313
3314 using namespace diagnostics;
3315
3316 using d = Diagnostic;
3317 using f = Diagnostic::Ptr; // "f" for "field"
3318 result = {
3319 // geometry
3320 { "cell_grounded_fraction", d::wrap(m_geometry.cell_grounded_fraction) },
3321 { "height_above_flotation", f(new HeightAboveFloatation(this)) },
3322 { "ice_area_specific_volume", d::wrap(m_geometry.ice_area_specific_volume) },
3323 { "ice_mass", f(new IceMass(this)) },
3324 { "lat", d::wrap(m_geometry.latitude) },
3325 { "lon", d::wrap(m_geometry.longitude) },
3326 { "mask", d::wrap(m_geometry.cell_type) },
3327 { "thk", f(new IceThickness(this)) },
3328 { "topg_sl_adjusted", f(new BedTopographySeaLevelAdjusted(this)) },
3329 { "usurf", f(new IceSurfaceElevation(this)) },
3330 { "ice_base_elevation", f(new IceBottomSurfaceElevation(this)) },
3331 { floating_ice_sheet_area_fraction_name, f(new IceAreaFractionFloating(this)) },
3332 { grounded_ice_sheet_area_fraction_name, f(new IceAreaFractionGrounded(this)) },
3333 { land_ice_area_fraction_name, f(new IceAreaFraction(this)) },
3334
3335 // temperature, enthalpy, and liquid water fraction
3336 { "enthalpybase", f(new IceEnthalpyBasal(this)) },
3337 { "enthalpysurf", f(new IceEnthalpySurface(this)) },
3338 { "bedtoptemp", d::wrap(m_bedtoptemp) },
3339 { "cts", f(new CTS(this)) },
3340 { "liqfrac", f(new LiquidFraction(this)) },
3341 { "temp", f(new Temperature(this)) },
3342 { "temp_pa", f(new TemperaturePA(this)) },
3343 { "tempbase", f(new TemperatureBasal(this, BOTH)) },
3344 { "temppabase", f(new TemperaturePABasal(this)) },
3345 { "tempsurf", f(new TemperatureSurface(this)) },
3346
3347 // rheology-related stuff
3348 { "tempicethk", f(new TemperateIceThickness(this)) },
3349 { "tempicethk_basal", f(new TemperateIceThicknessBasal(this)) },
3350 { "hardav", f(new HardnessAverage(this)) },
3351 { "hardness", f(new IceHardness(this)) },
3352 { "effective_viscosity", f(new IceViscosity(this)) },
3353
3354 // boundary conditions
3355 { "vel_bc_mask", d::wrap(m_velocity_bc_mask) },
3356 { "vel_bc_values", d::wrap(m_velocity_bc_values) },
3357 { "ice_margin_pressure_difference", f(new IceMarginPressureDifference(this)) },
3358 { "thk_bc_mask", d::wrap(m_ice_thickness_bc_mask) },
3359
3360 // balancing the books
3361 // tendency_of_ice_amount = (tendency_of_ice_amount_due_to_flow +
3362 // tendency_of_ice_amount_due_to_conservation_error +
3363 // tendency_of_ice_amount_due_to_surface_mass_balance +
3364 // tendency_of_ice_amount_due_to_basal_mass_balance +
3365 // tendency_of_ice_amount_due_to_discharge)
3366 //
3367 // Also,
3368 // tendency_of_ice_amount_due_to_discharge = (tendency_of_ice_amount_due_to_calving +
3369 // tendency_of_ice_amount_due_to_frontal_melt +
3370 // tendency_of_ice_amount_due_to_forced_retreat)
3371 { "tendency_of_ice_amount", f(new TendencyOfIceAmount(this, AMOUNT)) },
3372 { "tendency_of_ice_amount_due_to_flow", f(new TendencyOfIceAmountDueToFlow(this, AMOUNT)) },
3373 { "tendency_of_ice_amount_due_to_conservation_error",
3374 f(new ConservationErrorFlux(this, AMOUNT)) },
3375 { "tendency_of_ice_amount_due_to_surface_mass_flux", f(new SurfaceFlux(this, AMOUNT)) },
3376 { "tendency_of_ice_amount_due_to_basal_mass_flux", f(new BasalFlux(this, AMOUNT)) },
3377 { "tendency_of_ice_amount_due_to_discharge", f(new DischargeFlux(this, AMOUNT)) },
3378 { "tendency_of_ice_amount_due_to_calving", f(new CalvingFlux(this, AMOUNT)) },
3379 { "tendency_of_ice_amount_due_to_frontal_melt", f(new FrontalMeltFlux(this, AMOUNT)) },
3380 { "tendency_of_ice_amount_due_to_forced_retreat", f(new ForcedRetreatFlux(this, AMOUNT)) },
3381
3382 // same, in terms of mass
3383 // tendency_of_ice_mass = (tendency_of_ice_mass_due_to_flow +
3384 // tendency_of_ice_mass_due_to_conservation_error +
3385 // tendency_of_ice_mass_due_to_surface_mass_flux +
3386 // tendency_of_ice_mass_due_to_basal_mass_balance +
3387 // tendency_of_ice_mass_due_to_discharge)
3388 //
3389 // Also,
3390 // tendency_of_ice_mass_due_to_discharge = (tendency_of_ice_mass_due_to_calving +
3391 // tendency_of_ice_mass_due_to_frontal_melt +
3392 // tendency_of_ice_mass_due_to_forced_retreat)
3393 { "tendency_of_ice_mass", f(new TendencyOfIceAmount(this, MASS)) },
3394 { "tendency_of_ice_mass_due_to_flow", f(new TendencyOfIceAmountDueToFlow(this, MASS)) },
3395 { "tendency_of_ice_mass_due_to_conservation_error", f(new ConservationErrorFlux(this, MASS)) },
3396 { "tendency_of_ice_mass_due_to_surface_mass_flux", f(new SurfaceFlux(this, MASS)) },
3397 { "tendency_of_ice_mass_due_to_basal_mass_flux", f(new BasalFlux(this, MASS)) },
3398 { "tendency_of_ice_mass_due_to_discharge", f(new DischargeFlux(this, MASS)) },
3399 { "tendency_of_ice_mass_due_to_calving", f(new CalvingFlux(this, MASS)) },
3400 { "tendency_of_ice_mass_due_to_frontal_melt", f(new FrontalMeltFlux(this, MASS)) },
3401 { "tendency_of_ice_mass_due_to_forced_retreat", f(new ForcedRetreatFlux(this, MASS)) },
3402
3403 // other rates and fluxes
3404 { "basal_mass_flux_grounded", f(new BMBSplit(this, GROUNDED)) },
3405 { "basal_mass_flux_floating", f(new BMBSplit(this, SHELF)) },
3406 { "dHdt", f(new ThicknessRateOfChange(this)) },
3407 { "bmelt", d::wrap(m_basal_melt_rate) },
3408 { "grounding_line_flux", f(new GroundingLineFlux(this)) },
3409 { "ice_mass_transport_across_grounding_line", f(new MassTransportAcrossGroundingLine(this)) },
3410
3411 // misc
3412 { "rank", f(new Rank(this)) },
3413 };
3414
3415#if (Pism_USE_PROJ == 1)
3416 std::string proj = m_grid->get_mapping_info()["proj_params"];
3417 if (not proj.empty()) {
3418 result["lat_bnds"] = f(new LatLonBounds(this, "lat", proj));
3419 result["lon_bnds"] = f(new LatLonBounds(this, "lon", proj));
3420 }
3421#endif
3422
3423 // add ISMIP6 variable names
3424 if (m_config->get_flag("output.ISMIP6")) {
3425 result["base"] = result["ice_base_elevation"];
3426 result["lithk"] = result["thk"];
3427 result["dlithkdt"] = result["dHdt"];
3428 result["orog"] = result["usurf"];
3429 result["acabf"] = result["tendency_of_ice_amount_due_to_surface_mass_flux"];
3430 result["libmassbfgr"] = result["basal_mass_flux_grounded"];
3431 result["libmassbffl"] = result["basal_mass_flux_floating"];
3432 result["lifmassbf"] = result["tendency_of_ice_amount_due_to_discharge"];
3433 result["licalvf"] = result["tendency_of_ice_amount_due_to_calving"];
3434 result["litempbotgr"] = f(new TemperatureBasal(this, GROUNDED));
3435 result["litempbotfl"] = f(new TemperatureBasal(this, SHELF));
3436 result["ligroundf"] = result["grounding_line_flux"];
3437 }
3438
3439 // get diagnostics from submodels
3440 for (const auto& m : m_submodels) {
3441 result = pism::combine(result, m.second->spatial_diagnostics());
3442 }
3443
3444 return result;
3445}
3446
3447std::map<std::string, TSDiagnostic::Ptr> IceModel::allocate_scalar_diagnostics() {
3448 std::map<std::string, TSDiagnostic::Ptr> result;
3449
3450 using namespace diagnostics;
3451
3452 using s = TSDiagnostic::Ptr; // "s" for "scalar"
3453
3454 result = {
3455 // area
3456 {"ice_area_glacierized", s(new scalar::IceAreaGlacierized(this))},
3457 {"ice_area_glacierized_cold_base", s(new scalar::IceAreaGlacierizedColdBase(this))},
3458 {"ice_area_glacierized_grounded", s(new scalar::IceAreaGlacierizedGrounded(this))},
3459 {"ice_area_glacierized_floating", s(new scalar::IceAreaGlacierizedShelf(this))},
3460 {"ice_area_glacierized_temperate_base", s(new scalar::IceAreaGlacierizedTemperateBase(this))},
3461 // mass
3462 {"ice_mass_glacierized", s(new scalar::IceMassGlacierized(this))},
3463 {"ice_mass", s(new scalar::IceMass(this))},
3464 {"tendency_of_ice_mass_glacierized", s(new scalar::IceMassRateOfChangeGlacierized(this))},
3465 {"limnsw", s(new scalar::IceMassNotDisplacingSeaWater(this))},
3466 // volume
3467 {"ice_volume_glacierized", s(new scalar::IceVolumeGlacierized(this))},
3468 {"ice_volume_glacierized_cold", s(new scalar::IceVolumeGlacierizedCold(this))},
3469 {"ice_volume_glacierized_grounded", s(new scalar::IceVolumeGlacierizedGrounded(this))},
3470 {"ice_volume_glacierized_floating", s(new scalar::IceVolumeGlacierizedShelf(this))},
3471 {"ice_volume_glacierized_temperate", s(new scalar::IceVolumeGlacierizedTemperate(this))},
3472 {"ice_volume", s(new scalar::IceVolume(this))},
3473 {"ice_volume_cold", s(new scalar::IceVolumeCold(this))},
3474 {"ice_volume_temperate", s(new scalar::IceVolumeTemperate(this))},
3475 {"tendency_of_ice_volume_glacierized", s(new scalar::IceVolumeRateOfChangeGlacierized(this))},
3476 {"tendency_of_ice_volume", s(new scalar::IceVolumeRateOfChange(this))},
3477 {"sea_level_rise_potential", s(new scalar::SeaLevelRisePotential(this))},
3478 // energy
3479 {"ice_enthalpy_glacierized", s(new scalar::IceEnthalpyGlacierized(this))},
3480 {"ice_enthalpy", s(new scalar::IceEnthalpy(this))},
3481 // time-stepping
3482 {"max_diffusivity", s(new scalar::MaxDiffusivity(this))},
3483 {"max_horizontal_vel", s(new scalar::MaxHorizontalVelocity(this))},
3484 {"dt", s(new scalar::TimeStepLength(this))},
3485 {"dt_ratio", s(new scalar::TimeStepRatio(this))},
3486 // balancing the books
3487 {"tendency_of_ice_mass", s(new scalar::IceMassRateOfChange(this))},
3488 {"tendency_of_ice_mass_due_to_flow", s(new scalar::IceMassRateOfChangeDueToFlow(this))},
3489 {"tendency_of_ice_mass_due_to_conservation_error", s(new scalar::IceMassFluxConservationError(this))},
3490 {"tendency_of_ice_mass_due_to_basal_mass_flux", s(new scalar::IceMassFluxBasal(this))},
3491 {"tendency_of_ice_mass_due_to_surface_mass_flux", s(new scalar::IceMassFluxSurface(this))},
3492 {"tendency_of_ice_mass_due_to_discharge", s(new scalar::IceMassFluxDischarge(this))},
3493 {"tendency_of_ice_mass_due_to_calving", s(new scalar::IceMassFluxCalving(this))},
3494 // other fluxes
3495 {"basal_mass_flux_grounded", s(new scalar::IceMassFluxBasalGrounded(this))},
3496 {"basal_mass_flux_floating", s(new scalar::IceMassFluxBasalFloating(this))},
3497 {"grounding_line_flux", s(new scalar::IceMassFluxAtGroundingLine(this))},
3498 };
3499
3500 // add ISMIP6 variable names
3501 if (m_config->get_flag("output.ISMIP6")) {
3502 result["iareafl"] = result["ice_area_glacierized_floating"];
3503 result["iareagr"] = result["ice_area_glacierized_grounded"];
3504 result["lim"] = result["ice_mass"];
3505 result["tendacabf"] = result["tendency_of_ice_mass_due_to_surface_mass_flux"];
3506 result["tendlibmassbf"] = result["tendency_of_ice_mass_due_to_basal_mass_flux"];
3507 result["tendlibmassbffl"] = result["basal_mass_flux_floating"];
3508 result["tendlicalvf"] = result["tendency_of_ice_mass_due_to_calving"];
3509 result["tendlifmassbf"] = result["tendency_of_ice_mass_due_to_discharge"];
3510 result["tendligroundf"] = result["grounding_line_flux"];
3511 }
3512
3513 // get diagnostics from submodels
3514 for (const auto& m : m_submodels) {
3515 result = pism::combine(result, m.second->scalar_diagnostics());
3516 }
3517
3518 return result;
3519}
3520
3521using Metadata = std::map<std::string, std::vector<VariableMetadata>>;
3522
3523static void print_diagnostics(const Logger &log, const Metadata &list) {
3524 for (const auto &d : list) {
3525 const std::string &name = d.first;
3526 log.message(1, " Name: %s\n", name.c_str());
3527
3528 for (const auto &v : d.second) {
3529
3530 std::string
3531 var_name = v.get_name(),
3532 units = v["units"],
3533 output_units = v["output_units"],
3534 long_name = v["long_name"],
3535 comment = v["comment"];
3536
3537 if (not output_units.empty()) {
3538 units = output_units;
3539 }
3540
3541 log.message(1, " %s [%s]\n", var_name.c_str(), units.c_str());
3542 log.message(1, " %s\n", long_name.c_str());
3543 if (not comment.empty()) {
3544 log.message(1, " %s\n", comment.c_str());
3545 }
3546 }
3547 log.message(1, "\n");
3548 }
3549}
3550
3551static void print_diagnostics_json(const Logger &log, const Metadata &list) {
3552 log.message(1, "{\n");
3553 bool first_diagnostic = true;
3554 for (const auto &d : list) {
3555
3556 if (not first_diagnostic) {
3557 log.message(1, ",\n");
3558 } else {
3559 first_diagnostic = false;
3560 }
3561
3562 log.message(1, "\"%s\" : [\n", d.first.c_str());
3563
3564 bool first_variable = true;
3565 for (const auto &variable : d.second) {
3566
3567 std::string
3568 var_name = variable.get_name(),
3569 units = variable["units"],
3570 output_units = variable["output_units"],
3571 long_name = variable["long_name"],
3572 standard_name = variable["standard_name"],
3573 comment = variable["comment"];
3574
3575 if (not output_units.empty()) {
3576 units = output_units;
3577 }
3578
3579 if (not first_variable) {
3580 log.message(1, ",\n");
3581 } else {
3582 first_variable = false;
3583 }
3584
3585 log.message(1, "[\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"]",
3586 var_name.c_str(), units.c_str(), long_name.c_str(), standard_name.c_str(), comment.c_str());
3587 }
3588 log.message(1, "]");
3589 }
3590 log.message(1, "}\n");
3591}
3592
3593/*!
3594 * Return metadata of 2D and 3D diagnostics.
3595 */
3596static Metadata spatial_diag_metadata(const std::map<std::string,Diagnostic::Ptr> &diags) {
3597 Metadata result;
3598
3599 for (const auto& f : diags) {
3600 auto diag = f.second;
3601
3602 for (unsigned int k = 0; k < diag->n_variables(); ++k) {
3603 result[f.first].push_back(diag->metadata(k));
3604 }
3605 }
3606
3607 return result;
3608}
3609
3610/*!
3611 * Return metadata of scalar diagnostics.
3612 */
3613static Metadata scalar_diag_metadata(const std::map<std::string,TSDiagnostic::Ptr> &diags) {
3614 Metadata result;
3615
3616 for (const auto& d : diags) {
3617 // always one variable per diagnostic
3618 result[d.first] = {d.second->metadata()};
3619 }
3620
3621 return result;
3622}
3623
3625
3626 if (type == DIAG_JSON) {
3627 m_log->message(1, "{\n");
3628
3629 m_log->message(1, "\"spatial\" :\n");
3631
3632 m_log->message(1, ",\n"); // separator
3633
3634 m_log->message(1, "\"scalar\" :\n");
3636
3637 m_log->message(1, "}\n");
3638
3639 return;
3640 }
3641
3642 if (type == DIAG_ALL or type == DIAG_SPATIAL) {
3643 m_log->message(1, "\n");
3644 m_log->message(1, "======== Available 2D and 3D diagnostics ========\n");
3645
3647 }
3648
3649 if (type == DIAG_ALL or type == DIAG_SCALAR) {
3650 // scalar time-series
3651 m_log->message(1, "======== Available time-series ========\n");
3652
3654 }
3655}
3656
3657/*!
3658 Computes fraction of the base which is melted.
3659
3660 Communication occurs here.
3661 */
3662double IceModel::compute_temperate_base_fraction(double total_ice_area) {
3663
3664 auto EC = m_ctx->enthalpy_converter();
3665
3666 auto cell_area = m_grid->cell_area();
3667
3668 double result = 0.0, meltarea = 0.0;
3669
3670 const array::Array3D &enthalpy = m_energy_model->enthalpy();
3671
3673 ParallelSection loop(m_grid->com);
3674 try {
3675 for (auto p : m_grid->points()) {
3676 const int i = p.i(), j = p.j();
3677
3678 if (m_geometry.cell_type.icy(i, j)) {
3679 const double
3680 E_basal = enthalpy.get_column(i, j)[0],
3681 pressure = EC->pressure(m_geometry.ice_thickness(i,j)); // FIXME issue #15
3682 // accumulate area of base which is at melt point
3683 if (EC->is_temperate_relaxed(E_basal, pressure)) {
3684 meltarea += cell_area;
3685 }
3686 }
3687 }
3688 } catch (...) {
3689 loop.failed();
3690 }
3691 loop.check();
3692
3693 // convert from m2 to km2
3694 meltarea = units::convert(m_sys, meltarea, "m^2", "km^2");
3695 // communication
3696 result = GlobalSum(m_grid->com, meltarea);
3697
3698 // normalize fraction correctly
3699 if (total_ice_area > 0.0) {
3700 result = result / total_ice_area;
3701 } else {
3702 result = 0.0;
3703 }
3704 return result;
3705}
3706
3707
3708/*!
3709 Computes fraction of the ice which is as old as the start of the run (original).
3710 Communication occurs here.
3711 */
3712double IceModel::compute_original_ice_fraction(double total_ice_volume) {
3713
3714 double result = -1.0; // result value if not age.enabled
3715
3716 if (not m_age_model) {
3717 return result; // leave now
3718 }
3719
3720 const double a = m_grid->cell_area() * 1e-3 * 1e-3, // area unit (km^2)
3721 currtime = m_time->current(); // seconds
3722
3723 const array::Array3D &ice_age = m_age_model->age();
3724
3726
3727 const double one_year = units::convert(m_sys, 1.0, "year", "seconds");
3728 double original_ice_volume = 0.0;
3729
3730 // compute local original volume
3731 ParallelSection loop(m_grid->com);
3732 try {
3733 for (auto p : m_grid->points()) {
3734 const int i = p.i(), j = p.j();
3735
3736 if (m_geometry.cell_type.icy(i, j)) {
3737 // accumulate volume of ice which is original
3738 const double *age = ice_age.get_column(i, j);
3739 auto ks = m_grid->kBelowHeight(m_geometry.ice_thickness(i,j));
3740 for (unsigned int k = 1; k <= ks; k++) {
3741 // ice in segment is original if it is as old as one year less than current time
3742 if (0.5 * (age[k - 1] + age[k]) > currtime - one_year) {
3743 original_ice_volume += a * 1.0e-3 * (m_grid->z(k) - m_grid->z(k - 1));
3744 }
3745 }
3746 }
3747 }
3748 } catch (...) {
3749 loop.failed();
3750 }
3751 loop.check();
3752
3753
3754 // communicate to turn into global original fraction
3755 result = GlobalSum(m_grid->com, original_ice_volume);
3756
3757 // normalize fraction correctly
3758 if (total_ice_volume > 0.0) {
3759 result = result / total_ice_volume;
3760 } else {
3761 result = 0.0;
3762 }
3763 return result;
3764}
3765
3766static void warn_about_missing(const Logger &log,
3767 const std::set<std::string> &vars,
3768 const std::string &type,
3769 const std::set<std::string> &available,
3770 bool stop) {
3771 std::vector<std::string> missing;
3772 for (const auto &v : vars) {
3773 if (available.find(v) == available.end()) {
3774 missing.push_back(v);
3775 }
3776 }
3777
3778 if (not missing.empty()) {
3779 size_t N = missing.size();
3780 const char *ending = N > 1 ? "s" : "";
3781 const char *verb = N > 1 ? "are" : "is";
3782 if (stop) {
3784 "%s variable%s %s %s not available!\n"
3785 "Available variables:\n- %s",
3786 type.c_str(),
3787 ending,
3788 join(missing, ",").c_str(),
3789 verb,
3790 set_join(available, ",\n- ").c_str());
3791 }
3792
3793 log.message(2,
3794 "\nWARNING: %s variable%s %s %s not available!\n\n",
3795 type.c_str(),
3796 ending,
3797 join(missing, ",").c_str(),
3798 verb);
3799 }
3800}
3801
3802/*!
3803 * De-allocate diagnostics that were not requested.
3804 *
3805 * Checks viewers, -spatial_vars, -checkpoint, -save_vars, and regular output.
3806 *
3807 * FIXME: I need to make sure that these reporting mechanisms are active. It is possible that
3808 * variables are on a list, but that list is not actually used.
3809 */
3811
3812 // get the list of available diagnostics
3813 std::set<std::string> available;
3814 for (const auto &d : m_available_spatial_diagnostics) {
3815 available.insert(d.first);
3816 }
3817
3818 auto stop = m_config->get_flag("output.spatial.stop_missing");
3819 warn_about_missing(*m_log, m_spatial_vars, "diagnostic", available, stop);
3820
3821 // get the list of requested diagnostics
3822 auto requested = set_split(m_config->get_string("output.runtime.viewer.variables"), ',');
3823 requested = combine(requested, m_output_vars);
3824 requested = combine(requested, m_snapshot_vars);
3825 requested = combine(requested, m_spatial_vars);
3826 requested = combine(requested, m_checkpoint_vars);
3827
3828 // de-allocate diagnostics that were not requested
3829 for (const auto &v : available) {
3830 if (requested.find(v) == requested.end()) {
3832 }
3833 }
3834}
3835
3836/*!
3837 * Update diagnostics.
3838 *
3839 * This usually involves accumulating data needed to computed time-averaged quantities.
3840 *
3841 * Call this after deallocate_unused_diagnostics() to avoid unnecessary work.
3842 */
3843void IceModel::update_diagnostics(double t, double dt) {
3844 for (const auto &d : m_available_spatial_diagnostics) {
3845 d.second->update(dt);
3846 }
3847
3848 for (const auto &d : m_available_scalar_diagnostics) {
3849 d.second->update(t - dt, t);
3850 }
3851}
3852
3853//! Writes variables listed in variable_names to file.
3855 const std::set<std::string> &variable_names) const {
3856 for (const auto &variable : variable_names) {
3857 auto diag = m_available_spatial_diagnostics.find(variable);
3858
3859 if (diag != m_available_spatial_diagnostics.end()) {
3860 diag->second->compute()->write(file);
3861 }
3862 }
3863}
3864
3865std::set<VariableMetadata>
3866IceModel::diagnostic_variables(const std::set<std::string> &variable_names) const {
3867 std::set<VariableMetadata> result{};
3868 {
3869 for (const auto &var : variable_names) {
3870 auto diag = m_available_spatial_diagnostics.find(var);
3871
3872 if (diag != m_available_spatial_diagnostics.end()) {
3873 const auto &D = diag->second;
3874 for (unsigned int k = 0; k < D->n_variables(); ++k) {
3875 result.insert(D->metadata(k));
3876 }
3877 }
3878 }
3879 }
3880 return result;
3881}
3882
3883std::set<VariableMetadata>
3884IceModel::state_variables_diagnostics(const std::set<std::string> &variable_names) const {
3885 std::set<VariableMetadata> result{};
3886 {
3887 for (const auto &var : variable_names) {
3888 auto diag = m_available_spatial_diagnostics.find(var);
3889
3890 if (diag != m_available_spatial_diagnostics.end()) {
3891 const auto &D = diag->second;
3892 result = pism::combine(result, D->state());
3893 }
3894 }
3895 }
3896 return result;
3897}
3898
3900 const std::set<std::string> &variable_names) const {
3901
3902 for (const auto &var : variable_names) {
3903 auto diag = m_available_spatial_diagnostics.find(var);
3904
3905 if (diag != m_available_spatial_diagnostics.end()) {
3906 const auto &D = diag->second;
3907 D->write_state(file);
3908 }
3909 }
3910}
3911
3912} // end of namespace pism
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
Definition Config.cc:188
A class for storing and accessing PISM configuration flags and parameters.
Definition Config.hh:56
const IceModel * model
A template derived from Diagnostic, adding a "Model".
std::vector< VariableMetadata > m_vars
metadata corresponding to NetCDF variables
double m_fill_value
fill value (used often enough to justify storing it)
const units::System::Ptr m_sys
the unit system
std::shared_ptr< const Config > m_config
Configuration flags and parameters.
double to_internal(double x) const
Definition Diagnostic.cc:62
std::shared_ptr< Diagnostic > Ptr
Definition Diagnostic.hh:67
std::shared_ptr< const Grid > m_grid
the grid
std::shared_ptr< array::Array > compute() const
Compute a diagnostic quantity and return a pointer to a newly-allocated Array.
Class representing diagnostic computations in PISM.
Definition Diagnostic.hh:62
High-level PISM I/O class.
Definition File.hh:57
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
const array::Scalar & thickness_change_due_to_flow() const
const array::Scalar & bottom_surface_mass_balance() const
const array::Scalar & top_surface_mass_balance() const
const array::Scalar & area_specific_volume_change_due_to_flow() const
const array::Scalar & conservation_error() const
const array::Staggered1 & flux_staggered() const
array::Scalar1 sea_level_elevation
Definition Geometry.hh:48
array::Scalar cell_grounded_fraction
Definition Geometry.hh:56
array::Scalar2 ice_surface_elevation
Definition Geometry.hh:57
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::Scalar longitude
Definition Geometry.hh:42
array::Scalar2 bed_elevation
Definition Geometry.hh:47
array::Scalar latitude
Definition Geometry.hh:41
Describes the PISM grid and the distribution of data across processors.
Definition Grid.hh:285
std::map< std::string, const Component * > m_submodels
the list of sub-models, for writing model states and obtaining diagnostics
Definition IceModel.hh:297
std::set< std::string > m_snapshot_vars
Definition IceModel.hh:473
virtual double compute_temperate_base_fraction(double ice_area)
const Geometry & geometry() const
Definition IceModel.cc:903
void init_checkpoints()
Initialize checkpointing (snapshot-on-wallclock-time) mechanism.
std::set< std::string > m_spatial_vars
Definition IceModel.hh:494
std::shared_ptr< Config > m_config
Configuration flags and parameters.
Definition IceModel.hh:278
virtual std::map< std::string, Diagnostic::Ptr > allocate_spatial_diagnostics()
Geometry m_geometry
Definition IceModel.hh:333
const GeometryEvolution & geometry_evolution() const
Definition IceModel.cc:907
const stressbalance::StressBalance * stress_balance() const
Definition IceModel.cc:911
virtual double compute_original_ice_fraction(double ice_volume)
std::shared_ptr< Context > m_ctx
Execution context.
Definition IceModel.hh:280
std::shared_ptr< Logger > m_log
Logger.
Definition IceModel.hh:284
std::set< VariableMetadata > m_spatial_file_contents
set of variables that will be written to extra files
Definition IceModel.hh:497
array::Scalar m_basal_melt_rate
rate of production of basal meltwater (ice-equivalent); no ghosts
Definition IceModel.hh:340
std::shared_ptr< Grid > grid() const
Return the grid used by this model.
Definition utilities.cc:111
virtual std::set< VariableMetadata > diagnostic_variables(const std::set< std::string > &variable_names) const
std::set< VariableMetadata > m_output_file_contents
Set of variables that will be written to the output file.
Definition IceModel.hh:239
void list_diagnostics(DiagnosticReport report_type) const
array::Scalar m_bedtoptemp
temperature at the top surface of the bedrock thermal layer
Definition IceModel.hh:342
std::set< VariableMetadata > state_variables_diagnostics(const std::set< std::string > &variable_names) const
const energy::EnergyModel * energy_balance_model() const
Definition IceModel.cc:923
virtual void update_diagnostics(double t, double dt)
std::shared_ptr< OutputWriter > m_snapshot_writer
Definition IceModel.hh:289
void init_outputs(InputOptions options, DiagnosticReport report_type)
std::shared_ptr< Time > m_time
Time manager.
Definition IceModel.hh:286
const array::Scalar & frontal_melt() const
Definition IceModel.cc:945
const array::Scalar & forced_retreat() const
Definition IceModel.cc:952
double dt() const
Definition IceModel.cc:151
std::set< std::string > m_checkpoint_vars
Definition IceModel.hh:507
array::Scalar2 m_velocity_bc_mask
mask to determine Dirichlet boundary locations for the sliding velocity
Definition IceModel.hh:347
void init_scalar_diagnostics()
Initializes the code writing scalar time-series.
std::map< std::string, Diagnostic::Ptr > m_available_spatial_diagnostics
Available spatially-variable diagnostics.
Definition IceModel.hh:461
void write_state_diagnostics(const OutputFile &file, const std::set< std::string > &variable_names) const
virtual std::map< std::string, TSDiagnostic::Ptr > allocate_scalar_diagnostics()
void write_diagnostics(const OutputFile &file, const std::set< std::string > &variable_names) const
Writes variables listed in variable_names to file.
std::set< VariableMetadata > m_snapshot_file_contents
set of variables that will be written to snapshot files
Definition IceModel.hh:475
array::Scalar1 m_ice_thickness_bc_mask
Mask prescribing locations where ice thickness is held constant.
Definition IceModel.hh:352
std::shared_ptr< AgeModel > m_age_model
Definition IceModel.hh:307
void init_spatial_diagnostics()
Initialize the code saving spatially-variable diagnostic quantities.
const units::System::Ptr m_sys
Unit system.
Definition IceModel.hh:282
std::map< std::string, TSDiagnostic::Ptr > m_available_scalar_diagnostics
Available scalar diagnostics.
Definition IceModel.hh:463
array::Vector2 m_velocity_bc_values
Dirichlet boundary velocities.
Definition IceModel.hh:349
void init_snapshots()
Initializes the snapshot-saving mechanism.
std::shared_ptr< OutputWriter > m_output_writer
Definition IceModel.hh:288
const array::Scalar & calving() const
Definition IceModel.cc:938
void init_final_output()
Definition output.cc:114
std::shared_ptr< Context > ctx() const
Return the context this model is running in.
Definition utilities.cc:116
std::set< std::string > m_output_vars
Definition IceModel.hh:236
std::set< VariableMetadata > m_checkpoint_file_contents
set of variables that will be written to checkpoint files
Definition IceModel.hh:509
std::shared_ptr< energy::EnergyModel > m_energy_model
Definition IceModel.hh:305
const std::shared_ptr< Grid > m_grid
Computational grid.
Definition IceModel.hh:276
void deallocate_unused_diagnostics()
void message(int threshold, const char format[],...) const __attribute__((format(printf
Print a message to the log.
Definition Logger.cc:50
A basic logging class.
Definition Logger.hh:40
void failed()
Indicates a failure of a parallel section.
A wrapper for PJ that makes sure pj_destroy is called.
Definition Proj.hh:32
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
void set_units(const std::string &units, const std::string &output_units)
VariableMetadata m_variable
std::shared_ptr< const Config > m_config
Configuration flags and parameters.
std::shared_ptr< const Grid > m_grid
the grid
std::shared_ptr< TSDiagnostic > Ptr
Scalar diagnostic reporting a "flux".
Scalar diagnostic reporting the rate of change of a quantity modeled by PISM.
Scalar diagnostic reporting a snapshot of a quantity modeled by PISM.
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & set_name(const std::string &name)
VariableMetadata & standard_name(const std::string &input)
VariableMetadata & output_units(const std::string &input)
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:66
void copy_from(const Array2D< T > &source)
Definition Array2D.hh:101
stencils::Star< T > star(int i, int j) const
Definition Array2D.hh:107
void add(double alpha, const Array2D< T > &x)
Definition Array2D.hh:93
void copy_from(const Array3D &input)
Definition Array3D.cc:215
double * get_column(int i, int j)
Definition Array3D.cc:127
A virtual class collecting methods common to ice and bedrock 3D fields.
Definition Array3D.hh:33
std::shared_ptr< const Grid > grid() const
Definition Array.cc:134
VariableMetadata & metadata(unsigned int N=0)
Returns a reference to the VariableMetadata object containing metadata for the compoment N.
Definition Array.cc:467
stencils::Star< int > star_int(int i, int j) const
Definition Scalar.hh:72
bool next_to_ice_free_ocean(int i, int j) const
Definition CellType.hh:106
bool ice_free_ocean(int i, int j) const
Definition CellType.hh:58
bool grounded_ice(int i, int j) const
Definition CellType.hh:46
bool icy(int i, int j) const
Definition CellType.hh:42
BMBSplit(const IceModel *m, AreaType flag)
Report average basal mass balance flux over the reporting interval (grounded or floating areas)
BasalFlux(const IceModel *m, AmountKind kind)
Report basal mass balance flux, averaged over the reporting interval.
std::shared_ptr< array::Array > compute_impl() const
Sea-level adjusted bed topography (zero at sea level).
CTS(const IceModel *m)
virtual std::shared_ptr< array::Array > compute_impl() const
Computes CTS, CTS = E/E_s(p).
CalvingFlux(const IceModel *m, AmountKind kind)
Report the calving flux.
ConservationErrorFlux(const IceModel *m, AmountKind kind)
DischargeFlux(const IceModel *m, AmountKind kind)
Report discharge (calving and frontal melt) flux.
ForcedRetreatFlux(const IceModel *m, AmountKind kind)
Report the frontal melt flux.
FrontalMeltFlux(const IceModel *m, AmountKind kind)
Report the frontal melt flux.
Report grounding line flux.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes vertically-averaged ice hardness.
Computes vertically-averaged ice hardness.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes the 2D height above flotation.
virtual std::shared_ptr< array::Array > compute_impl() const
virtual std::shared_ptr< array::Array > compute_impl() const
virtual std::shared_ptr< array::Array > compute_impl() const
std::shared_ptr< array::Array > compute_impl() const
Report ice top surface elevation.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes enthalpy at the base of the ice.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes surface values of ice enthalpy.
std::shared_ptr< array::Array > compute_impl() const
Ice hardness computed using the SIA flow law.
std::shared_ptr< array::Array > compute_impl() const
Ocean pressure difference at calving fronts. Used to debug CF boundary conditins.
IceMass(const IceModel *m)
virtual std::shared_ptr< array::Array > compute_impl() const
Computes the mass per cell.
std::shared_ptr< array::Array > compute_impl() const
Report ice top surface elevation.
std::shared_ptr< array::Array > compute_impl() const
std::shared_ptr< array::Array > compute_impl() const
Effective viscosity of ice (3D).
LatLonBounds(const IceModel *m, const std::string &var_name, const std::string &proj_string)
virtual std::shared_ptr< array::Array > compute_impl() const
Computes latitude and longitude bounds.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes the liquid water fraction.
Rank(const IceModel *m)
virtual std::shared_ptr< array::Array > compute_impl() const
Computes a diagnostic field filled with processor rank values.
SurfaceFlux(const IceModel *m, AmountKind kind)
Report surface mass balance flux, averaged over the reporting interval.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes the thickness of the basal layer of temperate ice.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes the total thickness of temperate ice in a column.
std::shared_ptr< array::Array > compute_impl() const
TemperatureBasal(const IceModel *m, AreaType area_type)
Computes ice temperature at the base of the ice.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes basal values of the pressure-adjusted temperature.
virtual std::shared_ptr< array::Array > compute_impl() const
Compute the pressure-adjusted temperature in degrees C corresponding to ice temperature.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes ice temperature at the surface of the ice.
virtual std::shared_ptr< array::Array > compute_impl() const
Computes ice temperature from enthalpy.
TendencyOfIceAmountDueToFlow(const IceModel *Model, AmountKind kind)
Computes tendency_of_ice_amount_due_to_flow, the rate of change of ice amount due to flow.
std::shared_ptr< array::Array > compute_impl() const
TendencyOfIceAmount(const IceModel *Model, AmountKind kind)
Computes tendency_of_ice_amount, the ice amount rate of change.
std::shared_ptr< array::Array > compute_impl() const
Computes dHdt, the ice thickness rate of change.
Computes the total area of the cold ice.
Computes the total grounded ice area.
Computes the total floating ice area.
Computes the total area of the temperate ice.
Computes the total ice enthalpy in glacierized areas.
Computes the total ice enthalpy.
Reports the total flux across the grounding line.
Reports the total sub-shelf ice flux.
Reports the total basal ice flux over the grounded region.
Reports the total bottom surface ice flux.
Reports the total numerical mass flux needed to preserve non-negativity of ice thickness.
Reports the total discharge flux.
Reports the total top surface ice flux.
Computes the total ice mass in glacierized areas.
Computes the total mass of the ice not displacing sea water.
Computes the rate of change of the total ice mass due to flow (influx due to prescribed constant-in-t...
Computes the rate of change of the total ice mass in glacierized areas.
Computes the rate of change of the total ice mass.
Computes the total ice mass.
Computes the total volume of the cold ice.
Computes the total volume of the cold ice in glacierized areas.
Computes the total grounded ice volume.
Computes the total floating ice volume.
Computes the total volume of the temperate ice in glacierized areas.
Computes the total ice volume in glacierized areas.
Computes the rate of change of the total ice volume in glacierized areas.
Computes the rate of change of the total ice volume.
Computes the total volume of the temperate ice.
Computes the total ice volume.
Reports the maximum horizontal absolute velocity component over the grid.
Computes the total ice volume which is relevant for sea-level.
Reports the mass continuity time step.
Reports the mass continuity time step.
const array::Array3D & enthalpy() const
void effective_viscosity(double hardness, double gamma, double *nu, double *dnu) const
Computes the regularized effective viscosity and its derivative with respect to the second invariant ...
Definition FlowLaw.cc:170
double hardness(double E, double p) const
Definition FlowLaw.cc:119
std::shared_ptr< const rheology::FlowLaw > flow_law() const
std::shared_ptr< const rheology::FlowLaw > flow_law() const
const array::Array3D & velocity_w() const
const SSB_Modifier * modifier() const
Returns a pointer to a stress balance modifier implementation.
const array::Array3D & velocity_u() const
Get components of the the 3D velocity field.
double max_diffusivity() const
Get the max diffusivity (for the adaptive time-stepping).
const ShallowStressBalance * shallow() const
Returns a pointer to a shallow stress balance solver implementation.
const array::Array3D & velocity_v() const
#define PISM_ERROR_LOCATION
const double rho_ice
Definition exactTestK.c:31
@ WITH_GHOSTS
Definition Array.hh:63
@ WITHOUT_GHOSTS
Definition Array.hh:63
static double ice_volume(const array::Scalar &ice_thickness, const array::Array3D &ice_enthalpy, IceKind kind, double thickness_threshold)
static double base_area(const array::Scalar &ice_thickness, const array::Array3D &ice_enthalpy, IceKind kind, double thickness_threshold)
double mass_change(const IceModel *model, TermType term, AreaType area)
static double square(double x)
static void accumulate_changes(const IceModel *model, double factor, ChangeKind kind, array::Scalar &accumulator)
void compute_liquid_water_fraction(const array::Array3D &enthalpy, const array::Scalar &ice_thickness, array::Array3D &result)
Compute the liquid fraction corresponding to enthalpy and ice_thickness.
Definition utilities.cc:136
void compute_cts(const array::Array3D &ice_enthalpy, const array::Scalar &ice_thickness, array::Array3D &result)
Compute the CTS field, CTS = E/E_s(p), from ice_enthalpy and ice_thickness, and put in result.
Definition utilities.cc:178
double total_ice_enthalpy(double thickness_threshold, const array::Array3D &ice_enthalpy, const array::Scalar &ice_thickness)
Computes the total ice enthalpy in J.
Definition utilities.cc:217
bool domain_edge(const Grid &grid, int i, int j)
Definition Grid.hh:410
@ PISM_GUESS
Definition IO_Flags.hh:57
@ PISM_READONLY
open an existing file for reading only
Definition IO_Flags.hh:69
bool ice_free(int M)
Ice-free cell (grounded or ocean).
Definition Mask.hh:58
double averaged_hardness(const FlowLaw &ice, double thickness, unsigned int kbelowH, const double *zlevels, const double *enthalpy)
Computes vertical average of B(E, p) ice hardness, namely .
Definition FlowLaw.cc:221
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
Definition Units.cc:70
double ice_volume_not_displacing_seawater(const Geometry &geometry, double thickness_threshold)
Definition Geometry.cc:310
double ice_area(const Geometry &geometry, double thickness_threshold)
Computes ice area, in m^2.
Definition Geometry.cc:359
std::map< std::string, std::vector< VariableMetadata > > Metadata
double grounded_area_fraction(double a, double b, double c)
DiagnosticReport
Definition IceModel.hh:117
@ DIAG_JSON
Definition IceModel.hh:117
@ DIAG_SPATIAL
Definition IceModel.hh:117
@ DIAG_ALL
Definition IceModel.hh:117
@ DIAG_SCALAR
Definition IceModel.hh:117
static const char * grounded_ice_sheet_area_fraction_name
double average_water_column_pressure(double ice_thickness, double bed, double floatation_level, double rho_ice, double rho_water, double g)
static const double g
Definition exactTestP.cc:36
@ INIT_RESTART
Definition Component.hh:56
std::string set_join(const std::set< std::string > &input, const std::string &separator)
void compute_grounded_cell_fraction(double ice_density, double ocean_density, const array::Scalar1 &sea_level, const array::Scalar1 &ice_thickness, const array::Scalar1 &bed_topography, array::Scalar &result)
double ice_area_floating(const Geometry &geometry, double thickness_threshold)
Computes floating ice area, in m^2.
Definition Geometry.cc:376
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 sea_level_rise_potential(const Geometry &geometry, double thickness_threshold)
Computes the sea level rise that would result if all the ice were melted.
Definition Geometry.cc:385
double total_grounding_line_flux(const array::CellType1 &cell_type, const array::Staggered1 &flux, double dt)
static const double k
Definition exactTestP.cc:42
static void warn_about_missing(const Logger &log, const std::set< std::string > &vars, const std::string &type, const std::set< std::string > &available, bool stop)
std::set< std::string > set_split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a set of strings.
static void print_diagnostics_json(const Logger &log, const Metadata &list)
double ice_volume(const Geometry &geometry, double thickness_threshold)
Computes the ice volume, in m^3.
Definition Geometry.cc:277
void ice_flow_rate_across_grounding_line(const array::CellType1 &cell_type, const array::Staggered1 &flux, double unit_conversion_factor, array::Scalar &output)
void ice_bottom_surface(const Geometry &geometry, array::Scalar &result)
Definition Geometry.cc:241
MaxTimestep max_timestep_diffusivity(double D_max, double dx, double dy, double adaptive_timestepping_ratio)
static Metadata spatial_diag_metadata(const std::map< std::string, Diagnostic::Ptr > &diags)
static void print_diagnostics(const Logger &log, const Metadata &list)
double ice_area_grounded(const Geometry &geometry, double thickness_threshold)
Computes grounded ice area, in m^2.
Definition Geometry.cc:367
T combine(const T &a, const T &b)
static Metadata scalar_diag_metadata(const std::map< std::string, TSDiagnostic::Ptr > &diags)
void compute_lat_bounds(const std::string &projection, array::Array3D &result)
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
void compute_lon_bounds(const std::string &projection, array::Array3D &result)
bool set_member(const std::string &string, const std::set< std::string > &set)
static const char * land_ice_area_fraction_name
static const char * floating_ice_sheet_area_fraction_name
InitializationType type
initialization type
Definition Component.hh:61
std::string filename
name of the input file (if applicable)
Definition Component.hh:63
unsigned int record
index of the record to re-start from
Definition Component.hh:65