PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
SSATestCase.cc
Go to the documentation of this file.
1// Copyright (C) 2009--2026 Ed Bueler, Constantine Khroulev, and David Maxwell
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 "pism/stressbalance/ssa/tests/SSATestCase.hh"
20#include "pism/stressbalance/ssa/SSAFD_SNES.hh"
21#include "pism/stressbalance/StressBalance.hh"
22#include "pism/util/Context.hh"
23#include "pism/util/Interpolation1D.hh"
24#include "pism/util/io/File.hh"
25#include "pism/util/pism_options.hh"
26#include "pism/util/pism_utilities.hh"
27
28#include "pism/stressbalance/ssa/SSAFD.hh"
29#include "pism/stressbalance/ssa/SSAFEM.hh"
30#include "pism/util/Logger.hh"
31#include "pism/util/Time.hh"
32#include "pism/util/io/SynchronousOutputWriter.hh"
33#include "pism/util/io/IO_Flags.hh"
34#include "pism/util/io/io_helpers.hh"
35#include <vector>
36
37namespace pism {
38namespace stressbalance {
39
40SSATestCase::SSATestCase(std::shared_ptr<SSA> ssa)
41 : m_grid(ssa->grid()),
42 m_ctx(m_grid->ctx()),
43 m_config(m_ctx->config()),
44 m_sys(m_ctx->unit_system()),
45 m_tauc(m_grid, "tauc"),
46 m_ice_enthalpy(m_grid, "enthalpy", array::WITH_GHOSTS, m_grid->z(), 1),
47 m_bc_values(m_grid, "_bc"), // u_bc and v_bc
48 m_bc_mask(m_grid, "bc_mask"),
49 m_geometry(m_grid),
50 m_ssa(ssa) {
51
53
54 // yield stress for basal till (plastic or pseudo-plastic model)
56 .long_name("yield stress for basal till (plastic or pseudo-plastic model)")
57 .units("Pa");
58
59 // enthalpy
61 .long_name("ice enthalpy (includes sensible heat, latent heat, pressure)")
62 .units("J kg^-1");
63
64 // dirichlet boundary condition (FIXME: perhaps unused!)
66 .long_name("X-component of the SSA velocity boundary conditions")
67 .units("m s^-1")
68 .output_units("m year^-1");
70 .long_name("Y-component of the SSA velocity boundary conditions")
71 .units("m s^-1")
72 .output_units("m year^-1");
73
75 double fill_value =
76 units::convert(sys, m_config->get_number("output.fill_value"), "m year^-1", "m second^-1");
77
78 auto large_number = units::convert(m_sys, 1e6, "m year^-1", "m second^-1");
79
80 m_bc_values.metadata(0)["valid_range"] = { -large_number, large_number };
81 m_bc_values.metadata(0)["_FillValue"] = { fill_value };
82
83 m_bc_values.metadata(1)["valid_range"] = { -large_number, large_number };
84 m_bc_values.metadata(1)["_FillValue"] = { fill_value };
85
86 m_bc_values.set(fill_value);
87
88 // Dirichlet B.C. mask
89 m_bc_mask.metadata().long_name("grounded_dragging_floating integer mask");
90
91 m_bc_mask.metadata()["flag_values"] = { 0.0, 1.0 };
92 m_bc_mask.metadata()["flag_meanings"] = "no_data ssa.dirichlet_bc_location";
93}
94
95std::shared_ptr<SSA> SSATestCase::solver(std::shared_ptr<Grid> grid, const std::string &method) {
96 if (method == "fem") {
97 return std::make_shared<SSAFEM>(grid);
98 }
99 if (method == "fd") {
100 return std::make_shared<SSAFD>(grid, false);
101 }
102 return std::make_shared<SSAFD_SNES>(grid, false);
103}
104
105//! Initialize the test case at the start of a run
107
108 m_ssa->init();
109
110 // Allow the subclass to set the coefficients.
112}
113
114//! Solve the SSA
116 m_ctx->log()->message(2, "* Solving the SSA stress balance ...\n");
117
118 m_geometry.ensure_consistency(m_config->get_number("stress_balance.ice_free_thickness_standard"));
119
120 Inputs inputs;
121 inputs.water_column_pressure = nullptr;
122 inputs.geometry = &m_geometry;
123 inputs.enthalpy = &m_ice_enthalpy;
124 inputs.basal_yield_stress = &m_tauc;
125 inputs.bc_mask = &m_bc_mask;
126 inputs.bc_values = &m_bc_values;
127
128 bool full_update = true;
129 m_ssa->update(inputs, full_update);
130}
131
132//! Report on the generated solution
133void SSATestCase::report(const std::string &testname) {
134
135 m_ctx->log()->message(3, m_ssa->stdout_report());
136
137 double maxvecerr = 0.0, avvecerr = 0.0, avuerr = 0.0, avverr = 0.0, maxuerr = 0.0, maxverr = 0.0;
138 double gmaxvecerr = 0.0, gavvecerr = 0.0, gavuerr = 0.0, gavverr = 0.0, gmaxuerr = 0.0,
139 gmaxverr = 0.0;
140
141 if (m_config->get_flag("basal_resistance.pseudo_plastic.enabled") &&
142 m_config->get_number("basal_resistance.pseudo_plastic.q") != 1.0) {
143 m_ctx->log()->message(1, "WARNING: numerical errors not valid for pseudo-plastic till\n");
144 }
145 m_ctx->log()->message(1, "NUMERICAL ERRORS in velocity relative to exact solution:\n");
146
147 const array::Vector &vel_ssa = m_ssa->velocity();
148
149 array::AccessScope list{ &vel_ssa };
150
151 double exactvelmax = 0, gexactvelmax = 0;
152 for (auto p : m_grid->points()) {
153 const int i = p.i(), j = p.j();
154
155 double uexact, vexact;
156 double myx = m_grid->x(i), myy = m_grid->y(j);
157
158 exactSolution(i, j, myx, myy, &uexact, &vexact);
159
160 double exactnormsq = sqrt(uexact * uexact + vexact * vexact);
161 exactvelmax = std::max(exactnormsq, exactvelmax);
162
163 // compute maximum errors
164 const double uerr = fabs(vel_ssa(i, j).u - uexact);
165 const double verr = fabs(vel_ssa(i, j).v - vexact);
166 avuerr = avuerr + uerr;
167 avverr = avverr + verr;
168 maxuerr = std::max(maxuerr, uerr);
169 maxverr = std::max(maxverr, verr);
170 const double vecerr = sqrt(uerr * uerr + verr * verr);
171 maxvecerr = std::max(maxvecerr, vecerr);
172 avvecerr = avvecerr + vecerr;
173 }
174
175 unsigned int N = (m_grid->Mx() * m_grid->My());
176
177 gexactvelmax = GlobalMax(m_grid->com, exactvelmax);
178 gmaxuerr = GlobalMax(m_grid->com, maxuerr);
179 gmaxverr = GlobalMax(m_grid->com, maxverr);
180 gavuerr = GlobalSum(m_grid->com, avuerr);
181 gavuerr = gavuerr / N;
182 gavverr = GlobalSum(m_grid->com, avverr);
183 gavverr = gavverr / N;
184 gmaxvecerr = GlobalMax(m_grid->com, maxvecerr);
185 gavvecerr = GlobalSum(m_grid->com, avvecerr);
186 gavvecerr = gavvecerr / N;
187
189
190 m_ctx->log()->message(
191 1, "velocity : maxvector prcntavvec maxu maxv avu avv\n");
192 m_ctx->log()->message(1, " %11.4f%13.5f%10.4f%10.4f%10.4f%10.4f\n",
193 convert(m_sys, gmaxvecerr, "m second-1", "m year-1"),
194 (gavvecerr / gexactvelmax) * 100.0,
195 convert(m_sys, gmaxuerr, "m second-1", "m year-1"),
196 convert(m_sys, gmaxverr, "m second-1", "m year-1"),
197 convert(m_sys, gavuerr, "m second-1", "m year-1"),
198 convert(m_sys, gavverr, "m second-1", "m year-1"));
199
200 m_ctx->log()->message(1, "NUM ERRORS DONE\n");
201
202 report_netcdf(testname, convert(m_sys, gmaxvecerr, "m second-1", "m year-1"),
203 (gavvecerr / gexactvelmax) * 100.0,
204 convert(m_sys, gmaxuerr, "m second-1", "m year-1"),
205 convert(m_sys, gmaxverr, "m second-1", "m year-1"),
206 convert(m_sys, gavuerr, "m second-1", "m year-1"),
207 convert(m_sys, gavverr, "m second-1", "m year-1"));
208}
209
210namespace details {
211static void write(const File &file, const char *name,
212 const char *units,
213 const char *long_name,
214 unsigned int start, double value,
215 io::Type type = io::PISM_DOUBLE) {
217 file.define_variable(name, type, { "N" });
218 file.write_attribute(name, "units", units);
219 file.write_attribute(name, "long_name", long_name);
220 file.write_variable(name, { start }, { 1 }, &value);
221}
222} // namespace details
223
224void SSATestCase::report_netcdf(const std::string &testname, double max_vector, double rel_vector,
225 double max_u, double max_v, double avg_u, double avg_v) {
226 auto sys = m_grid->ctx()->unit_system();
227
228 VariableMetadata global_attributes("PISM_GLOBAL", sys);
229 global_attributes["source"] = std::string("PISM ") + pism::revision;
230
231 options::String filename("-report_file", "NetCDF error report file");
232
233 if (not filename.is_set()) {
234 return;
235 }
236
237 m_ctx->log()->message(2, "Also writing errors to '%s'...\n", filename->c_str());
238
239 bool append = options::Bool("-append", "Append the NetCDF error report");
240
242 if (not append) {
244 }
245
246 // Find the number of records in this file:
247 File file(m_grid->com, filename, io::PISM_NETCDF3, mode); // OK to use NetCDF3.
248 size_t start = static_cast<size_t>(file.dimension_length("N"));
249
250 for (const auto &arg : global_attributes.all_strings()) {
251 file.write_attribute("PISM_GLOBAL", arg.first, arg.second);
252 }
253
254 details::write(file, "N", "", "", start, (double)(start + 1));
255
256 details::write(file, "dx", "meters", "", start, m_grid->dx());
257
258 details::write(file, "dy", "meters", "", start, m_grid->dy());
259
260 details::write(file, "test", "", "", start, (double)testname[0], io::PISM_INT);
261
262 details::write(file, "max_velocity", "m year^-1", "maximum ice velocity magnitude error", start,
263 max_vector);
264
265 details::write(file, "relative_velocity", "percent", "relative ice velocity magnitude error",
266 start, rel_vector);
267
268 details::write(file, "maximum_u", "m year^-1",
269 "maximum error in the X-component of the ice velocity", start, max_u);
270
271 details::write(file, "maximum_v", "m year^-1",
272 "maximum error in the Y-component of the ice velocity", start, max_v);
273
274 details::write(file, "average_u", "m year^-1",
275 "average error in the X-component of the ice velocity", start, avg_u);
276
277 details::write(file, "average_v", "m year^-1",
278 "average error in the Y-component of the ice velocity", start, avg_v);
279
280 file.close();
281}
282
283void SSATestCase::exactSolution(int /*i*/, int /*j*/, double /*x*/, double /*y*/, double *u,
284 double *v) {
285 *u = 0;
286 *v = 0;
287}
288
289//! Save the computation and data to a file.
290void SSATestCase::write(const std::string &filename) {
291 auto writer = std::make_shared<SynchronousOutputWriter>(m_grid->com, *m_config);
292 writer->initialize({}, true);
293
294 // Write results to an output file:
295 OutputFile file(writer, filename);
296
297 // Write results to an output file:
298 file.define_variable(m_ctx->time()->metadata());
299 file.append_time(0.0);
300
301 const array::Array *variables[] = {
304 };
305
306 // define
307 for (const auto *v : variables) {
308 for (const auto &m : v->all_metadata()) {
309 file.define_variable(m);
310 }
311 }
312
313 // write
314 for (const auto *v : variables) {
315 v->write(file);
316 }
317
318 // write all easily available diagnostics:
319 {
320 std::vector<std::shared_ptr<array::Array>> vecs;
321 for (auto &pair : m_ssa->spatial_diagnostics()) {
322 try {
323 vecs.push_back(pair.second->compute());
324 } catch (RuntimeError &e) {
325 // ignore errors: some diagnostics may need variables not present in this context
326 }
327 }
328
329 // define
330 for (auto &v : vecs) {
331 for (auto &m : v->all_metadata()) {
332 file.define_variable(m);
333 }
334 }
335
336 // write
337 for (auto &v : vecs) {
338 v->write(file);
339 }
340 }
341
342 array::Vector tmp(m_grid, "_exact");
343 tmp.metadata(0)
344 .long_name("X-component of the SSA exact solution")
345 .units("m s^-1");
346 tmp.metadata(1)
347 .long_name("Y-component of the SSA exact solution")
348 .units("m s^-1");
349
350 array::AccessScope list(tmp);
351 for (auto p : m_grid->points()) {
352 const int i = p.i(), j = p.j();
353
354 exactSolution(i, j, m_grid->x(i), m_grid->y(j),
355 &(tmp(i,j).u), &(tmp(i,j).v));
356 }
357
358 // define
359 for (auto &m : tmp.all_metadata()) {
360 file.define_variable(m);
361 }
362 // write
363 tmp.write(file);
364
365 tmp.metadata(0)
366 .set_name("u_error")
367 .long_name("X-component of the error (exact - computed)")
368 .units("m s^-1");
369 tmp.metadata(1)
370 .set_name("v_error")
371 .long_name("Y-component of the error (exact - computed)")
372 .units("m s^-1");
373
374 tmp.add(-1.0, m_ssa->velocity());
375 // define
376 for (auto &m : tmp.all_metadata()) {
377 file.define_variable(m);
378 }
379 tmp.write(file);
380
381 array::Scalar error_mag(m_grid, "error_mag");
382 error_mag.metadata(0).long_name("magnitude of the error").units("m s^-1");
383 array::compute_magnitude(tmp, error_mag);
384
385 // define
386 for (auto &m : error_mag.all_metadata()) {
387 file.define_variable(m);
388 }
389 // write
390 error_mag.write(file);
391
392 file.close();
393}
394
395} // end of namespace stressbalance
396} // end of namespace pism
void define_dimension(const std::string &name, size_t length) const
Definition File.cc:537
void write_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *op) const
Definition File.cc:717
void close()
Definition File.cc:237
void define_variable(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Define a variable.
Definition File.cc:548
void write_attribute(const std::string &var_name, const std::string &att_name, io::Type nctype, const std::vector< double > &values) const
Write a multiple-valued double attribute.
Definition File.cc:595
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition File.cc:420
High-level PISM I/O class.
Definition File.hh:57
void ensure_consistency(double ice_free_thickness_threshold)
Definition Geometry.cc:116
array::Scalar2 ice_surface_elevation
Definition Geometry.hh:57
array::Scalar2 ice_thickness
Definition Geometry.hh:51
array::Scalar2 bed_elevation
Definition Geometry.hh:47
void append_time(double time_seconds) const
Definition OutputFile.cc:41
void define_variable(const VariableMetadata &variable) const
Definition OutputFile.cc:31
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & set_name(const std::string &name)
const std::map< std::string, std::string > & all_strings() const
VariableMetadata & output_units(const std::string &input)
std::shared_ptr< units::System > unit_system() const
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
void set_interpolation_type(InterpolationType type)
Definition Array.cc:181
void write(const OutputFile &file) const
Definition Array.cc:859
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:659
std::vector< VariableMetadata > all_metadata() const
Definition Array.cc:477
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 is_set() const
Definition options.hh:35
const array::Scalar * basal_yield_stress
const array::Scalar * bc_mask
const array::Scalar * water_column_pressure
const array::Array3D * enthalpy
const array::Vector * bc_values
std::shared_ptr< const pism::Grid > m_grid
virtual void init()
Initialize the test case at the start of a run.
const units::System::Ptr m_sys
const std::shared_ptr< const Config > m_config
virtual void initializeSSACoefficients()=0
Set up the coefficient variables as appropriate for the test case.
void report_netcdf(const std::string &testname, double max_vector, double rel_vector, double max_u, double max_v, double avg_u, double avg_v)
virtual void exactSolution(int i, int j, double x, double y, double *u, double *v)
static std::shared_ptr< SSA > solver(std::shared_ptr< Grid > grid, const std::string &method)
virtual void report(const std::string &testname)
Report on the generated solution.
virtual void run()
Solve the SSA.
SSATestCase(std::shared_ptr< SSA > ssa)
std::shared_ptr< SSA > m_ssa
virtual void write(const std::string &filename)
Save the computation and data to a file.
const std::shared_ptr< const Context > m_ctx
std::shared_ptr< System > Ptr
Definition Units.hh:47
void compute_magnitude(const array::Vector &input, array::Scalar &result)
Definition Scalar.cc:66
@ PISM_NETCDF3
Definition IO_Flags.hh:58
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition IO_Flags.hh:75
@ PISM_READWRITE
open an existing file for reading and writing
Definition IO_Flags.hh:71
@ PISM_UNLIMITED
Definition IO_Flags.hh:80
@ PISM_DOUBLE
Definition IO_Flags.hh:53
bool Bool(const std::string &option, const std::string &description)
Definition options.cc:190
static void write(const File &file, const char *name, const char *units, const char *long_name, unsigned int start, double value, io::Type type=io::PISM_DOUBLE)
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
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)