PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
Geometry.cc
Go to the documentation of this file.
1/* Copyright (C) 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025 PISM Authors
2 *
3 * This file is part of PISM.
4 *
5 * PISM is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with PISM; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20#include <functional>
21
22#include "pism/geometry/Geometry.hh"
23
24#include "pism/util/array/CellType.hh"
25#include "pism/util/Mask.hh"
26#include "pism/util/pism_utilities.hh"
27#include "pism/geometry/grounded_cell_fraction.hh"
28#include "pism/util/Context.hh"
29#include "pism/util/VariableMetadata.hh"
30#include "pism/util/io/File.hh"
31#include "pism/util/io/io_helpers.hh"
32#include "pism/util/io/IO_Flags.hh"
33#include "pism/util/Time.hh"
34#include "pism/util/io/SynchronousOutputWriter.hh"
35
36namespace pism {
37
38Geometry::Geometry(const std::shared_ptr<const Grid> &grid)
39 // FIXME: ideally these fields should be "global", i.e. without ghosts.
40 // (However this may increase communication costs...)
41 : latitude(grid, "lat"),
42 longitude(grid, "lon"),
43 bed_elevation(grid, "topg"),
44 sea_level_elevation(grid, "sea_level"),
45 ice_thickness(grid, "thk"),
46 ice_area_specific_volume(grid, "ice_area_specific_volume"),
47 cell_type(grid, "mask"),
48 cell_grounded_fraction(grid, "cell_grounded_fraction"),
49 ice_surface_elevation(grid, "usurf") {
50
52 .long_name("latitude")
53 .units("degree_north")
54 .standard_name("latitude")
55 .set_time_dependent(false);
56 latitude.metadata()["grid_mapping"] = "";
57 latitude.metadata()["valid_range"] = { -90.0, 90.0 };
58
60 .long_name("longitude")
61 .units("degree_east")
62 .standard_name("longitude")
63 .set_time_dependent(false);
64 longitude.metadata()["grid_mapping"] = "";
65 longitude.metadata()["valid_range"] = { -180.0, 180.0 };
66
68 .long_name("bedrock surface elevation")
69 .units("m")
70 .standard_name("bedrock_altitude");
71
73 .long_name("sea level elevation above reference ellipsoid")
74 .units("meters")
75 .standard_name("sea_surface_height_above_reference_ellipsoid");
76
78 .long_name("land ice thickness")
79 .units("m")
80 .standard_name("land_ice_thickness");
81 ice_thickness.metadata()["valid_min"] = { 0.0 };
82
84 .long_name("ice-volume-per-area in partially-filled grid cells")
85 .units("m^3/m^2");
87 "this variable represents the amount of ice in a partially-filled cell and not "
88 "the corresponding geometry, so thinking about it as 'thickness' is not helpful";
89
91 .long_name("ice-type (ice-free/grounded/floating/ocean) integer mask")
95 cell_type.metadata()["flag_meanings"] =
96 "ice_free_bedrock grounded_ice floating_ice ice_free_ocean";
97
99 "fractional grounded/floating mask (floating=0, grounded=1)");
100
102 .long_name("ice upper surface elevation")
103 .units("m")
104 .standard_name("surface_altitude");
105
106 // make sure all the fields are initialized
107 latitude.set(0.0);
108 longitude.set(0.0);
109 bed_elevation.set(0.0);
111 ice_thickness.set(0.0);
114}
115
116void Geometry::ensure_consistency(double ice_free_thickness_threshold) {
117 auto grid = ice_thickness.grid();
118 auto config = grid->ctx()->config();
119
123
124 // first ensure that ice_area_specific_volume is 0 if ice_thickness > 0.
125 {
126 ParallelSection loop(grid->com);
127 try {
128 for (auto p : grid->points()) {
129 const int i = p.i(), j = p.j();
130
131 if (ice_thickness(i, j) < 0.0) {
133 "H = %e (negative) at point i=%d, j=%d",
134 ice_thickness(i, j), i, j);
135 }
136
137 if (ice_thickness(i, j) > 0.0 and ice_area_specific_volume(i, j) > 0.0) {
139 ice_area_specific_volume(i, j) = 0.0;
140 }
141 }
142 } catch (...) {
143 loop.failed();
144 }
145 loop.check();
146 }
147
148 // compute cell type and surface elevation
149 {
150 GeometryCalculator gc(*config);
151 gc.set_icefree_thickness(ice_free_thickness_threshold);
152
153 ParallelSection loop(grid->com);
154 try {
155 for (auto p : grid->points()) {
156 const int i = p.i(), j = p.j();
157
158 int mask = 0;
160 &mask, &ice_surface_elevation(i, j));
161 cell_type(i, j) = mask;
162 }
163 } catch (...) {
164 loop.failed();
165 }
166 loop.check();
167 }
168
173
174 const double
175 ice_density = config->get_number("constants.ice.density"),
176 ocean_density = config->get_number("constants.sea_water.density");
177
178 try {
180 ocean_density,
185 } catch (RuntimeError &e) {
186 e.add_context("computing the grounded cell fraction");
187
188 std::string output_file = config->get_string("output.file");
189 std::string o_file = filename_add_suffix(output_file,
190 "_grounded_cell_fraction_failed", "");
191 // save geometry to a file for debugging
192 dump(o_file.c_str());
193 throw;
194 }
195}
196
197void Geometry::dump(const char *filename) const {
198 auto grid = ice_thickness.grid();
199 auto ctx = grid->ctx();
200 auto config = ctx->config();
201
202 VariableMetadata mapping{ "mapping", ctx->unit_system() };
203 auto writer = std::make_shared<SynchronousOutputWriter>(ctx->com(), *config);
204 writer->initialize({}, true);
205
206 OutputFile file(writer, filename);
207
208 auto time = grid->ctx()->time();
209
210 const array::Array *variables[] = { &latitude,
211 &longitude,
216 &cell_type,
219
220 {
221 file.define_variable(time->metadata());
222
223 for (const auto *v : variables) {
224 for (const auto &var : v->all_metadata()) {
225 file.define_variable(var);
226 }
227 }
228 }
229
230 {
231 file.append_time(time->current());
232
233 for (const auto *v : variables) {
234 v->write(file);
235 }
236 }
237}
238
239/*! Compute the elevation of the bottom surface of the ice.
240 */
241void ice_bottom_surface(const Geometry &geometry, array::Scalar &result) {
242
243 auto grid = result.grid();
244 auto config = grid->ctx()->config();
245
246 double
247 ice_density = config->get_number("constants.ice.density"),
248 water_density = config->get_number("constants.sea_water.density"),
249 alpha = ice_density / water_density;
250
251 const array::Scalar &ice_thickness = geometry.ice_thickness;
252 const array::Scalar &bed_elevation = geometry.bed_elevation;
253 const array::Scalar &sea_level = geometry.sea_level_elevation;
254
255 array::AccessScope list{&ice_thickness, &bed_elevation, &sea_level, &result};
256
257 ParallelSection loop(grid->com);
258 try {
259 for (auto p : grid->points()) {
260 const int i = p.i(), j = p.j();
261
262 double
263 b_grounded = bed_elevation(i, j),
264 b_floating = sea_level(i, j) - alpha * ice_thickness(i, j);
265
266 result(i, j) = std::max(b_grounded, b_floating);
267 }
268 } catch (...) {
269 loop.failed();
270 }
271 loop.check();
272
273 result.update_ghosts();
274}
275
276//! Computes the ice volume, in m^3.
277double ice_volume(const Geometry &geometry, double thickness_threshold) {
278 auto grid = geometry.ice_thickness.grid();
279 auto config = grid->ctx()->config();
280
281 array::AccessScope list{&geometry.ice_thickness};
282
283 double volume = 0.0;
284
285 auto cell_area = grid->cell_area();
286
287 {
288 for (auto p : grid->points()) {
289 const int i = p.i(), j = p.j();
290
291 if (geometry.ice_thickness(i,j) >= thickness_threshold) {
292 volume += geometry.ice_thickness(i,j) * cell_area;
293 }
294 }
295 }
296
297 // Add the volume of the ice in Href:
298 if (config->get_flag("geometry.part_grid.enabled")) {
299 list.add(geometry.ice_area_specific_volume);
300 for (auto p : grid->points()) {
301 const int i = p.i(), j = p.j();
302
303 volume += geometry.ice_area_specific_volume(i,j) * cell_area;
304 }
305 }
306
307 return GlobalSum(grid->com, volume);
308}
309
311 double thickness_threshold) {
312 auto grid = geometry.ice_thickness.grid();
313 auto config = grid->ctx()->config();
314
315 const double
316 sea_water_density = config->get_number("constants.sea_water.density"),
317 ice_density = config->get_number("constants.ice.density"),
318 cell_area = grid->cell_area();
319
320 array::AccessScope list{&geometry.cell_type, &geometry.ice_thickness,
321 &geometry.bed_elevation, &geometry.sea_level_elevation};
322
323 double volume = 0.0;
324
325 for (auto p : grid->points()) {
326 const int i = p.i(), j = p.j();
327
328 const double
329 bed = geometry.bed_elevation(i, j),
330 thickness = geometry.ice_thickness(i, j),
331 sea_level = geometry.sea_level_elevation(i, j);
332
333 if (geometry.cell_type.grounded(i, j) and thickness > thickness_threshold) {
334 double max_floating_thickness =
335 std::max(sea_level - bed, 0.0) * (sea_water_density / ice_density);
336 volume += cell_area * (thickness - max_floating_thickness);
337 }
338 } // end of the loop over grid points
339
340 return GlobalSum(grid->com, volume);
341}
342
343static double compute_area(const Grid &grid, std::function<bool(int, int)> condition) {
344 double cell_area = grid.cell_area();
345 double area = 0.0;
346
347 for (auto p : grid.points()) {
348 const int i = p.i(), j = p.j();
349
350 if (condition(i, j)) {
351 area += cell_area;
352 }
353 }
354
355 return GlobalSum(grid.com, area);
356}
357
358//! Computes ice area, in m^2.
359double ice_area(const Geometry &geometry, double thickness_threshold) {
360 array::AccessScope list{ &geometry.ice_thickness };
361 return compute_area(*geometry.ice_thickness.grid(), [&](int i, int j) {
362 return geometry.ice_thickness(i, j) >= thickness_threshold;
363 });
364}
365
366//! Computes grounded ice area, in m^2.
367double ice_area_grounded(const Geometry &geometry, double thickness_threshold) {
368 array::AccessScope list{ &geometry.cell_type, &geometry.ice_thickness };
369 return compute_area(*geometry.ice_thickness.grid(), [&](int i, int j) {
370 return (geometry.cell_type.grounded(i, j) and
371 geometry.ice_thickness(i, j) >= thickness_threshold);
372 });
373}
374
375//! Computes floating ice area, in m^2.
376double ice_area_floating(const Geometry &geometry, double thickness_threshold) {
377 array::AccessScope list{ &geometry.cell_type, &geometry.ice_thickness };
378 return compute_area(*geometry.ice_thickness.grid(), [&](int i, int j) {
379 return (geometry.cell_type.ocean(i, j) and geometry.ice_thickness(i, j) >= thickness_threshold);
380 });
381}
382
383
384//! Computes the sea level rise that would result if all the ice were melted.
385double sea_level_rise_potential(const Geometry &geometry, double thickness_threshold) {
386 auto config = geometry.ice_thickness.grid()->ctx()->config();
387
388 const double
389 water_density = config->get_number("constants.fresh_water.density"),
390 ice_density = config->get_number("constants.ice.density"),
391 ocean_area = config->get_number("constants.global_ocean_area");
392
393 const double
394 volume = ice_volume_not_displacing_seawater(geometry,
395 thickness_threshold),
396 additional_water_volume = (ice_density / water_density) * volume,
397 sea_level_change = additional_water_volume / ocean_area;
398
399 return sea_level_change;
400}
401
402
403/*!
404 * @brief Set no_model_mask variable to have value 1 in strip of width 'strip' m around
405 * edge of computational domain, and value 0 otherwise.
406 */
407void set_no_model_strip(const Grid &grid, double width, array::Scalar &result) {
408
409 if (width <= 0.0) {
410 return;
411 }
412
413 array::AccessScope list(result);
414
415 for (auto p : grid.points()) {
416 const int i = p.i(), j = p.j();
417
418 if (grid::in_null_strip(grid, i, j, width)) {
419 result(i, j) = 1;
420 } else {
421 result(i, j) = 0;
422 }
423 }
424
425 result.update_ghosts();
426}
427
428} // end of namespace pism
void set_icefree_thickness(double threshold)
Definition Mask.hh:81
void compute(const array::Scalar &sea_level, const array::Scalar &bed, const array::Scalar &thickness, array::Scalar &out_mask, array::Scalar &out_surface) const
Definition Mask.cc:27
array::Scalar1 sea_level_elevation
Definition Geometry.hh:48
array::Scalar cell_grounded_fraction
Definition Geometry.hh:56
void ensure_consistency(double ice_free_thickness_threshold)
Definition Geometry.cc:116
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
void dump(const char *filename) const
Definition Geometry.cc:197
array::Scalar2 ice_thickness
Definition Geometry.hh:51
Geometry(const std::shared_ptr< const Grid > &grid)
Definition Geometry.cc:38
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
void append_time(double time_seconds) const
Definition OutputFile.cc:41
void define_variable(const VariableMetadata &variable) const
Definition OutputFile.cc:31
void failed()
Indicates a failure of a parallel section.
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & standard_name(const std::string &input)
std::shared_ptr< units::System > unit_system() const
VariableMetadata & set_output_type(io::Type type)
VariableMetadata & set_time_dependent(bool flag)
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:66
void add(double alpha, const Array2D< T > &x)
Definition Array2D.hh:93
std::shared_ptr< const Grid > grid() const
Definition Array.cc:134
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:659
void update_ghosts()
Updates ghost points.
Definition Array.cc:645
VariableMetadata & metadata(unsigned int N=0)
Returns a reference to the VariableMetadata object containing metadata for the compoment N.
Definition Array.cc:467
Abstract class for reading, writing, allocating, and accessing a DA-based PETSc Vec (2D and 3D fields...
Definition Array.hh:209
bool grounded(int i, int j) const
Definition CellType.hh:38
#define PISM_ERROR_LOCATION
bool in_null_strip(const Grid &grid, int i, int j, double strip_width)
Check if a point (i,j) is in the strip of stripwidth meters around the edge of the computational doma...
Definition Grid.hh:399
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
void set_no_model_strip(const Grid &grid, double width, array::Scalar &result)
Set no_model_mask variable to have value 1 in strip of width 'strip' m around edge of computational d...
Definition Geometry.cc:407
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 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
@ MASK_FLOATING
Definition Mask.hh:34
@ MASK_ICE_FREE_OCEAN
Definition Mask.hh:35
@ MASK_ICE_FREE_BEDROCK
Definition Mask.hh:32
@ MASK_GROUNDED
Definition Mask.hh:33
std::string filename_add_suffix(const std::string &filename, const std::string &separator, const std::string &suffix)
Adds a suffix to a filename.
double ice_volume(const Geometry &geometry, double thickness_threshold)
Computes the ice volume, in m^3.
Definition Geometry.cc:277
void ice_bottom_surface(const Geometry &geometry, array::Scalar &result)
Definition Geometry.cc:241
static double compute_area(const Grid &grid, std::function< bool(int, int)> condition)
Definition Geometry.cc:343
double ice_area_grounded(const Geometry &geometry, double thickness_threshold)
Computes grounded ice area, in m^2.
Definition Geometry.cc:367
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)