PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
Distributed.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2019, 2021, 2022, 2023, 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#include <algorithm> // std::min, std::max
20
21#include "Routing.hh"
22#include "pism/geometry/Geometry.hh"
23#include "pism/hydrology/Distributed.hh"
24#include "pism/util/array/CellType.hh"
25#include "pism/util/error_handling.hh"
26#include "pism/util/io/File.hh"
27#include "pism/util/pism_utilities.hh"
28#include "pism/util/Logger.hh"
29#include "pism/util/io/IO_Flags.hh"
30
31namespace pism {
32namespace hydrology {
33
34Distributed::Distributed(std::shared_ptr<const Grid> g)
35 : Routing(g), m_P(m_grid, "bwp"), m_Pnew(m_grid, "Pnew_internal") {
36
37 // additional variables beyond hydrology::Routing
38 m_P.metadata(0)
39 .long_name("pressure of transportable water in subglacial layer")
40 .units("Pa");
41 m_P.metadata()["valid_min"] = { 0.0 };
42
44 .long_name("new transportable subglacial water pressure during update")
45 .units("Pa");
46 m_Pnew.metadata()["valid_min"] = { 0.0 };
47}
48
50 m_log->message(2,
51 "* Initializing the distributed, linked-cavities subglacial hydrology model...\n");
52}
53
54void Distributed::restart_impl(const File &input_file, int record) {
55 Routing::restart_impl(input_file, record);
56
57 m_P.read(input_file, record);
58
59 regrid("Hydrology", m_P);
60}
61
62void Distributed::bootstrap_impl(const File &input_file, const array::Scalar &ice_thickness) {
63 Routing::bootstrap_impl(input_file, ice_thickness);
64
65 double bwp_default = m_config->get_number("bootstrapping.defaults.bwp");
66 m_P.regrid(input_file, io::Default(bwp_default));
67
68 regrid("Hydrology", m_P);
69
70 bool init_P_from_steady = m_config->get_flag("hydrology.distributed.init_p_from_steady");
71
72 if (init_P_from_steady) { // if so, just overwrite -i or -bootstrap value of P=bwp
73 m_log->message(2, " initializing P from P(W) formula which applies in steady state\n");
74
76
77 array::Scalar sliding_speed(m_grid, "velbase_mag");
78 sliding_speed.metadata(0).long_name("basal sliding speed").units("m s^-1");
79
80 std::string filename = m_config->get_string("hydrology.distributed.sliding_speed_file");
81
82 if (filename.empty()) {
84 "hydrology.distributed.sliding_speed_file is not set");
85 }
86
87 sliding_speed.regrid(filename, io::Default::Nil());
88
89 P_from_W_steady(m_W, m_Pover, sliding_speed,
90 m_P);
91 }
92}
93
95 const array::Scalar &W,
96 const array::Scalar &P) {
97 Routing::init_impl(W_till, W, P);
98
99 m_P.copy_from(P);
100}
101
102std::set<VariableMetadata> Distributed::state_impl() const {
104}
105
106void Distributed::write_state_impl(const OutputFile &output) const {
108 m_P.write(output);
109}
110
111std::map<std::string, TSDiagnostic::Ptr> Distributed::scalar_diagnostics_impl() const {
112 std::map<std::string, TSDiagnostic::Ptr> result = {
113 // FIXME: add mass-conservation time-series diagnostics
114 };
115 return result;
116}
117
118//! Copies the P state variable which is the modeled water pressure.
122
123//! Check bounds on P and fail with message if not satisfied. Optionally, enforces the
124//! upper bound instead of checking it.
125/*!
126 The bounds are \f$0 \le P \le P_o\f$ where \f$P_o\f$ is the overburden pressure.
127*/
129 const array::Scalar &P_o,
130 bool enforce_upper) {
131
132 array::AccessScope list{&P, &P_o};
133
134 ParallelSection loop(m_grid->com);
135 try {
136 for (auto p : m_grid->points()) {
137 const int i = p.i(), j = p.j();
138
139 if (P(i,j) < 0.0) {
141 "negative subglacial water pressure\n"
142 "P(%d, %d) = %.6f Pa",
143 i, j, P(i, j));
144 }
145
146 if (enforce_upper) {
147 P(i,j) = std::min(P(i,j), P_o(i,j));
148 } else if (P(i,j) > P_o(i,j) + 0.001) {
150 "subglacial water pressure P = %.16f Pa exceeds\n"
151 "overburden pressure Po = %.16f Pa at (i,j)=(%d,%d)",
152 P(i, j), P_o(i, j), i, j);
153 }
154 }
155 } catch (...) {
156 loop.failed();
157 }
158 loop.check();
159
160}
161
162
163//! Compute functional relationship P(W) which applies only in steady state.
164/*!
165 In steady state in this model, water pressure is determined by a balance of
166 cavitation (opening) caused by sliding and creep closure.
167
168 This will be used in initialization when P is otherwise unknown, and
169 in verification and/or reporting. It is not used during time-dependent
170 model runs. To be more complete, \f$P = P(W,P_o,|v_b|)\f$.
171*/
173 const array::Scalar &P_overburden,
174 const array::Scalar &sliding_speed,
175 array::Scalar &result) {
176
177 const double
178 ice_softness = m_config->get_number("flow_law.isothermal_Glen.ice_softness"),
179 creep_closure_coefficient = m_config->get_number("hydrology.creep_closure_coefficient"),
180 cavitation_opening_coefficient = m_config->get_number("hydrology.cavitation_opening_coefficient"),
181 Glen_exponent = m_config->get_number("stress_balance.sia.Glen_exponent"),
182 Wr = m_config->get_number("hydrology.roughness_scale");
183
184 const double CC = cavitation_opening_coefficient / (creep_closure_coefficient * ice_softness);
185
186 array::AccessScope list{&W, &P_overburden, &sliding_speed, &result};
187
188 for (auto p : m_grid->points()) {
189 const int i = p.i(), j = p.j();
190
191 double sb = pow(CC * sliding_speed(i, j), 1.0 / Glen_exponent);
192 if (W(i, j) == 0.0) {
193 // see P(W) formula in steady state; note P(W) is continuous (in steady
194 // state); these facts imply:
195 if (sb > 0.0) {
196 // no water + cavitation = underpressure
197 result(i, j) = 0.0;
198 } else {
199 // no water + no cavitation = creep repressurizes = overburden
200 result(i, j) = P_overburden(i, j);
201 }
202 } else {
203 double Wratio = std::max(0.0, Wr - W(i, j)) / W(i, j);
204 // in cases where steady state is actually possible this will come out positive, but
205 // otherwise we should get underpressure P=0, and that is what it yields
206 result(i, j) = std::max(0.0, P_overburden(i, j) - sb * pow(Wratio, 1.0 / Glen_exponent));
207 }
208 } // end of the loop over grid points
209}
210
211double Distributed::max_timestep_P_diff(double phi0, double dt_diff_w) const {
212 return 2.0 * phi0 * dt_diff_w;
213}
214
216 const array::CellType &cell_type,
217 const array::Scalar &sliding_speed,
218 const array::Scalar &surface_input_rate,
219 const array::Scalar &basal_melt_rate,
220 const array::Scalar &P_overburden,
221 const array::Scalar &Wtill,
222 const array::Scalar &Wtill_new,
223 const array::Scalar &P,
224 const array::Scalar1 &W,
225 const array::Staggered1 &Ws,
226 const array::Staggered1 &K,
227 const array::Staggered1 &Q,
228 array::Scalar &P_new) const {
229
230 const double
231 n = m_config->get_number("stress_balance.sia.Glen_exponent"),
232 A = m_config->get_number("flow_law.isothermal_Glen.ice_softness"),
233 c1 = m_config->get_number("hydrology.cavitation_opening_coefficient"),
234 c2 = m_config->get_number("hydrology.creep_closure_coefficient"),
235 Wr = m_config->get_number("hydrology.roughness_scale"),
236 phi0 = m_config->get_number("hydrology.regularizing_porosity");
237
238 // update Pnew from time step
239 const double
240 CC = (m_rg * dt) / phi0,
241 wux = 1.0 / (m_dx * m_dx),
242 wuy = 1.0 / (m_dy * m_dy);
243
244 array::AccessScope list{&P, &W, &Wtill, &Wtill_new, &sliding_speed, &Ws,
245 &K, &Q, &surface_input_rate, &basal_melt_rate,
246 &cell_type, &P_overburden, &P_new};
247
248 for (auto p : m_grid->points()) {
249 const int i = p.i(), j = p.j();
250
251 auto w = W.star(i, j);
252 double P_o = P_overburden(i, j);
253
254 if (cell_type.ice_free_land(i, j)) {
255 P_new(i, j) = 0.0;
256 } else if (cell_type.ocean(i, j)) {
257 P_new(i, j) = P_o;
258 } else if (w.c <= 0.0) {
259 P_new(i, j) = P_o;
260 } else {
261 auto q = Q.star(i, j);
262 auto k = K.star(i, j);
263 auto ws = Ws.star(i, j);
264
265 double
266 Open = c1 * sliding_speed(i, j) * std::max(0.0, Wr - w.c),
267 Close = c2 * A * pow(P_o - P(i, j), n) * w.c;
268
269 // compute the flux divergence the same way as in update_W()
270 const double divadflux = (q.e - q.w) / m_dx + (q.n - q.s) / m_dy;
271 const double
272 De = m_rg * k.e * ws.e,
273 Dw = m_rg * k.w * ws.w,
274 Dn = m_rg * k.n * ws.n,
275 Ds = m_rg * k.s * ws.s;
276
277 double diffW = (wux * (De * (w.e - w.c) - Dw * (w.c - w.w)) +
278 wuy * (Dn * (w.n - w.c) - Ds * (w.c - w.s)));
279
280 double divflux = -divadflux + diffW;
281
282 // pressure update equation
283 double Wtill_change = Wtill_new(i, j) - Wtill(i, j);
284 double total_input = surface_input_rate(i, j) + basal_melt_rate(i, j);
285 double ZZ = Close - Open + total_input - Wtill_change / dt;
286
287 P_new(i, j) = P(i, j) + CC * (divflux + ZZ);
288
289 // projection to enforce 0 <= P <= P_o
290 P_new(i, j) = clip(P_new(i, j), 0.0, P_o);
291 }
292 } // end of the loop over grid points
293}
294
295
296//! Update the model state variables W,P by running the subglacial hydrology model.
297/*!
298 Runs the hydrology model from time t to time t + dt. Here [t,dt]
299 is generally on the order of months to years. This hydrology model will take its
300 own shorter time steps, perhaps hours to weeks.
301*/
302void Distributed::update_impl(double t, double dt, const Inputs& inputs) {
303
305
306 double
307 ht = t,
308 hdt = 0.0;
309
310 const double
311 t_final = t + dt,
312 dt_max = m_config->get_number("hydrology.maximum_time_step", "seconds"),
313 phi0 = m_config->get_number("hydrology.regularizing_porosity"),
314 tillwat_max = m_config->get_number("hydrology.tillwat_max");
315
316 m_Qstag_average.set(0.0);
317
318 // make sure W,P have valid ghosts before starting hydrology steps
321
322 unsigned int step_counter = 0;
323 for (; ht < t_final; ht += hdt) {
324 step_counter++;
325
326#if (Pism_DEBUG==1)
327 double huge_number = 1e6;
328 check_bounds(m_W, huge_number);
329 check_bounds(m_Wtill, tillwat_max);
330#endif
331
332 // note that ice dynamics can change overburden pressure, so we can only check P
333 // bounds if thk has not changed; if thk could have just changed, such as in the first
334 // time through the current loop, we enforce them
335 bool enforce_upper = (step_counter == 1);
336 check_P_bounds(m_P, m_Pover, enforce_upper);
337
339 inputs.geometry->cell_type,
340 m_Wstag);
341
342 double maxKW = 0.0;
346 m_Kstag, maxKW);
347
351 m_Kstag,
352 inputs.no_model_mask,
353 m_Vstag);
354
355 // to get Q, W needs valid ghosts
357
359
360 {
361 const double
362 dt_cfl = max_timestep_W_cfl(),
363 dt_diff_w = max_timestep_W_diff(maxKW),
364 dt_diff_p = max_timestep_P_diff(phi0, dt_diff_w);
365
366 hdt = std::min(t_final - ht, dt_max);
367 hdt = std::min(hdt, dt_cfl);
368 hdt = std::min(hdt, dt_diff_w);
369 hdt = std::min(hdt, dt_diff_p);
370 }
371
372 m_log->message(3, " hydrology step %05d, dt = %f s\n", step_counter, hdt);
373
374 // update Wtillnew from Wtill and input_rate
375 update_Wtill(hdt,
376 m_Wtill,
379 m_Wtillnew);
380 // remove water in ice-free areas and account for changes
382 inputs.no_model_mask,
383 0.0, // do not limit maximum thickness
384 tillwat_max, // till water thickness under the ocean
390
391 update_P(hdt,
392 inputs.geometry->cell_type,
393 *inputs.ice_sliding_speed,
396 m_Pover,
399 m_W, m_Wstag,
401 m_Pnew);
402
403 // update Wnew from W, Wtill, Wtillnew, Wstag, Q, input_rate
404 update_W(hdt,
407 m_W, m_Wstag,
410 m_Wnew);
411 // remove water in ice-free areas and account for changes
413 inputs.no_model_mask,
414 0.0, // do not limit maximum thickness
415 0.0, // transportable water layer thickness under the ocean
416 m_Wnew,
421
422 // transfer new into old
426 } // end of the time-stepping loop
427
428 staggered_to_regular(inputs.geometry->cell_type, m_Qstag_average,
429 m_config->get_flag("hydrology.routing.include_floating_ice"),
430 m_Q);
431 m_Q.scale(1.0 / dt);
432
433 m_log->message(2,
434 " took %d hydrology sub-steps with average dt = %.6f years (%.6f s)\n",
435 step_counter,
436 units::convert(m_sys, dt/step_counter, "seconds", "years"),
437 dt/step_counter);
438}
439
440} // end of namespace hydrology
441} // end of namespace pism
const units::System::Ptr m_sys
unit system used by this component
Definition Component.hh:162
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
void regrid(const std::string &module_name, array::Array &variable, RegriddingFlag flag=NO_REGRID_WITHOUT_REGRID_VARS)
Definition Component.cc:152
std::shared_ptr< const Logger > m_log
logger (for easy access)
Definition Component.hh:164
High-level PISM I/O class.
Definition File.hh:57
array::CellType2 cell_type
Definition Geometry.hh:55
void failed()
Indicates a failure of a parallel section.
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)
std::string get_string(const std::string &name) const
Get a string attribute.
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 read(const std::string &filename, unsigned int time)
Definition Array.cc:753
void write(const OutputFile &file) const
Definition Array.cc:859
void scale(double alpha)
Result: v <- v * alpha. Calls VecScale.
Definition Array.cc:227
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:659
void regrid(const std::string &filename, io::Default default_value)
Definition Array.cc:758
void add(double alpha, const Array &x)
Result: v <- v + alpha * x. Calls VecAXPY.
Definition Array.cc:209
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 ocean(int i, int j) const
Definition CellType.hh:34
bool ice_free_land(int i, int j) const
Definition CellType.hh:62
"Cell type" mask. Adds convenience methods to array::Scalar.
Definition CellType.hh:30
stencils::Star< double > star(int i, int j) const
Returns the values at interfaces of the cell i,j using the staggered grid.
Definition Staggered.hh:75
virtual void bootstrap_impl(const File &input_file, const array::Scalar &ice_thickness)
std::map< std::string, TSDiagnostic::Ptr > scalar_diagnostics_impl() const
void update_P(double dt, const array::CellType &cell_type, const array::Scalar &sliding_speed, const array::Scalar &surface_input_rate, const array::Scalar &basal_melt_rate, const array::Scalar &P_overburden, const array::Scalar &Wtill, const array::Scalar &Wtill_new, const array::Scalar &P, const array::Scalar1 &W, const array::Staggered1 &Ws, const array::Staggered1 &K, const array::Staggered1 &Q, array::Scalar &P_new) const
virtual std::set< VariableMetadata > state_impl() const
void write_state_impl(const OutputFile &output) const
The default (empty implementation).
virtual void init_impl(const array::Scalar &W_till, const array::Scalar &W, const array::Scalar &P)
void P_from_W_steady(const array::Scalar &W, const array::Scalar &P_overburden, const array::Scalar &sliding_speed, array::Scalar &result)
Compute functional relationship P(W) which applies only in steady state.
const array::Scalar & subglacial_water_pressure() const
Copies the P state variable which is the modeled water pressure.
void initialization_message() const
Distributed(std::shared_ptr< const Grid > g)
void update_impl(double t, double dt, const Inputs &inputs)
Update the model state variables W,P by running the subglacial hydrology model.
virtual double max_timestep_P_diff(double phi0, double dt_diff_w) const
void check_P_bounds(array::Scalar &P, const array::Scalar &P_o, bool enforce_upper)
virtual void restart_impl(const File &input_file, int record)
array::Scalar m_surface_input_rate
Definition Hydrology.hh:179
array::Scalar m_grounding_line_change
Definition Hydrology.hh:192
const array::Scalar & surface_input_rate() const
Definition Hydrology.cc:519
void enforce_bounds(const array::CellType &cell_type, const array::Scalar *no_model_mask, double max_thickness, double ocean_water_thickness, array::Scalar &water_thickness, array::Scalar &grounded_margin_change, array::Scalar &grounding_line_change, array::Scalar &conservation_error_change, array::Scalar &no_model_mask_change)
Correct the new water thickness based on boundary requirements.
Definition Hydrology.cc:670
array::Scalar m_basal_melt_rate
Definition Hydrology.hh:182
array::Scalar1 m_W
effective thickness of transportable basal water
Definition Hydrology.hh:173
void compute_overburden_pressure(const array::Scalar &ice_thickness, array::Scalar &result) const
Update the overburden pressure from ice thickness.
Definition Hydrology.cc:481
array::Scalar m_Wtill
effective thickness of basal water stored in till
Definition Hydrology.hh:170
array::Scalar m_Pover
overburden pressure
Definition Hydrology.hh:176
array::Scalar m_grounded_margin_change
Definition Hydrology.hh:191
array::Scalar m_no_model_mask_change
Definition Hydrology.hh:194
array::Scalar m_conservation_error_change
Definition Hydrology.hh:190
const Geometry * geometry
Definition Hydrology.hh:37
const array::Scalar * ice_sliding_speed
Definition Hydrology.hh:41
const array::Scalar1 * no_model_mask
Definition Hydrology.hh:35
virtual void init_impl(const array::Scalar &W_till, const array::Scalar &W, const array::Scalar &P)
Definition Routing.cc:349
array::Staggered1 m_Qstag_average
Definition Routing.hh:115
virtual void write_state_impl(const OutputFile &output) const
The default (empty implementation).
Definition Routing.cc:361
array::Staggered m_Vstag
Definition Routing.hh:118
array::Staggered1 m_Wstag
Definition Routing.hh:121
double max_timestep_W_diff(double KW_max) const
Definition Routing.cc:693
virtual void bootstrap_impl(const File &input_file, const array::Scalar &ice_thickness)
Definition Routing.cc:339
double max_timestep_W_cfl() const
Definition Routing.cc:705
void compute_conductivity(const array::Staggered &W, const array::Scalar &P, const array::Scalar &bed, array::Staggered &result, double &maxKW) const
Compute the nonlinear conductivity at the center of cell edges.
Definition Routing.cc:439
array::Staggered1 m_Kstag
Definition Routing.hh:124
void advective_fluxes(const array::Staggered &V, const array::Scalar &W, array::Staggered &result) const
Compute Q = V W at edge-centers (staggered grid) by first-order upwinding.
Definition Routing.cc:673
void update_W(double dt, const array::Scalar &surface_input_rate, const array::Scalar &basal_melt_rate, const array::Scalar1 &W, const array::Staggered1 &Wstag, const array::Scalar &Wtill, const array::Scalar &Wtill_new, const array::Staggered1 &K, const array::Staggered1 &Q, array::Scalar &W_new)
The computation of Wnew, called by update().
Definition Routing.cc:802
void water_thickness_staggered(const array::Scalar &W, const array::CellType1 &mask, array::Staggered &result)
Average the regular grid water thickness to values at the center of cell edges.
Definition Routing.cc:380
void compute_velocity(const array::Staggered &W, const array::Scalar &P, const array::Scalar &bed, const array::Staggered &K, const array::Scalar1 *no_model_mask, array::Staggered &result) const
Get the advection velocity V at the center of cell edges.
Definition Routing.cc:614
array::Staggered1 m_Qstag
Definition Routing.hh:113
void update_Wtill(double dt, const array::Scalar &Wtill, const array::Scalar &surface_input_rate, const array::Scalar &basal_melt_rate, array::Scalar &Wtill_new)
The computation of Wtillnew, called by update().
Definition Routing.cc:736
array::Scalar m_Wtillnew
Definition Routing.hh:127
array::Scalar m_Wnew
Definition Routing.hh:127
virtual void restart_impl(const File &input_file, int record)
Definition Routing.cc:331
array::Scalar1 m_bottom_surface
Definition Routing.hh:135
virtual std::set< VariableMetadata > state_impl() const
Definition Routing.cc:357
A subglacial hydrology model which assumes water pressure equals overburden pressure.
Definition Routing.hh:81
static Default Nil()
Definition IO_Flags.hh:94
#define PISM_ERROR_LOCATION
#define n
Definition exactTestM.c:37
std::set< VariableMetadata > metadata(std::initializer_list< const Array * > vecs)
Definition Array.cc:1244
static double K(double psi_x, double psi_y, double speed, double epsilon)
void check_bounds(const array::Scalar &W, double W_max)
Definition Hydrology.cc:554
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
static const double g
Definition exactTestP.cc:36
T clip(T x, T a, T b)
static const double c2
Definition exactTestP.cc:45
static const double k
Definition exactTestP.cc:42
static const double Wr
Definition exactTestP.cc:43
void ice_bottom_surface(const Geometry &geometry, array::Scalar &result)
Definition Geometry.cc:241
static const double c1
Definition exactTestP.cc:44
T combine(const T &a, const T &b)