PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
Pico.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2019, 2021, 2022, 2023, 2024, 2025, 2026 Constantine Khrulev, Ricarda Winkelmann, Ronja Reese, Torsten
2// Albrecht, and Matthias Mengel
3//
4// This file is part of PISM.
5//
6// PISM is free software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the Free Software
8// Foundation; either version 2 of the License, or (at your option) any later
9// version.
10//
11// PISM is distributed in the hope that it will be useful, but WITHOUT ANY
12// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14// details.
15//
16// You should have received a copy of the GNU General Public License
17// along with PISM; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19//
20// Please cite this model as:
21// 1.
22// Antarctic sub-shelf melt rates via PICO
23// R. Reese, T. Albrecht, M. Mengel, X. Asay-Davis and R. Winkelmann
24// The Cryosphere, 12, 1969-1985, (2018)
25// DOI: 10.5194/tc-12-1969-2018
26//
27// 2.
28// A box model of circulation and melting in ice shelf caverns
29// D. Olbers & H. Hellmer
30// Ocean Dynamics (2010), Volume 60, Issue 1, pp 141–153
31// DOI: 10.1007/s10236-009-0252-z
32
33#include <gsl/gsl_math.h> // GSL_NAN
34
35#include "pism/coupler/util/options.hh"
36#include "pism/geometry/Geometry.hh"
37#include "pism/util/Config.hh"
38#include "pism/util/Grid.hh"
39#include "pism/util/Mask.hh"
40#include "pism/util/Time.hh"
41
42#include "pism/coupler/ocean/Pico.hh"
43#include "pism/coupler/ocean/PicoGeometry.hh"
44#include "pism/coupler/ocean/PicoPhysics.hh"
45#include "pism/util/array/Forcing.hh"
46#include "pism/util/Logger.hh"
47#include "pism/util/io/IO_Flags.hh"
48
49namespace pism {
50namespace ocean {
51
52Pico::Pico(std::shared_ptr<const Grid> grid)
53 : CompleteOceanModel(grid, std::shared_ptr<OceanModel>()),
54 m_Soc(m_grid, "pico_salinity"),
55 m_Soc_box0(m_grid, "pico_salinity_box0"),
56 m_Toc(m_grid, "pico_temperature"),
57 m_Toc_box0(m_grid, "pico_temperature_box0"),
58 m_T_star(m_grid, "pico_T_star"),
59 m_overturning(m_grid, "pico_overturning"),
60 m_basal_melt_rate(m_grid, "pico_basal_melt_rate"),
61 m_geometry(grid),
62 m_n_basins(0),
63 m_n_boxes(0),
64 m_n_shelves(0) {
65
66 ForcingOptions opt(*m_grid->ctx(), "ocean.pico");
67
68 {
69 auto buffer_size = static_cast<int>(m_config->get_number("input.forcing.buffer_size"));
70
72
73 m_theta_ocean = std::make_shared<array::Forcing>(m_grid,
74 file,
75 "theta_ocean",
76 "", // no standard name
77 buffer_size,
78 opt.periodic,
79 LINEAR);
80
81 m_salinity_ocean = std::make_shared<array::Forcing>(m_grid,
82 file,
83 "salinity_ocean",
84 "", // no standard name
85 buffer_size,
86 opt.periodic,
87 LINEAR);
88 }
89
90 m_theta_ocean->metadata(0)
91 .long_name("potential temperature of the adjacent ocean")
92 .units("kelvin");
93
94 m_salinity_ocean->metadata(0)
95 .long_name("salinity of the adjacent ocean")
96 .units("g/kg");
97
98 // computed salinity in ocean boxes
99 m_Soc.metadata(0).long_name("ocean salinity field").units("g/kg");
100 m_Soc.metadata()["_FillValue"] = { 0.0 };
101
102 // salinity input for box 1
103 m_Soc_box0.metadata(0).long_name("ocean base salinity field").units("g/kg");
104 m_Soc_box0.metadata()["_FillValue"] = { 0.0 };
105
106 // computed temperature in ocean boxes
107 m_Toc.metadata(0).long_name("ocean temperature field").units("kelvin");
108 m_Toc.metadata()["_FillValue"] = { 0.0 };
109
110 // temperature input for box 1
111 m_Toc_box0.metadata(0).long_name("ocean base temperature").units("kelvin");
112 m_Toc_box0.metadata()["_FillValue"] = { 0.0 };
113
114 m_T_star.metadata(0).long_name("T_star field").units("degree_Celsius");
115 m_T_star.metadata()["_FillValue"] = { 0.0 };
116
117 m_overturning.metadata(0).long_name("cavity overturning").units("m^3 s^-1");
118 m_overturning.metadata()["_FillValue"] = { 0.0 };
119
121 .long_name("PICO sub-shelf melt rate")
122 .units("m s^-1")
123 .output_units("m year^-1");
124 m_basal_melt_rate.metadata()["_FillValue"] = {0.0};
125
126 m_shelf_base_temperature->metadata()["_FillValue"] = {0.0};
127
128 m_n_boxes = static_cast<int>(m_config->get_number("ocean.pico.number_of_boxes"));
129}
130
131void Pico::init_impl(const Geometry &geometry) {
132 (void) geometry;
133
134 m_log->message(2, "* Initializing the Potsdam Ice-shelf Cavity mOdel for the ocean ...\n");
135
136 ForcingOptions opt(*m_grid->ctx(), "ocean.pico");
137
138 m_theta_ocean->init(opt.filename, opt.periodic);
139 m_salinity_ocean->init(opt.filename, opt.periodic);
140
141 // This initializes the basin_mask
143
144 // FIXME: m_n_basins is a misnomer
145 m_n_basins = static_cast<int>(max(m_geometry.basin_mask())) + 1;
146
147 m_log->message(4, "PICO basin min=%f, max=%f\n",
148 min(m_geometry.basin_mask()),
149 max(m_geometry.basin_mask()));
150
151 PicoPhysics physics(*m_config);
152
153 m_log->message(2, " -Using %d drainage basins and values: \n"
154 " gamma_T= %.2e, overturning_coeff = %.2e... \n",
155 m_n_basins - 1, physics.gamma_T(), physics.overturning_coeff());
156
157 m_log->message(2, " -Depth of continental shelf for computation of temperature and salinity input\n"
158 " is set for whole domain to continental_shelf_depth=%.0f meter\n",
159 physics.continental_shelf_depth());
160
161 // read time-independent data right away:
162 if (m_theta_ocean->buffer_size() == 1 and m_salinity_ocean->buffer_size() == 1) {
163 m_theta_ocean->update(time().current(), 0.0);
164 m_salinity_ocean->update(time().current(), 0.0);
165 }
166
167 double
168 ice_density = m_config->get_number("constants.ice.density"),
169 water_density = m_config->get_number("constants.sea_water.density"),
170 g = m_config->get_number("constants.standard_gravity");
171
172 compute_average_water_column_pressure(geometry, ice_density, water_density, g,
174}
175
176std::set<VariableMetadata> Pico::state_impl() const {
177 auto variables =
179
180 return pism::combine(variables, OceanModel::state_impl());
181}
182
183void Pico::write_state_impl(const OutputFile &output) const {
184
185 m_geometry.basin_mask().write(output);
186 m_Soc_box0.write(output);
187 m_Toc_box0.write(output);
188 m_overturning.write(output);
189
191}
192
193/*!
194* Extend basal melt rates to grounded and ocean neighbors for consitency with subgl_melt.
195* Note that melt rates are then simply interpolated into partially floating cells, they
196* are not included in the calculations of PICO.
197*/
198static void extend_basal_melt_rates(const array::CellType1 &cell_type,
199 array::Scalar1 &basal_melt_rate) {
200
201 auto grid = basal_melt_rate.grid();
202
203 // update ghosts of the basal melt rate so that we can use basal_melt_rate.box(i,j)
204 // below
205 basal_melt_rate.update_ghosts();
206
207 array::AccessScope list{&cell_type, &basal_melt_rate};
208
209 for (auto p : grid->points()) {
210
211 const int i = p.i(), j = p.j();
212
213 auto M = cell_type.box(i, j);
214
215 bool potential_partially_filled_cell =
216 ((M.c == MASK_GROUNDED or M.c == MASK_ICE_FREE_OCEAN) and
217 (M.w == MASK_FLOATING or M.e == MASK_FLOATING or
218 M.s == MASK_FLOATING or M.n == MASK_FLOATING or
219 M.sw == MASK_FLOATING or M.nw == MASK_FLOATING or
220 M.se == MASK_FLOATING or M.ne == MASK_FLOATING));
221
222 if (potential_partially_filled_cell) {
223 auto BMR = basal_melt_rate.box(i, j);
224
225 int N = 0;
226 double melt_sum = 0.0;
227
228 melt_sum += M.nw == MASK_FLOATING ? (++N, BMR.nw) : 0.0;
229 melt_sum += M.n == MASK_FLOATING ? (++N, BMR.n) : 0.0;
230 melt_sum += M.ne == MASK_FLOATING ? (++N, BMR.ne) : 0.0;
231 melt_sum += M.e == MASK_FLOATING ? (++N, BMR.e) : 0.0;
232 melt_sum += M.se == MASK_FLOATING ? (++N, BMR.se) : 0.0;
233 melt_sum += M.s == MASK_FLOATING ? (++N, BMR.s) : 0.0;
234 melt_sum += M.sw == MASK_FLOATING ? (++N, BMR.sw) : 0.0;
235 melt_sum += M.w == MASK_FLOATING ? (++N, BMR.w) : 0.0;
236
237 if (N != 0) { // If there are floating neigbors, return average melt rates
238 basal_melt_rate(i, j) = melt_sum / N;
239 }
240 }
241 } // end of the loop over grid points
242}
243
244void Pico::update_impl(const Inputs &inputs, double t, double dt) {
245
246 m_theta_ocean->update(t, dt);
247 m_salinity_ocean->update(t, dt);
248
249 m_theta_ocean->average(t, dt);
250 m_salinity_ocean->average(t, dt);
251
252 // set values that will be used outside of floating ice areas
253 {
254 double T_fill_value = m_config->get_number("constants.fresh_water.melting_point_temperature");
255 double Toc_fill_value = m_Toc.metadata().get_number("_FillValue");
256 double Soc_fill_value = m_Soc.metadata().get_number("_FillValue");
257 double M_fill_value = m_basal_melt_rate.metadata().get_number("_FillValue");
258 double O_fill_value = m_overturning.metadata().get_number("_FillValue");
259
260 m_shelf_base_temperature->set(T_fill_value);
261 m_basal_melt_rate.set(M_fill_value);
262 m_Toc.set(Toc_fill_value);
263 m_Soc.set(Soc_fill_value);
264 m_overturning.set(O_fill_value);
265 m_T_star.set(Toc_fill_value);
266 }
267
268 PicoPhysics physics(*m_config);
269
270 const array::Scalar &ice_thickness = inputs.geometry->ice_thickness;
271 const auto &cell_type = inputs.geometry->cell_type;
272 const array::Scalar &bed_elevation = inputs.geometry->bed_elevation;
273
274 // Geometric part of PICO
275 m_geometry.update(bed_elevation, cell_type);
276
277 // FIXME: m_n_shelves is not really the number of shelves.
278 m_n_shelves = static_cast<int>(max(m_geometry.ice_shelf_mask())) + 1;
279
280 // Physical part of PICO
281 {
282
283 // prepare ocean input temperature and salinity
284 {
285 std::vector<double> basin_temperature(m_n_basins);
286 std::vector<double> basin_salinity(m_n_basins);
287
293 basin_temperature,
294 basin_salinity); // per basin
295
297 ice_thickness,
298 cell_type,
301 basin_temperature,
302 basin_salinity,
304 m_Soc_box0); // per shelf
305 }
306
307 // Use the Beckmann-Goosse parameterization to set reasonable values throughout the
308 // domain.
309 beckmann_goosse(physics,
310 ice_thickness, // input
311 m_geometry.ice_shelf_mask(), // input
312 cell_type, // input
313 m_Toc_box0, // input
314 m_Soc_box0, // input
317 m_Toc,
318 m_Soc);
319
320 // In ice shelves, replace Beckmann-Goosse values using the Olbers and Hellmer model.
321 process_box1(physics,
322 ice_thickness, // input
323 m_geometry.ice_shelf_mask(), // input
324 m_geometry.box_mask(), // input
325 m_Toc_box0, // input
326 m_Soc_box0, // input
329 m_T_star,
330 m_Toc,
331 m_Soc,
333
334 process_other_boxes(physics,
335 ice_thickness, // input
336 m_geometry.ice_shelf_mask(), // input
337 m_geometry.box_mask(), // input
340 m_T_star,
341 m_Toc,
342 m_Soc);
343 }
344
346
348 m_shelf_base_mass_flux->scale(physics.ice_density());
349
350 double
351 ice_density = m_config->get_number("constants.ice.density"),
352 water_density = m_config->get_number("constants.sea_water.density"),
353 g = m_config->get_number("constants.standard_gravity");
354
355 compute_average_water_column_pressure(*inputs.geometry, ice_density, water_density, g,
357}
358
359
361 (void) t;
362
363 return MaxTimestep("ocean pico");
364}
365
366
367//! Compute temperature and salinity input from ocean data by averaging.
368
369//! We average the ocean data over the continental shelf reagion for each basin.
370//! We use dummy ocean data if no such average can be calculated.
371
372
374 const array::Scalar &basin_mask,
375 const array::Scalar &continental_shelf_mask,
376 const array::Scalar &salinity_ocean,
377 const array::Scalar &theta_ocean,
378 std::vector<double> &temperature,
379 std::vector<double> &salinity) const {
380 std::vector<int> count(m_n_basins, 0);
381 // additional vectors to allreduce efficiently with IntelMPI
382 std::vector<int> countr(m_n_basins, 0);
383 std::vector<double> salinityr(m_n_basins);
384 std::vector<double> temperaturer(m_n_basins);
385
386 temperature.resize(m_n_basins);
387 salinity.resize(m_n_basins);
388 for (int basin_id = 0; basin_id < m_n_basins; basin_id++) {
389 temperature[basin_id] = 0.0;
390 salinity[basin_id] = 0.0;
391 }
392
393 array::AccessScope list{ &theta_ocean, &salinity_ocean, &basin_mask, &continental_shelf_mask };
394
395 // compute the sum for each basin for region that intersects with the continental shelf
396 // area and is not covered by an ice shelf. (continental shelf mask excludes ice shelf
397 // areas)
398 for (auto p : m_grid->points()) {
399 const int i = p.i(), j = p.j();
400
401 if (continental_shelf_mask.as_int(i, j) == 2) {
402 int basin_id = basin_mask.as_int(i, j);
403
404 count[basin_id] += 1;
405 salinity[basin_id] += salinity_ocean(i, j);
406 temperature[basin_id] += theta_ocean(i, j);
407 }
408 }
409
410 // Divide by number of grid cells if more than zero cells belong to the basin. if no
411 // ocean_contshelf_mask values intersect with the basin, count is zero. In such case,
412 // use dummy temperature and salinity. This could happen, for example, if the ice shelf
413 // front advances beyond the continental shelf break.
414 GlobalSum(m_grid->com, count.data(), countr.data(), m_n_basins);
415 GlobalSum(m_grid->com, salinity.data(), salinityr.data(), m_n_basins);
416 GlobalSum(m_grid->com, temperature.data(), temperaturer.data(), m_n_basins);
417
418 // copy values
419 count = countr;
420 salinity = salinityr;
421 temperature = temperaturer;
422
423 // "dummy" basin
424 {
425 temperature[0] = physics.T_dummy();
426 salinity[0] = physics.S_dummy();
427 }
428
429 for (int basin_id = 1; basin_id < m_n_basins; basin_id++) {
430
431 if (count[basin_id] != 0) {
432 salinity[basin_id] /= count[basin_id];
433 temperature[basin_id] /= count[basin_id];
434
435 m_log->message(5, " %d: temp =%.3f, salinity=%.3f\n", basin_id, temperature[basin_id], salinity[basin_id]);
436 } else {
437 m_log->message(
438 2,
439 "PICO WARNING: basin %d contains no cells with ocean data on continental shelf\n"
440 " (no values with ocean_contshelf_mask=2).\n"
441 " Using default temperature (%.3f K) and salinity (%.3f g/kg)\n"
442 " since mean salinity and temperature cannot be computed.\n"
443 " This may bias the basal melt rate estimate.\n"
444 " Please check your input data.\n",
445 basin_id, physics.T_dummy(), physics.S_dummy());
446
447 temperature[basin_id] = physics.T_dummy();
448 salinity[basin_id] = physics.S_dummy();
449 }
450 }
451}
452
453//! Set ocean ocean input from box 0 as boundary condition for box 1.
454
455//! Set ocean temperature and salinity (Toc_box0, Soc_box0)
456//! from box 0 (in front of the ice shelf) as inputs for
457//! box 1, which is the ocean box adjacent to the grounding line.
458//!
459//! We enforce that Toc_box0 is always at least the local pressure melting point.
461 const array::Scalar &ice_thickness,
462 const array::CellType1 &mask,
463 const array::Scalar &basin_mask,
464 const array::Scalar &shelf_mask,
465 const std::vector<double> &basin_temperature,
466 const std::vector<double> &basin_salinity,
467 array::Scalar &Toc_box0,
468 array::Scalar &Soc_box0) const {
469
470 array::AccessScope list{ &ice_thickness, &basin_mask, &Soc_box0, &Toc_box0, &mask, &shelf_mask };
471
472 std::vector<int> n_shelf_cells_per_basin(m_n_shelves * m_n_basins, 0);
473 std::vector<int> n_shelf_cells(m_n_shelves, 0);
474 std::vector<int> cfs_in_basins_per_shelf(m_n_shelves * m_n_basins, 0);
475 // additional vectors to allreduce efficiently with IntelMPI
476 std::vector<int> n_shelf_cells_per_basinr(m_n_shelves * m_n_basins, 0);
477 std::vector<int> n_shelf_cellsr(m_n_shelves, 0);
478 std::vector<int> cfs_in_basins_per_shelfr(m_n_shelves * m_n_basins, 0);
479
480 // 1) count the number of cells in each shelf
481 // 2) count the number of cells in the intersection of each shelf with all the basins
482 {
483 for (auto p : m_grid->points()) {
484 const int i = p.i(), j = p.j();
485 int s = shelf_mask.as_int(i, j);
486 int b = basin_mask.as_int(i, j);
487 n_shelf_cells_per_basin[s*m_n_basins+b]++;
488 n_shelf_cells[s]++;
489
490 // find all basins b, in which the ice shelf s has a calving front with potential ocean water intrusion
491 if (mask.as_int(i, j) == MASK_FLOATING) {
492 auto M = mask.star(i, j);
493 if (M.n == MASK_ICE_FREE_OCEAN or
494 M.e == MASK_ICE_FREE_OCEAN or
495 M.s == MASK_ICE_FREE_OCEAN or
496 M.w == MASK_ICE_FREE_OCEAN) {
497 if (cfs_in_basins_per_shelf[s * m_n_basins + b] != b) {
498 cfs_in_basins_per_shelf[s * m_n_basins + b] = b;
499 }
500 }
501 }
502 }
503
504 GlobalSum(m_grid->com, n_shelf_cells.data(),
505 n_shelf_cellsr.data(), m_n_shelves);
506 GlobalSum(m_grid->com, n_shelf_cells_per_basin.data(),
507 n_shelf_cells_per_basinr.data(), m_n_shelves*m_n_basins);
508 GlobalSum(m_grid->com, cfs_in_basins_per_shelf.data(),
509 cfs_in_basins_per_shelfr.data(), m_n_shelves*m_n_basins);
510 // copy data
511 n_shelf_cells = n_shelf_cellsr;
512 n_shelf_cells_per_basin = n_shelf_cells_per_basinr;
513 cfs_in_basins_per_shelf = cfs_in_basins_per_shelfr;
514
515 for (int s = 0; s < m_n_shelves; s++) {
516 for (int b = 0; b < m_n_basins; b++) {
517 int sb = s * m_n_basins + b;
518 // remove ice shelf parts from the count that do not have a calving front in that basin
519 if (n_shelf_cells_per_basin[sb] > 0 and cfs_in_basins_per_shelf[sb] == 0) {
520 n_shelf_cells[s] -= n_shelf_cells_per_basin[sb];
521 n_shelf_cells_per_basin[sb] = 0;
522 }
523 }
524 }
525 }
526
527 // now set potential temperature and salinity box 0:
528
529 int low_temperature_counter = 0;
530 for (auto p : m_grid->points()) {
531 const int i = p.i(), j = p.j();
532
533 // make sure all temperatures are zero at the beginning of each time step
534 Toc_box0(i, j) = 0.0; // in K
535 Soc_box0(i, j) = 0.0; // in psu
536
537 int s = shelf_mask.as_int(i, j);
538
539 if (mask.as_int(i, j) == MASK_FLOATING and s > 0) {
540 // note: shelf_mask = 0 in lakes
541
542 assert(n_shelf_cells[s] > 0);
543 double N = std::max(n_shelf_cells[s], 1); // protect from division by zero
544
545 // weighted input depending on the number of shelf cells in each basin
546 for (int b = 1; b < m_n_basins; b++) { //Note: b=0 yields nan
547 int sb = s * m_n_basins + b;
548 Toc_box0(i, j) += basin_temperature[b] * n_shelf_cells_per_basin[sb] / N;
549 Soc_box0(i, j) += basin_salinity[b] * n_shelf_cells_per_basin[sb] / N;
550 }
551
552 double theta_pm = physics.theta_pm(Soc_box0(i, j), physics.pressure(ice_thickness(i, j)));
553
554 // temperature input for grounding line box should not be below pressure melting point
555 if (Toc_box0(i, j) < theta_pm) {
556 const double eps = 0.001;
557 // Setting Toc_box0 a little higher than theta_pm ensures that later equations are
558 // well solvable.
559 Toc_box0(i, j) = theta_pm + eps;
560 low_temperature_counter += 1;
561 }
562 }
563 }
564
565 low_temperature_counter = GlobalSum(m_grid->com, low_temperature_counter);
566 if (low_temperature_counter > 0) {
567 m_log->message(
568 2,
569 "PICO WARNING: temperature has been below pressure melting temperature in %d cases,\n"
570 " setting it to pressure melting temperature\n",
571 low_temperature_counter);
572 }
573}
574
575/*!
576 * Use the simpler parameterization due to [@ref BeckmannGoosse2003] to set default
577 * sub-shelf temperature and melt rate values.
578 *
579 * At grid points containing floating ice not connected to the ocean, set the basal melt
580 * rate to zero and set basal temperature to the pressure melting point.
581 */
583 const array::Scalar &ice_thickness,
584 const array::Scalar &shelf_mask,
585 const array::CellType &cell_type,
586 const array::Scalar &Toc_box0,
587 const array::Scalar &Soc_box0,
588 array::Scalar &basal_melt_rate,
589 array::Scalar &basal_temperature,
590 array::Scalar &Toc,
591 array::Scalar &Soc) {
592
593 const double T0 = m_config->get_number("constants.fresh_water.melting_point_temperature"),
594 beta_CC = m_config->get_number("constants.ice.beta_Clausius_Clapeyron"),
595 g = m_config->get_number("constants.standard_gravity"),
596 ice_density = m_config->get_number("constants.ice.density");
597
598 array::AccessScope list{ &ice_thickness, &cell_type, &shelf_mask, &Toc_box0, &Soc_box0,
599 &Toc, &Soc, &basal_melt_rate, &basal_temperature };
600
601 for (auto p : m_grid->points()) {
602 const int i = p.i(), j = p.j();
603
604 if (cell_type.floating_ice(i, j)) {
605 if (shelf_mask.as_int(i, j) > 0) {
606 double pressure = physics.pressure(ice_thickness(i, j));
607
608 basal_melt_rate(i, j) =
609 physics.melt_rate_beckmann_goosse(physics.theta_pm(Soc_box0(i, j), pressure), Toc_box0(i, j));
610 basal_temperature(i, j) = physics.T_pm(Soc_box0(i, j), pressure);
611
612 // diagnostic outputs
613 Toc(i, j) = Toc_box0(i, j); // in kelvin
614 Soc(i, j) = Soc_box0(i, j); // in psu
615 } else {
616 // Floating ice cells not connected to the ocean.
617 const double pressure = ice_density * g * ice_thickness(i, j); // FIXME issue #15
618
619 basal_temperature(i, j) = T0 - beta_CC * pressure;
620 basal_melt_rate(i, j) = 0.0;
621 }
622 }
623 }
624}
625
626
627void Pico::process_box1(const PicoPhysics &physics,
628 const array::Scalar &ice_thickness,
629 const array::Scalar &shelf_mask,
630 const array::Scalar &box_mask,
631 const array::Scalar &Toc_box0,
632 const array::Scalar &Soc_box0,
633 array::Scalar &basal_melt_rate,
634 array::Scalar &basal_temperature,
635 array::Scalar &T_star,
636 array::Scalar &Toc,
637 array::Scalar &Soc,
638 array::Scalar &overturning) {
639
640 std::vector<double> box1_area(m_n_shelves);
641
642 compute_box_area(1, shelf_mask, box_mask, box1_area);
643
644 array::AccessScope list{ &ice_thickness, &shelf_mask, &box_mask, &T_star, &Toc_box0, &Toc,
645 &Soc_box0, &Soc, &overturning, &basal_melt_rate, &basal_temperature };
646
647 int n_Toc_failures = 0;
648
649 // basal melt rate, ambient temperature and salinity and overturning calculation
650 // for each box1 grid cell.
651 for (auto p : m_grid->points()) {
652 const int i = p.i(), j = p.j();
653
654 int shelf_id = shelf_mask.as_int(i, j);
655
656 if (box_mask.as_int(i, j) == 1 and shelf_id > 0) {
657
658 const double pressure = physics.pressure(ice_thickness(i, j));
659
660 T_star(i, j) = physics.T_star(Soc_box0(i, j), Toc_box0(i, j), pressure);
661
662 auto Toc_box1 = physics.Toc_box1(box1_area[shelf_id], T_star(i, j), Soc_box0(i, j), Toc_box0(i, j));
663
664 // This can only happen if T_star > 0.25*p_coeff, in particular T_star > 0
665 // which can only happen for values of Toc_box0 close to the local pressure melting point
666 if (Toc_box1.failed) {
667 m_log->message(5,
668 "PICO WARNING: negative square root argument at %d, %d\n"
669 " probably because of positive T_star=%f \n"
670 " Not aborting, but setting square root to 0... \n",
671 i, j, T_star(i, j));
672
673 n_Toc_failures += 1;
674 }
675
676 Toc(i, j) = Toc_box1.value;
677 Soc(i, j) = physics.Soc_box1(Toc_box0(i, j), Soc_box0(i, j), Toc(i, j)); // in psu
678
679 overturning(i, j) = physics.overturning(Soc_box0(i, j), Soc(i, j), Toc_box0(i, j), Toc(i, j));
680
681 // main outputs
682 basal_melt_rate(i, j) = physics.melt_rate(physics.theta_pm(Soc(i, j), pressure), Toc(i, j));
683 basal_temperature(i, j) = physics.T_pm(Soc(i, j), pressure);
684 }
685 }
686
687 n_Toc_failures = GlobalSum(m_grid->com, n_Toc_failures);
688 if (n_Toc_failures > 0) {
689 m_log->message(2,
690 "PICO WARNING: square-root argument for temperature calculation\n"
691 " has been negative in %d cases.\n",
692 n_Toc_failures);
693 }
694}
695
697 const array::Scalar &ice_thickness,
698 const array::Scalar &shelf_mask,
699 const array::Scalar &box_mask,
700 array::Scalar &basal_melt_rate,
701 array::Scalar &basal_temperature,
702 array::Scalar &T_star,
703 array::Scalar &Toc,
704 array::Scalar &Soc) const {
705
706 std::vector<double> overturning(m_n_shelves, 0.0);
707 std::vector<double> salinity(m_n_shelves, 0.0);
708 std::vector<double> temperature(m_n_shelves, 0.0);
709
710 // get average overturning from box 1 that is used as input later
711 compute_box_average(1, m_overturning, shelf_mask, box_mask, overturning);
712
713 std::vector<bool> use_beckmann_goosse(m_n_shelves);
714
715 array::AccessScope list{ &ice_thickness, &shelf_mask, &box_mask, &T_star, &Toc,
716 &Soc, &basal_melt_rate, &basal_temperature };
717
718 // Iterate over all boxes i for i > 1
719 for (int box = 2; box <= m_n_boxes; ++box) {
720
721 compute_box_average(box - 1, Toc, shelf_mask, box_mask, temperature);
722 compute_box_average(box - 1, Soc, shelf_mask, box_mask, salinity);
723
724 // find all the shelves where we should fall back to the Beckmann-Goosse
725 // parameterization
726 for (int s = 1; s < m_n_shelves; ++s) {
727 use_beckmann_goosse[s] = (salinity[s] == 0.0 or
728 temperature[s] == 0.0 or
729 overturning[s] == 0.0);
730 }
731
732 std::vector<double> box_area;
733 compute_box_area(box, shelf_mask, box_mask, box_area);
734
735 int n_beckmann_goosse_cells = 0;
736
737 for (auto p : m_grid->points()) {
738 const int i = p.i(), j = p.j();
739
740 int shelf_id = shelf_mask.as_int(i, j);
741
742 if (box_mask.as_int(i, j) == box and shelf_id > 0) {
743
744 if (use_beckmann_goosse[shelf_id]) {
745 n_beckmann_goosse_cells += 1;
746 continue;
747 }
748
749 // Get the input from previous box
750 const double
751 S_previous = salinity[shelf_id],
752 T_previous = temperature[shelf_id],
753 overturning_box1 = overturning[shelf_id];
754
755 {
756 double pressure = physics.pressure(ice_thickness(i, j));
757
758 // diagnostic outputs
759 T_star(i, j) = physics.T_star(S_previous, T_previous, pressure);
760 Toc(i, j) = physics.Toc(box_area[shelf_id], T_previous, T_star(i, j), overturning_box1, S_previous);
761 Soc(i, j) = physics.Soc(S_previous, T_previous, Toc(i, j));
762
763 // main outputs: basal melt rate and temperature
764 basal_melt_rate(i, j) = physics.melt_rate(physics.theta_pm(Soc(i, j), pressure), Toc(i, j));
765 basal_temperature(i, j) = physics.T_pm(Soc(i, j), pressure);
766 }
767 }
768 } // loop over grid points
769
770 n_beckmann_goosse_cells = GlobalSum(m_grid->com, n_beckmann_goosse_cells);
771 if (n_beckmann_goosse_cells > 0) {
772 m_log->message(
773 2,
774 "PICO WARNING: [box %d]: switched to the Beckmann Goosse (2003) model at %d locations\n"
775 " (no boundary data from the previous box)\n",
776 box, n_beckmann_goosse_cells);
777 }
778
779 } // loop over boxes
780}
781
782
783// Return spatially-variable diagnostics
785
786 DiagnosticList result = {
787 { "basins", Diagnostic::wrap(m_geometry.basin_mask()) },
788 { "pico_overturning", Diagnostic::wrap(m_overturning) },
789 { "pico_salinity_box0", Diagnostic::wrap(m_Soc_box0) },
790 { "pico_temperature_box0", Diagnostic::wrap(m_Toc_box0) },
791 { "pico_box_mask", Diagnostic::wrap(m_geometry.box_mask()) },
792 { "pico_shelf_mask", Diagnostic::wrap(m_geometry.ice_shelf_mask()) },
793 { "pico_ice_rise_mask", Diagnostic::wrap(m_geometry.ice_rise_mask()) },
794 { "pico_basal_melt_rate", Diagnostic::wrap(m_basal_melt_rate) },
795 { "pico_contshelf_mask", Diagnostic::wrap(m_geometry.continental_shelf_mask()) },
796 { "pico_salinity", Diagnostic::wrap(m_Soc) },
797 { "pico_temperature", Diagnostic::wrap(m_Toc) },
798 { "pico_T_star", Diagnostic::wrap(m_T_star) },
799 { "pico_basal_temperature", Diagnostic::wrap(*m_shelf_base_temperature) },
800 };
801
803}
804
805/*!
806 * For each shelf, compute average of a given field over the box with id `box_id`.
807 *
808 * This method is used to get inputs from a previous box for the next one.
809 */
811 const array::Scalar &field,
812 const array::Scalar &shelf_mask,
813 const array::Scalar &box_mask,
814 std::vector<double> &result) const {
815
816 array::AccessScope list{ &field, &shelf_mask, &box_mask };
817
818 // fill results with zeros
819 result.resize(m_n_shelves);
820 for (int s = 0; s < m_n_shelves; ++s) {
821 result[s] = 0.0;
822 }
823
824 // compute the sum of field in each shelf's box box_id
825 std::vector<int> n_cells(m_n_shelves);
826 {
827 std::vector<int> n_cells_per_box(m_n_shelves, 0);
828 for (auto p : m_grid->points()) {
829 const int i = p.i(), j = p.j();
830
831 int shelf_id = shelf_mask.as_int(i, j);
832
833 if (box_mask.as_int(i, j) == box_id) {
834 n_cells_per_box[shelf_id] += 1;
835 result[shelf_id] += field(i, j);
836 }
837 }
838 GlobalSum(m_grid->com, n_cells_per_box.data(), n_cells.data(), m_n_shelves);
839 }
840
841 {
842 std::vector<double> tmp(m_n_shelves);
843 GlobalSum(m_grid->com, result.data(), tmp.data(), m_n_shelves);
844 // copy data
845 result = tmp;
846 }
847
848 for (int s = 0; s < m_n_shelves; ++s) {
849 if (n_cells[s] > 0) {
850 result[s] /= static_cast<double>(n_cells[s]);
851 }
852 }
853}
854
855/*!
856 * For all shelves compute areas of boxes with id `box_id`.
857 *
858 * @param[in] box_mask box index mask
859 * @param[out] result resulting box areas.
860 *
861 * Note: shelf and box indexes start from 1.
862 */
864 const array::Scalar &shelf_mask,
865 const array::Scalar &box_mask,
866 std::vector<double> &result) const {
867 result.resize(m_n_shelves);
868 array::AccessScope list{ &shelf_mask, &box_mask };
869
870 auto cell_area = m_grid->cell_area();
871
872 for (auto p : m_grid->points()) {
873 const int i = p.i(), j = p.j();
874
875 int shelf_id = shelf_mask.as_int(i, j);
876
877 if (shelf_id > 0 and box_mask.as_int(i, j) == box_id) {
878 result[shelf_id] += cell_area;
879 }
880 }
881
882 // compute GlobalSum from index 1 to index m_n_shelves-1
883 std::vector<double> result1(m_n_shelves);
884 GlobalSum(m_grid->com, &result[1], &result1[1], m_n_shelves-1);
885 // copy data
886 for (int i = 1; i < m_n_shelves; i++) {
887 result[i] = result1[i];
888 }
889}
890
891} // end of namespace ocean
892} // end of namespace pism
const Time & time() const
Definition Component.cc:111
std::shared_ptr< const Config > m_config
configuration database used by this component
Definition Component.hh:160
const std::shared_ptr< const Grid > m_grid
grid used by this component
Definition Component.hh:158
std::shared_ptr< const Logger > m_log
logger (for easy access)
Definition Component.hh:164
static Ptr wrap(const T &input)
High-level PISM I/O class.
Definition File.hh:57
array::CellType2 cell_type
Definition Geometry.hh:55
array::Scalar2 ice_thickness
Definition Geometry.hh:51
array::Scalar2 bed_elevation
Definition Geometry.hh:47
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
double get_number(const std::string &name) const
Get a single-valued scalar attribute.
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
stencils::Star< T > star(int i, int j) const
Definition Array2D.hh:107
stencils::Box< T > box(int i, int j) const
Definition Array2D.hh:121
void write(const OutputFile &file) const
Definition Array.cc:859
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
bool floating_ice(int i, int j) const
Definition CellType.hh:50
"Cell type" mask. Adds convenience methods to array::Scalar.
Definition CellType.hh:30
int as_int(int i, int j) const
Definition Scalar.hh:45
std::shared_ptr< array::Scalar > m_shelf_base_mass_flux
std::shared_ptr< array::Scalar > m_shelf_base_temperature
std::shared_ptr< array::Scalar > m_water_column_pressure
Definition OceanModel.hh:78
virtual void write_state_impl(const OutputFile &output) const
The default (empty implementation).
virtual DiagnosticList spatial_diagnostics_impl() const
virtual std::set< VariableMetadata > state_impl() const
A very rudimentary PISM ocean model.
Definition OceanModel.hh:38
const array::Scalar & continental_shelf_mask() const
const array::Scalar & ice_shelf_mask() const
const array::Scalar & ice_rise_mask() const
void update(const array::Scalar &bed_elevation, const array::CellType1 &cell_type)
const array::Scalar & basin_mask() const
const array::Scalar & box_mask() const
double pressure(double ice_thickness) const
double overturning_coeff() const
double ice_density() const
TocBox1 Toc_box1(double area, double T_star, double Soc_box0, double Toc_box0) const
double Toc(double box_area, double temperature, double T_star, double overturning, double salinity) const
double continental_shelf_depth() const
double melt_rate(double pm_point, double Toc) const
equation 8 in the PICO paper.
double overturning(double Soc_box0, double Soc, double Toc_box0, double Toc) const
equation 3 in the PICO paper. See also equation 4.
double theta_pm(double salinity, double pressure) const
double Soc_box1(double Toc_box0, double Soc_box0, double Toc) const
double T_pm(double salinity, double pressure) const
double melt_rate_beckmann_goosse(double pot_pm_point, double Toc) const
Beckmann & Goosse meltrate.
double T_star(double salinity, double temperature, double pressure) const
See equation A6 and lines before in PICO paper.
double Soc(double salinity, double temperature, double Toc) const
void set_ocean_input_fields(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::CellType1 &mask, const array::Scalar &basin_mask, const array::Scalar &shelf_mask, const std::vector< double > &basin_temperature, const std::vector< double > &basin_salinity, array::Scalar &Toc_box0, array::Scalar &Soc_box0) const
Set ocean ocean input from box 0 as boundary condition for box 1.
Definition Pico.cc:460
array::Scalar m_Toc_box0
Definition Pico.hh:57
void write_state_impl(const OutputFile &output) const
The default (empty implementation).
Definition Pico.cc:183
std::shared_ptr< array::Forcing > m_salinity_ocean
Definition Pico.hh:63
array::Scalar m_Soc
Definition Pico.hh:56
std::map< std::string, Diagnostic::Ptr > spatial_diagnostics_impl() const
Definition Pico.cc:784
MaxTimestep max_timestep_impl(double t) const
Definition Pico.cc:360
void process_box1(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::Scalar &shelf_mask, const array::Scalar &box_mask, const array::Scalar &Toc_box0, const array::Scalar &Soc_box0, array::Scalar &basal_melt_rate, array::Scalar &basal_temperature, array::Scalar &T_star, array::Scalar &Toc, array::Scalar &Soc, array::Scalar &overturning)
Definition Pico.cc:627
array::Scalar m_Toc
Definition Pico.hh:57
void compute_box_area(int box_id, const array::Scalar &shelf_mask, const array::Scalar &box_mask, std::vector< double > &result) const
Definition Pico.cc:863
Pico(std::shared_ptr< const Grid > g)
Definition Pico.cc:52
array::Scalar1 m_basal_melt_rate
Definition Pico.hh:59
void compute_ocean_input_per_basin(const PicoPhysics &physics, const array::Scalar &basin_mask, const array::Scalar &continental_shelf_mask, const array::Scalar &salinity_ocean, const array::Scalar &theta_ocean, std::vector< double > &temperature, std::vector< double > &salinity) const
Compute temperature and salinity input from ocean data by averaging.
Definition Pico.cc:373
array::Scalar m_Soc_box0
Definition Pico.hh:56
void compute_box_average(int box_id, const array::Scalar &field, const array::Scalar &shelf_mask, const array::Scalar &box_mask, std::vector< double > &result) const
Definition Pico.cc:810
std::shared_ptr< array::Forcing > m_theta_ocean
Definition Pico.hh:63
std::set< VariableMetadata > state_impl() const
Definition Pico.cc:176
void process_other_boxes(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::Scalar &shelf_mask, const array::Scalar &box_mask, array::Scalar &basal_melt_rate, array::Scalar &basal_temperature, array::Scalar &T_star, array::Scalar &Toc, array::Scalar &Soc) const
Definition Pico.cc:696
PicoGeometry m_geometry
Definition Pico.hh:61
void init_impl(const Geometry &geometry)
Definition Pico.cc:131
void update_impl(const Inputs &inputs, double t, double dt)
Definition Pico.cc:244
array::Scalar m_T_star
Definition Pico.hh:57
void beckmann_goosse(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::Scalar &shelf_mask, const array::CellType &cell_type, const array::Scalar &Toc_box0, const array::Scalar &Soc_box0, array::Scalar &basal_melt_rate, array::Scalar &basal_temperature, array::Scalar &Toc, array::Scalar &Soc)
Definition Pico.cc:582
array::Scalar m_overturning
Definition Pico.hh:58
static const double beta_CC
Definition exactTestO.c:23
std::set< VariableMetadata > metadata(std::initializer_list< const Array * > vecs)
Definition Array.cc:1244
@ PISM_NETCDF3
Definition IO_Flags.hh:58
@ PISM_READONLY
open an existing file for reading only
Definition IO_Flags.hh:69
bool ocean(int M)
An ocean cell (floating ice or ice-free).
Definition Mask.hh:40
static void extend_basal_melt_rates(const array::CellType1 &cell_type, array::Scalar1 &basal_melt_rate)
Definition Pico.cc:198
void compute_average_water_column_pressure(const Geometry &geometry, double ice_density, double water_density, double standard_gravity, array::Scalar &result)
static const double g
Definition exactTestP.cc:36
std::map< std::string, Diagnostic::Ptr > DiagnosticList
@ MASK_FLOATING
Definition Mask.hh:34
@ MASK_ICE_FREE_OCEAN
Definition Mask.hh:35
@ MASK_GROUNDED
Definition Mask.hh:33
T combine(const T &a, const T &b)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
std::string filename
Definition options.hh:33
const Geometry * geometry
Definition OceanModel.hh:34
int count
Definition test_cube.c:16