Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SSATestCase.cc
Go to the documentation of this file.
1// Copyright (C) 2009--2024 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/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/io/io_helpers.hh"
26#include "pism/util/pism_options.hh"
27#include "pism/util/pism_utilities.hh"
28
29#include "pism/stressbalance/ssa/SSAFD.hh"
30#include "pism/stressbalance/ssa/SSAFEM.hh"
31
32namespace pism {
33namespace stressbalance {
34
35SSATestCase::SSATestCase(std::shared_ptr<SSA> ssa)
36 : m_grid(ssa->grid()),
37 m_ctx(m_grid->ctx()),
38 m_config(m_ctx->config()),
39 m_sys(m_ctx->unit_system()),
40 m_tauc(m_grid, "tauc"),
41 m_ice_enthalpy(m_grid, "enthalpy", array::WITH_GHOSTS, m_grid->z(), 1),
42 m_bc_values(m_grid, "_bc"), // u_bc and v_bc
43 m_bc_mask(m_grid, "bc_mask"),
44 m_geometry(m_grid),
45 m_ssa(ssa) {
46
48
49 // yield stress for basal till (plastic or pseudo-plastic model)
51 .long_name("yield stress for basal till (plastic or pseudo-plastic model)")
52 .units("Pa");
53
54 // enthalpy
56 .long_name("ice enthalpy (includes sensible heat, latent heat, pressure)")
57 .units("J kg^-1");
58
59 // dirichlet boundary condition (FIXME: perhaps unused!)
61 .long_name("X-component of the SSA velocity boundary conditions")
62 .units("m s^-1")
63 .output_units("m year^-1");
65 .long_name("Y-component of the SSA velocity boundary conditions")
66 .units("m s^-1")
67 .output_units("m year^-1");
68
70 double fill_value =
71 units::convert(sys, m_config->get_number("output.fill_value"), "m year^-1", "m second^-1");
72
73 auto large_number = units::convert(m_sys, 1e6, "m year^-1", "m second^-1");
74
75 m_bc_values.metadata(0)["valid_range"] = { -large_number, large_number };
76 m_bc_values.metadata(0)["_FillValue"] = { fill_value };
77
78 m_bc_values.metadata(1)["valid_range"] = { -large_number, large_number };
79 m_bc_values.metadata(1)["_FillValue"] = { fill_value };
80
81 m_bc_values.set(fill_value);
82
83 // Dirichlet B.C. mask
84 m_bc_mask.metadata().long_name("grounded_dragging_floating integer mask");
85
86 m_bc_mask.metadata()["flag_values"] = { 0.0, 1.0 };
87 m_bc_mask.metadata()["flag_meanings"] = "no_data ssa.dirichlet_bc_location";
88}
89
90std::shared_ptr<SSA> SSATestCase::solver(std::shared_ptr<Grid> grid, const std::string &method) {
91 if (method == "fem") {
92 return std::make_shared<SSAFEM>(grid);
93 }
94 if (method == "fd") {
95 return std::make_shared<SSAFD>(grid, false);
96 }
97 return std::make_shared<SSAFD_SNES>(grid, false);
98}
99
100//! Initialize the test case at the start of a run
102
103 m_ssa->init();
104
105 // Allow the subclass to set the coefficients.
107}
108
109//! Solve the SSA
111 m_ctx->log()->message(2, "* Solving the SSA stress balance ...\n");
112
113 m_geometry.ensure_consistency(m_config->get_number("stress_balance.ice_free_thickness_standard"));
114
115 Inputs inputs;
116 inputs.water_column_pressure = nullptr;
117 inputs.geometry = &m_geometry;
118 inputs.enthalpy = &m_ice_enthalpy;
119 inputs.basal_yield_stress = &m_tauc;
120 inputs.bc_mask = &m_bc_mask;
121 inputs.bc_values = &m_bc_values;
122
123 bool full_update = true;
124 m_ssa->update(inputs, full_update);
125}
126
127//! Report on the generated solution
128void SSATestCase::report(const std::string &testname) {
129
130 m_ctx->log()->message(3, m_ssa->stdout_report());
131
132 double maxvecerr = 0.0, avvecerr = 0.0, avuerr = 0.0, avverr = 0.0, maxuerr = 0.0, maxverr = 0.0;
133 double gmaxvecerr = 0.0, gavvecerr = 0.0, gavuerr = 0.0, gavverr = 0.0, gmaxuerr = 0.0,
134 gmaxverr = 0.0;
135
136 if (m_config->get_flag("basal_resistance.pseudo_plastic.enabled") &&
137 m_config->get_number("basal_resistance.pseudo_plastic.q") != 1.0) {
138 m_ctx->log()->message(1, "WARNING: numerical errors not valid for pseudo-plastic till\n");
139 }
140 m_ctx->log()->message(1, "NUMERICAL ERRORS in velocity relative to exact solution:\n");
141
142 const array::Vector &vel_ssa = m_ssa->velocity();
143
144 array::AccessScope list{ &vel_ssa };
145
146 double exactvelmax = 0, gexactvelmax = 0;
147 for (auto p = m_grid->points(); p; p.next()) {
148 const int i = p.i(), j = p.j();
149
150 double uexact, vexact;
151 double myx = m_grid->x(i), myy = m_grid->y(j);
152
153 exactSolution(i, j, myx, myy, &uexact, &vexact);
154
155 double exactnormsq = sqrt(uexact * uexact + vexact * vexact);
156 exactvelmax = std::max(exactnormsq, exactvelmax);
157
158 // compute maximum errors
159 const double uerr = fabs(vel_ssa(i, j).u - uexact);
160 const double verr = fabs(vel_ssa(i, j).v - vexact);
161 avuerr = avuerr + uerr;
162 avverr = avverr + verr;
163 maxuerr = std::max(maxuerr, uerr);
164 maxverr = std::max(maxverr, verr);
165 const double vecerr = sqrt(uerr * uerr + verr * verr);
166 maxvecerr = std::max(maxvecerr, vecerr);
167 avvecerr = avvecerr + vecerr;
168 }
169
170 unsigned int N = (m_grid->Mx() * m_grid->My());
171
172 gexactvelmax = GlobalMax(m_grid->com, exactvelmax);
173 gmaxuerr = GlobalMax(m_grid->com, maxuerr);
174 gmaxverr = GlobalMax(m_grid->com, maxverr);
175 gavuerr = GlobalSum(m_grid->com, avuerr);
176 gavuerr = gavuerr / N;
177 gavverr = GlobalSum(m_grid->com, avverr);
178 gavverr = gavverr / N;
179 gmaxvecerr = GlobalMax(m_grid->com, maxvecerr);
180 gavvecerr = GlobalSum(m_grid->com, avvecerr);
181 gavvecerr = gavvecerr / N;
182
184
185 m_ctx->log()->message(
186 1, "velocity : maxvector prcntavvec maxu maxv avu avv\n");
187 m_ctx->log()->message(1, " %11.4f%13.5f%10.4f%10.4f%10.4f%10.4f\n",
188 convert(m_sys, gmaxvecerr, "m second-1", "m year-1"),
189 (gavvecerr / gexactvelmax) * 100.0,
190 convert(m_sys, gmaxuerr, "m second-1", "m year-1"),
191 convert(m_sys, gmaxverr, "m second-1", "m year-1"),
192 convert(m_sys, gavuerr, "m second-1", "m year-1"),
193 convert(m_sys, gavverr, "m second-1", "m year-1"));
194
195 m_ctx->log()->message(1, "NUM ERRORS DONE\n");
196
197 report_netcdf(testname, convert(m_sys, gmaxvecerr, "m second-1", "m year-1"),
198 (gavvecerr / gexactvelmax) * 100.0,
199 convert(m_sys, gmaxuerr, "m second-1", "m year-1"),
200 convert(m_sys, gmaxverr, "m second-1", "m year-1"),
201 convert(m_sys, gavuerr, "m second-1", "m year-1"),
202 convert(m_sys, gavverr, "m second-1", "m year-1"));
203}
204
205void SSATestCase::report_netcdf(const std::string &testname, double max_vector, double rel_vector,
206 double max_u, double max_v, double avg_u, double avg_v) {
207 auto sys = m_grid->ctx()->unit_system();
208
209 VariableMetadata global_attributes("PISM_GLOBAL", sys);
210
211 options::String filename("-report_file", "NetCDF error report file");
212
213 if (not filename.is_set()) {
214 return;
215 }
216
217 m_ctx->log()->message(2, "Also writing errors to '%s'...\n", filename->c_str());
218
219 bool append = options::Bool("-append", "Append the NetCDF error report");
220
222 if (not append) {
224 }
225
226 global_attributes["source"] = std::string("PISM ") + pism::revision;
227
228 // Find the number of records in this file:
229 File file(m_grid->com, filename, io::PISM_NETCDF3, mode); // OK to use NetCDF3.
230 size_t start = static_cast<size_t>(file.dimension_length("N"));
231
232 io::write_attributes(file, global_attributes, io::PISM_DOUBLE);
233
234 {
235 VariableMetadata err("N", sys);
236 io::define_timeseries(err, "N", file, io::PISM_DOUBLE);
237 io::write_timeseries(file, err, start, { (double)(start + 1) });
238 }
239 {
240 VariableMetadata dx{"dx", sys};
241 dx.units("meters");
243 io::write_timeseries(file, dx, start, { m_grid->dx() });
244 }
245 {
246 VariableMetadata dy{"dy", sys};
247 dy.units("meters");
249 io::write_timeseries(file, dy, start, { m_grid->dy() });
250 }
251 {
252 VariableMetadata test{"test", sys};
253 test.units("1");
254 io::define_timeseries(test, "N", file, io::PISM_INT);
255 io::write_timeseries(file, test, start, { (double)testname[0] });
256 }
257 {
258 VariableMetadata max_velocity{ "max_velocity", sys };
259 max_velocity.long_name("maximum ice velocity magnitude error").units("m year^-1");
260 io::define_timeseries(max_velocity, "N", file, io::PISM_DOUBLE);
261 io::write_timeseries(file, max_velocity, start, { max_vector });
262 }
263 {
264 VariableMetadata rel_velocity{ "relative_velocity", sys };
265 rel_velocity.long_name("relative ice velocity magnitude error").units("percent");
266 io::define_timeseries(rel_velocity, "N", file, io::PISM_DOUBLE);
267 io::write_timeseries(file, rel_velocity, start, { rel_vector });
268 }
269 {
270 VariableMetadata maximum_u{ "maximum_u", sys };
271 maximum_u.long_name("maximum error in the X-component of the ice velocity").units("m year^-1");
272 io::define_timeseries(maximum_u, "N", file, io::PISM_DOUBLE);
273 io::write_timeseries(file, maximum_u, start, { max_u });
274 }
275 {
276 VariableMetadata maximum_v{ "maximum_v", sys };
277 maximum_v.long_name("maximum error in the Y-component of the ice velocity").units("m year^-1");
278 io::define_timeseries(maximum_v, "N", file, io::PISM_DOUBLE);
279 io::write_timeseries(file, maximum_v, start, { max_v });
280 }
281 {
282 VariableMetadata average_u{ "average_u", sys };
283 average_u.long_name("average error in the X-component of the ice velocity").units("m year^-1");
284 io::define_timeseries(average_u, "N", file, io::PISM_DOUBLE);
285 io::write_timeseries(file, average_u, start, { avg_u });
286 }
287 {
288 VariableMetadata average_v{ "average_v", sys };
289 average_v.long_name("average error in the Y-component of the ice velocity").units("m year^-1");
290 io::define_timeseries(average_v, "N", file, io::PISM_DOUBLE);
291 io::write_timeseries(file, average_v, start, { avg_v });
292 }
293 file.close();
294}
295
296void SSATestCase::exactSolution(int /*i*/, int /*j*/, double /*x*/, double /*y*/, double *u,
297 double *v) {
298 *u = 0;
299 *v = 0;
300}
301
302//! Save the computation and data to a file.
303void SSATestCase::write(const std::string &filename) {
304
305 // Write results to an output file:
307 io::define_time(file, *m_grid->ctx());
308 io::append_time(file, *m_config, 0.0);
309
312 m_bc_mask.write(file);
313 m_tauc.write(file);
315 m_ice_enthalpy.write(file);
316 m_bc_values.write(file);
317
318 m_ssa->velocity().write(file);
319
320 // write all diagnostics:
321 {
322 auto diagnostics = m_ssa->diagnostics();
323
324 for (auto &p : diagnostics) {
325 try {
326 p.second->compute()->write(file);
327 } catch (RuntimeError &e) {
328 // ignore errors
329 }
330 }
331 }
332
333 array::Vector tmp(m_grid, "_exact");
334 tmp.metadata(0)
335 .long_name("X-component of the SSA exact solution")
336 .units("m s^-1");
337 tmp.metadata(1)
338 .long_name("Y-component of the SSA exact solution")
339 .units("m s^-1");
340
341 array::AccessScope list(tmp);
342 for (auto p = m_grid->points(); p; p.next()) {
343 const int i = p.i(), j = p.j();
344
345 exactSolution(i, j, m_grid->x(i), m_grid->y(j),
346 &(tmp(i,j).u), &(tmp(i,j).v));
347 }
348 tmp.write(file);
349
350 tmp.metadata(0)
351 .set_name("u_error")
352 .long_name("X-component of the error (exact - computed)")
353 .units("m s^-1");
354 tmp.metadata(1)
355 .set_name("v_error")
356 .long_name("Y-component of the error (exact - computed)")
357 .units("m s^-1");
358
359 tmp.add(-1.0, m_ssa->velocity());
360 tmp.write(file);
361
362 array::Scalar error_mag(m_grid, "error_mag");
363 error_mag.metadata(0).long_name("magnitude of the error").units("m s^-1");
364 array::compute_magnitude(tmp, error_mag);
365 error_mag.write(file);
366
367 file.close();
368}
369
370} // end of namespace stressbalance
371} // end of namespace pism
void close()
Definition File.cc:237
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:55
void ensure_consistency(double ice_free_thickness_threshold)
Definition Geometry.cc:114
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
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & set_name(const std::string &name)
VariableMetadata & output_units(const std::string &input)
units::System::Ptr unit_system() const
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:64
void add(double alpha, const Array2D< T > &x)
Definition Array2D.hh:65
void set_interpolation_type(InterpolationType type)
Definition Array.cc:178
void write(const std::string &filename) const
Definition Array.cc:722
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:629
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition Array.cc:476
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
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)
const Config::ConstPtr m_config
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
void append_time(const File &file, const Config &config, double time_seconds)
Prepare a file for output.
@ PISM_NETCDF3
Definition IO_Flags.hh:57
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition IO_Flags.hh:74
@ PISM_READWRITE
open an existing file for reading and writing
Definition IO_Flags.hh:70
void write_attributes(const File &file, const VariableMetadata &variable, io::Type nctype)
Write variable attributes to a NetCDF file.
@ PISM_DOUBLE
Definition IO_Flags.hh:52
void write_timeseries(const File &file, const VariableMetadata &metadata, size_t t_start, const std::vector< double > &data)
Write a time-series data to a file.
void define_time(const File &file, const Context &ctx)
Prepare a file for output.
void define_timeseries(const VariableMetadata &var, const std::string &dimension_name, const File &file, io::Type nctype)
Define a NetCDF variable corresponding to a time-series.
bool Bool(const std::string &option, const std::string &description)
Definition options.cc:190
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)