PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
siafd_test.cc
Go to the documentation of this file.
1// Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025 Ed Bueler and 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 <memory>
20static char help[] =
21 "\nSIAFD_TEST\n"
22 " Testing program for SIA, time-independent calculations separate from\n"
23 " IceModel. Uses verification test F. Also may be used in a PISM software"
24 "(regression) test.\n\n";
25
26#include "pism/stressbalance/sia/SIAFD.hh"
27#include "pism/util/EnthalpyConverter.hh"
28#include "pism/stressbalance/StressBalance.hh"
29#include "pism/stressbalance/SSB_Modifier.hh"
30#include "pism/stressbalance/ShallowStressBalance.hh"
31#include "pism/util/Grid.hh"
32#include "pism/util/Mask.hh"
33#include "pism/util/Context.hh"
34#include "pism/util/Time.hh"
35#include "pism/util/error_handling.hh"
36#include "pism/util/petscwrappers/PetscInitializer.hh"
37#include "pism/util/pism_utilities.hh"
38#include "pism/util/pism_options.hh"
39#include "pism/verification/tests/exactTestsFG.hh"
40#include "pism/geometry/Geometry.hh"
41#include "pism/util/Logger.hh"
42#include "pism/util/io/SynchronousOutputWriter.hh"
43#include "pism/util/io/io_helpers.hh"
44
45namespace pism {
46
47static void compute_strain_heating_errors(const array::Array3D &strain_heating,
48 const array::Scalar &thickness,
49 const Grid &grid,
50 double &gmax_strain_heating_err,
51 double &gav_strain_heating_err) {
52 double max_strain_heating_error = 0.0, av_strain_heating_error = 0.0, avcount = 0.0;
53 const int Mz = grid.Mz();
54
55 const double LforFG = 750000; // m
56
57 const double
58 ice_rho = grid.ctx()->config()->get_number("constants.ice.density"),
59 ice_c = grid.ctx()->config()->get_number("constants.ice.specific_heat_capacity");
60
61 array::AccessScope list{&thickness, &strain_heating};
62
63 ParallelSection loop(grid.com);
64 try {
65 for (auto p : grid.points()) {
66 const int i = p.i(), j = p.j();
67
68 double
69 xx = grid.x(i),
70 yy = grid.y(j),
71 r = sqrt(pow(xx, 2) + pow(yy, 2));
72
73 if ((r >= 1.0) && (r <= LforFG - 1.0)) {
74 // only evaluate error if inside sheet and not at central
75 // singularity
76 TestFGParameters F = exactFG(0.0, r, grid.z(), 0.0);
77
78 for (int k = 0; k < Mz; k++) {
79 F.Sig[k] *= ice_rho * ice_c; // scale exact strain_heating to J/(s m^3)
80 }
81 const int ks = grid.kBelowHeight(thickness(i, j));
82 const double *strain_heating_ij = strain_heating.get_column(i, j);
83 for (int k = 0; k < ks; k++) { // only eval error if below num surface
84 const double _strain_heating_error = fabs(strain_heating_ij[k] - F.Sig[k]);
85 max_strain_heating_error = std::max(max_strain_heating_error, _strain_heating_error);
86 avcount += 1.0;
87 av_strain_heating_error += _strain_heating_error;
88 }
89 }
90 }
91 } catch (...) {
92 loop.failed();
93 }
94 loop.check();
95
96 gmax_strain_heating_err = GlobalMax(grid.com, max_strain_heating_error);
97 gav_strain_heating_err = GlobalSum(grid.com, av_strain_heating_error);
98 double gavcount = GlobalSum(grid.com, avcount);
99 gav_strain_heating_err = gav_strain_heating_err/std::max(gavcount,1.0); // avoid div by zero
100}
101
102
103static void computeSurfaceVelocityErrors(const Grid &grid,
104 const array::Scalar &ice_thickness,
105 const array::Array3D &u3,
106 const array::Array3D &v3,
107 const array::Array3D &w3,
108 double &gmaxUerr,
109 double &gavUerr,
110 double &gmaxWerr,
111 double &gavWerr) {
112 double maxUerr = 0.0, maxWerr = 0.0, avUerr = 0.0, avWerr = 0.0;
113
114 const double LforFG = 750000; // m
115
116 array::AccessScope list{&ice_thickness, &u3, &v3, &w3};
117
118 for (auto p : grid.points()) {
119 const int i = p.i(), j = p.j();
120
121 double xx = grid.x(i), yy = grid.y(j),
122 r = sqrt(pow(xx, 2) + pow(yy, 2));
123 if ((r >= 1.0) && (r <= LforFG - 1.0)) { // only evaluate error if inside sheet
124 // and not at central singularity
125 const double H = ice_thickness(i, j);
126 std::vector<double> z(1, H);
127 TestFGParameters F = exactFG(0.0, r, z, 0.0);
128
129 const double uex = (xx/r) * F.U[0];
130 const double vex = (yy/r) * F.U[0];
131 // note that because interpolate does linear interpolation and H is not exactly at
132 // a grid point, this causes nonzero errors
133 const double Uerr = sqrt(pow(u3.interpolate(i,j,H) - uex, 2) +
134 pow(v3.interpolate(i,j,H) - vex, 2));
135 maxUerr = std::max(maxUerr,Uerr);
136 avUerr += Uerr;
137 const double Werr = fabs(w3.interpolate(i,j,H) - F.w[0]);
138 maxWerr = std::max(maxWerr,Werr);
139 avWerr += Werr;
140 }
141 }
142
143 gmaxUerr = GlobalMax(grid.com, maxUerr);
144 gmaxWerr = GlobalMax(grid.com, maxWerr);
145 gavUerr = GlobalSum(grid.com, avUerr);
146 gavUerr = gavUerr/(grid.Mx()*grid.My());
147 gavWerr = GlobalSum(grid.com, avWerr);
148 gavWerr = gavWerr/(grid.Mx()*grid.My());
149}
150
151
153 const Grid &grid,
154 const array::Scalar &thickness,
155 const array::Array3D &temperature,
156 array::Array3D &enthalpy) {
157
158 array::AccessScope list{&temperature, &enthalpy, &thickness};
159
160 for (auto p : grid.points()) {
161 const int i = p.i(), j = p.j();
162
163 const double *T_ij = temperature.get_column(i,j);
164 double *E_ij = enthalpy.get_column(i,j);
165
166 for (unsigned int k=0; k<grid.Mz(); ++k) {
167 double depth = thickness(i,j) - grid.z(k);
168 E_ij[k] = EC.enthalpy_permissive(T_ij[k], 0.0,
169 EC.pressure(depth));
170 }
171
172 }
173
174 enthalpy.update_ghosts();
175}
176
177
178//! \brief Set the test F initial state.
179static void setInitStateF(Grid &grid,
181 array::Scalar &bed,
182 array::Scalar &mask,
183 array::Scalar &surface,
184 array::Scalar &thickness,
185 array::Array3D &enthalpy) {
186
187 double
188 ST = 1.67e-5,
189 Tmin = 223.15, // K
190 LforFG = 750000; // m
191
192 bed.set(0.0);
193 mask.set(MASK_GROUNDED);
194
195 array::AccessScope list{&thickness, &enthalpy};
196
197 for (auto p : grid.points()) {
198 const int i = p.i(), j = p.j();
199
200 const double
201 r = std::max(grid::radius(grid, i, j), 1.0), // avoid singularity at origin
202 Ts = Tmin + ST * r;
203
204 if (r > LforFG - 1.0) { // if (essentially) outside of sheet
205 thickness(i, j) = 0.0;
206 enthalpy.set_column(i, j, Ts);
207 } else {
208 TestFGParameters F = exactFG(0.0, r, grid.z(), 0.0);
209
210 thickness(i, j) = F.H;
211 enthalpy.set_column(i, j, (F.T).data());
212 }
213 }
214
215
216 thickness.update_ghosts();
217
218 surface.copy_from(thickness);
219
220 enthalpy_from_temperature_cold(EC, grid, thickness, enthalpy,
221 enthalpy);
222}
223
224static void reportErrors(const Grid &grid,
225 units::System::Ptr unit_system,
226 const array::Scalar &thickness,
227 const array::Array3D &u_sia,
228 const array::Array3D &v_sia,
229 const array::Array3D &w_sia,
230 const array::Array3D &strain_heating) {
231
232 auto log = grid.ctx()->log();
233
234 // strain_heating errors if appropriate; reported in 10^6 J/(s m^3)
235 double max_strain_heating_error, av_strain_heating_error;
236 compute_strain_heating_errors(strain_heating, thickness,
237 grid,
238 max_strain_heating_error, av_strain_heating_error);
239
240 log->message(1,
241 "Sigma : maxSig avSig\n");
242 log->message(1, " %12.6f%12.6f\n",
243 max_strain_heating_error*1.0e6, av_strain_heating_error*1.0e6);
244
245 // surface velocity errors if exact values are available; reported in m year-1
246 double maxUerr, avUerr, maxWerr, avWerr;
247 computeSurfaceVelocityErrors(grid, thickness,
248 u_sia,
249 v_sia,
250 w_sia,
251 maxUerr, avUerr,
252 maxWerr, avWerr);
253
254 log->message(1,
255 "surf vels : maxUvec avUvec maxW avW\n");
256 log->message(1, " %12.6f%12.6f%12.6f%12.6f\n",
257 units::convert(unit_system, maxUerr, "m second^-1", "m year^-1"),
258 units::convert(unit_system, avUerr, "m second^-1", "m year^-1"),
259 units::convert(unit_system, maxWerr, "m second^-1", "m year^-1"),
260 units::convert(unit_system, avWerr, "m second^-1", "m year^-1"));
261}
262
263} // end of namespace pism
264
265int main(int argc, char *argv[]) {
266
267 using namespace pism;
268 using namespace pism::stressbalance;
269
270 MPI_Comm com = MPI_COMM_WORLD;
271 petsc::Initializer petsc(argc, argv, help);
272
273 com = MPI_COMM_WORLD;
274
275 /* This explicit scoping forces destructors to be called before PetscFinalize() */
276 try {
277 // set default verbosity
278 std::shared_ptr<Context> ctx = context_from_options(com, "siafd_test");
279 auto config = ctx->config();
280
281 config->set_flag("stress_balance.sia.grain_size_age_coupling", false);
282 config->set_string("stress_balance.sia.flow_law", "arr");
283
284 set_config_from_options(*config);
285 config->resolve_filenames();
286
287 std::string usage = "\n"
288 "usage of SIAFD_TEST:\n"
289 " run siafd_test -Mx <number> -My <number> -Mz <number> -o foo.nc\n"
290 "\n";
291
292 bool stop = maybe_show_usage(*ctx->log(), "siafd_test", usage);
293
294 if (stop) {
295 return 0;
296 }
297
298 grid::Parameters P(*config);
299 P.Lx = 900e3;
300 P.Ly = P.Lx;
301
302 double Lz = 4000.0;
303 unsigned int Mz = config->get_number("grid.Mz");
304
305 P.z = grid::compute_vertical_levels(Lz, Mz, grid::EQUAL);
306 P.ownership_ranges_from_options(*ctx->config(), ctx->size());
307
308 // create grid and set defaults
309 auto grid = std::make_shared<Grid>(ctx, P);
310 grid->report_parameters();
311
312 std::shared_ptr<EnthalpyConverter> EC(new ColdEnthalpyConverter(*config));
313
314 const int WIDE_STENCIL = config->get_number("grid.max_stencil_width");
315
317 enthalpy(grid, "enthalpy", array::WITH_GHOSTS, grid->z(), WIDE_STENCIL),
318 age(grid, "age", array::WITHOUT_GHOSTS, grid->z());
319
320 Geometry geometry(grid);
321 geometry.sea_level_elevation.set(0.0);
322
323 // age of the ice; is not used here
324 age.metadata(0).long_name("age of the ice").units("s");
325 age.set(0.0);
326
327 // enthalpy in the ice
328 enthalpy.metadata(0)
329 .long_name("ice enthalpy (includes sensible heat, latent heat, pressure)")
330 .units("J kg^-1");
331 //
332 enthalpy.set(EC->enthalpy(263.15, 0.0, EC->pressure(1000.0)));
333
334 // Create the SIA solver object:
335
336 // We use SIA_Nonsliding and not SIAFD here because we need the z-component
337 // of the ice velocity, which is computed using incompressibility of ice in
338 // StressBalance::compute_vertical_velocity().
339 std::shared_ptr<SIAFD> sia(new SIAFD(grid));
340 std::shared_ptr<ZeroSliding> no_sliding(new ZeroSliding(grid));
341
342 // stress_balance will de-allocate no_sliding and sia.
343 StressBalance stress_balance(grid, no_sliding, sia);
344
345 // fill the fields:
346 setInitStateF(*grid, *EC,
347 geometry.bed_elevation,
348 geometry.cell_type,
349 geometry.ice_surface_elevation,
350 geometry.ice_thickness,
351 enthalpy);
352
353 geometry.ensure_consistency(config->get_number("geometry.ice_free_thickness_standard"));
354
355 // Initialize the SIA solver:
356 stress_balance.init();
357
358 bool full_update = true;
359
361 inputs.geometry = &geometry;
362 inputs.water_column_pressure = nullptr;
363 inputs.enthalpy = &enthalpy;
364 inputs.age = &age;
365
366 stress_balance.update(inputs, full_update);
367
368 // Report errors relative to the exact solution:
369 const array::Array3D
370 &u3 = stress_balance.velocity_u(),
371 &v3 = stress_balance.velocity_v(),
372 &w3 = stress_balance.velocity_w();
373
374 const array::Array3D &sigma = stress_balance.volumetric_strain_heating();
375
376 reportErrors(*grid, ctx->unit_system(),
377 geometry.ice_thickness, u3, v3, w3, sigma);
378
379 {
380 auto writer = std::make_shared<SynchronousOutputWriter>(grid->com, *config);
381 writer->initialize({}, true);
382
383 // Write results to an output file:
384 OutputFile file(writer, config->get_string("output.file"));
385
386 const array::Array *vecs[] = {
387 &geometry.ice_surface_elevation,
388 &geometry.ice_thickness,
389 &geometry.cell_type,
390 &geometry.bed_elevation,
391 &sia->diffusivity(),
392 &u3,
393 &v3,
394 &w3,
395 &sigma,
396 };
397
398 // define
399 auto time = ctx->time();
400 file.define_variable(time->metadata());
401 for (const auto *vec : vecs) {
402 for (auto &var : vec->all_metadata()) {
403 file.define_variable(var);
404 }
405 }
406
407 // write
408 file.append_time(time->current());
409 for (const auto *vec : vecs) {
410 vec->write(file);
411 }
412 }
413 }
414 catch (...) {
415 handle_fatal_errors(com);
416 return 1;
417 }
418
419 return 0;
420}
An EnthalpyConverter for use in verification tests.
double enthalpy_permissive(double T, double omega, double P) const
Compute enthalpy more permissively than enthalpy().
double pressure(double depth) const
Get pressure in ice from depth below surface using the hydrostatic assumption.
Converts between specific enthalpy and temperature or liquid content.
array::Scalar1 sea_level_elevation
Definition Geometry.hh:48
void ensure_consistency(double ice_free_thickness_threshold)
Definition Geometry.cc:116
array::Scalar2 ice_surface_elevation
Definition Geometry.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
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.
VariableMetadata & long_name(const std::string &input)
VariableMetadata & 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
double interpolate(int i, int j, double z) const
Return value of scalar quantity at level z (m) above base of ice (by linear interpolation).
Definition Array3D.cc:95
void set_column(int i, int j, double c)
Set all values of scalar quantity to given a single value in a particular column.
Definition Array3D.cc:56
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
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
std::vector< double > z
Vertical levels.
Definition Grid.hh:142
void ownership_ranges_from_options(const Config &config, unsigned int size)
Re-compute ownership ranges. Uses current values of Mx and My.
Definition Grid.cc:1181
double Ly
Domain half-width in the Y direction.
Definition Grid.hh:128
double Lx
Domain half-width in the X direction.
Definition Grid.hh:126
Grid parameters; used to collect defaults before an Grid is allocated.
Definition Grid.hh:100
const array::Array3D * age
const array::Scalar * water_column_pressure
const array::Array3D * enthalpy
const array::Array3D & velocity_w() const
const array::Array3D & velocity_u() const
Get components of the the 3D velocity field.
void init()
Initialize the StressBalance object.
void update(const Inputs &inputs, bool full_update)
Update all the fields if (full_update), only update diffusive flux and max. diffusivity otherwise.
const array::Array3D & velocity_v() const
const array::Array3D & volumetric_strain_heating() const
The class defining PISM's interface to the shallow stress balance code.
Returns zero velocity field, zero friction heating, and zero for D^2.
std::shared_ptr< System > Ptr
Definition Units.hh:47
const double Ts
Definition exactTestK.c:39
double radius(const Grid &grid, int i, int j)
Returns the distance from the point (i,j) to the origin.
Definition Grid.cc:1367
Stress balance models and related diagnostics.
Definition Isochrones.hh:31
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 double F(double SL, double B, double H, double alpha)
static void compute_strain_heating_errors(const array::Array3D &strain_heating, const array::Scalar &thickness, const Grid &grid, double &gmax_strain_heating_err, double &gav_strain_heating_err)
Definition siafd_test.cc:47
static void computeSurfaceVelocityErrors(const Grid &grid, const array::Scalar &ice_thickness, const array::Array3D &u3, const array::Array3D &v3, const array::Array3D &w3, double &gmaxUerr, double &gavUerr, double &gmaxWerr, double &gavWerr)
static void enthalpy_from_temperature_cold(EnthalpyConverter &EC, const Grid &grid, const array::Scalar &thickness, const array::Array3D &temperature, array::Array3D &enthalpy)
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
static const double k
Definition exactTestP.cc:42
TestFGParameters exactFG(double t, double r, const std::vector< double > &z, double Cp)
@ MASK_GROUNDED
Definition Mask.hh:33
static void reportErrors(const Grid &grid, units::System::Ptr unit_system, const array::Scalar &thickness, const array::Array3D &u_sia, const array::Array3D &v_sia, const array::Array3D &w_sia, const array::Array3D &strain_heating)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
static void setInitStateF(Grid &grid, EnthalpyConverter &EC, array::Scalar &bed, array::Scalar &mask, array::Scalar &surface, array::Scalar &thickness, array::Array3D &enthalpy)
Set the test F initial state.
int main(int argc, char *argv[])
static char help[]
Definition siafd_test.cc:20