PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
SSATestCase.cc
Go to the documentation of this file.
1 // Copyright (C) 2009--2023 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/StressBalance.hh"
21 #include "pism/util/Context.hh"
22 #include "pism/util/interpolation.hh"
23 #include "pism/util/io/File.hh"
24 #include "pism/util/io/io_helpers.hh"
25 #include "pism/util/pism_options.hh"
26 #include "pism/util/pism_utilities.hh"
27 
28 namespace pism {
29 namespace stressbalance {
30 
31 SSATestCase::SSATestCase(std::shared_ptr<Context> ctx, int Mx, int My, double Lx, double Ly,
32  grid::Registration registration, grid::Periodicity periodicity)
33  : m_com(ctx->com()),
34  m_ctx(ctx),
35  m_config(ctx->config()),
36  m_grid(Grid::Shallow(m_ctx, Lx, Ly, 0.0, 0.0, Mx, My, registration, periodicity)),
37  m_sys(ctx->unit_system()),
38  m_stencil_width(m_config->get_number("grid.max_stencil_width")),
39  m_tauc(m_grid, "tauc"),
40  m_ice_enthalpy(m_grid, "enthalpy", array::WITH_GHOSTS, m_grid->z(), m_stencil_width),
41  m_bc_values(m_grid, "_bc"), // u_bc and v_bc
42  m_bc_mask(m_grid, "bc_mask"),
43  m_geometry(m_grid),
44  m_ssa(NULL) {
46 
47  // yield stress for basal till (plastic or pseudo-plastic model)
48  m_tauc.metadata(0)
49  .long_name("yield stress for basal till (plastic or pseudo-plastic model)")
50  .units("Pa");
51 
52  // enthalpy
54  .long_name("ice enthalpy (includes sensible heat, latent heat, pressure)")
55  .units("J kg-1");
56 
57  // dirichlet boundary condition (FIXME: perhaps unused!)
59  .long_name("X-component of the SSA velocity boundary conditions")
60  .units("m s-1")
61  .output_units("m year-1");
63  .long_name("Y-component of the SSA velocity boundary conditions")
64  .units("m s-1")
65  .output_units("m year-1");
66 
67  Config::ConstPtr config = m_grid->ctx()->config();
68  units::System::Ptr sys = m_grid->ctx()->unit_system();
69  double fill_value =
70  units::convert(sys, config->get_number("output.fill_value"), "m year-1", "m second-1");
71 
72  auto large_number = units::convert(m_sys, 1e6, "m year-1", "m second-1");
73 
74  m_bc_values.metadata(0)["valid_range"] = { -large_number, large_number };
75  m_bc_values.metadata(0)["_FillValue"] = { fill_value };
76 
77  m_bc_values.metadata(1)["valid_range"] = { -large_number, large_number };
78  m_bc_values.metadata(1)["_FillValue"] = { fill_value };
79 
80  m_bc_values.set(fill_value);
81 
82  // Dirichlet B.C. mask
83  m_bc_mask.metadata().long_name("grounded_dragging_floating integer mask");
84 
85  m_bc_mask.metadata()["flag_values"] = { 0.0, 1.0 };
86  m_bc_mask.metadata()["flag_meanings"] = "no_data ssa.dirichlet_bc_location";
87 }
88 
90  delete m_ssa;
91 }
92 
93 //! Initialize the test case at the start of a run
95 
96  m_ssa->init();
97 
98  // Allow the subclass to set the coefficients.
100 }
101 
102 //! Solve the SSA
104  m_ctx->log()->message(2, "* Solving the SSA stress balance ...\n");
105 
106  m_geometry.ensure_consistency(m_config->get_number("stress_balance.ice_free_thickness_standard"));
107 
108  Inputs inputs;
109  inputs.water_column_pressure = nullptr;
110  inputs.geometry = &m_geometry;
111  inputs.enthalpy = &m_ice_enthalpy;
112  inputs.basal_yield_stress = &m_tauc;
113  inputs.bc_mask = &m_bc_mask;
114  inputs.bc_values = &m_bc_values;
115 
116  bool full_update = true;
117  m_ssa->update(inputs, full_update);
118 }
119 
120 //! Report on the generated solution
121 void SSATestCase::report(const std::string &testname) {
122 
123  m_ctx->log()->message(3, m_ssa->stdout_report());
124 
125  double maxvecerr = 0.0, avvecerr = 0.0, avuerr = 0.0, avverr = 0.0, maxuerr = 0.0, maxverr = 0.0;
126  double gmaxvecerr = 0.0, gavvecerr = 0.0, gavuerr = 0.0, gavverr = 0.0, gmaxuerr = 0.0,
127  gmaxverr = 0.0;
128 
129  if (m_config->get_flag("basal_resistance.pseudo_plastic.enabled") &&
130  m_config->get_number("basal_resistance.pseudo_plastic.q") != 1.0) {
131  m_ctx->log()->message(1, "WARNING: numerical errors not valid for pseudo-plastic till\n");
132  }
133  m_ctx->log()->message(1, "NUMERICAL ERRORS in velocity relative to exact solution:\n");
134 
135  const array::Vector &vel_ssa = m_ssa->velocity();
136 
137  array::AccessScope list{ &vel_ssa };
138 
139  double exactvelmax = 0, gexactvelmax = 0;
140  for (auto p = m_grid->points(); p; p.next()) {
141  const int i = p.i(), j = p.j();
142 
143  double uexact, vexact;
144  double myx = m_grid->x(i), myy = m_grid->y(j);
145 
146  exactSolution(i, j, myx, myy, &uexact, &vexact);
147 
148  double exactnormsq = sqrt(uexact * uexact + vexact * vexact);
149  exactvelmax = std::max(exactnormsq, exactvelmax);
150 
151  // compute maximum errors
152  const double uerr = fabs(vel_ssa(i, j).u - uexact);
153  const double verr = fabs(vel_ssa(i, j).v - vexact);
154  avuerr = avuerr + uerr;
155  avverr = avverr + verr;
156  maxuerr = std::max(maxuerr, uerr);
157  maxverr = std::max(maxverr, verr);
158  const double vecerr = sqrt(uerr * uerr + verr * verr);
159  maxvecerr = std::max(maxvecerr, vecerr);
160  avvecerr = avvecerr + vecerr;
161  }
162 
163  unsigned int N = (m_grid->Mx() * m_grid->My());
164 
165  gexactvelmax = GlobalMax(m_grid->com, exactvelmax);
166  gmaxuerr = GlobalMax(m_grid->com, maxuerr);
167  gmaxverr = GlobalMax(m_grid->com, maxverr);
168  gavuerr = GlobalSum(m_grid->com, avuerr);
169  gavuerr = gavuerr / N;
170  gavverr = GlobalSum(m_grid->com, avverr);
171  gavverr = gavverr / N;
172  gmaxvecerr = GlobalMax(m_grid->com, maxvecerr);
173  gavvecerr = GlobalSum(m_grid->com, avvecerr);
174  gavvecerr = gavvecerr / N;
175 
176  using pism::units::convert;
177 
178  m_ctx->log()->message(
179  1, "velocity : maxvector prcntavvec maxu maxv avu avv\n");
180  m_ctx->log()->message(1, " %11.4f%13.5f%10.4f%10.4f%10.4f%10.4f\n",
181  convert(m_sys, gmaxvecerr, "m second-1", "m year-1"),
182  (gavvecerr / gexactvelmax) * 100.0,
183  convert(m_sys, gmaxuerr, "m second-1", "m year-1"),
184  convert(m_sys, gmaxverr, "m second-1", "m year-1"),
185  convert(m_sys, gavuerr, "m second-1", "m year-1"),
186  convert(m_sys, gavverr, "m second-1", "m year-1"));
187 
188  m_ctx->log()->message(1, "NUM ERRORS DONE\n");
189 
190  report_netcdf(testname, convert(m_sys, gmaxvecerr, "m second-1", "m year-1"),
191  (gavvecerr / gexactvelmax) * 100.0,
192  convert(m_sys, gmaxuerr, "m second-1", "m year-1"),
193  convert(m_sys, gmaxverr, "m second-1", "m year-1"),
194  convert(m_sys, gavuerr, "m second-1", "m year-1"),
195  convert(m_sys, gavverr, "m second-1", "m year-1"));
196 }
197 
198 void SSATestCase::report_netcdf(const std::string &testname, double max_vector, double rel_vector,
199  double max_u, double max_v, double avg_u, double avg_v) {
200  auto sys = m_grid->ctx()->unit_system();
201 
202  VariableMetadata global_attributes("PISM_GLOBAL", sys);
203 
204  options::String filename("-report_file", "NetCDF error report file");
205 
206  if (not filename.is_set()) {
207  return;
208  }
209 
210  m_ctx->log()->message(2, "Also writing errors to '%s'...\n", filename->c_str());
211 
212  bool append = options::Bool("-append", "Append the NetCDF error report");
213 
215  if (not append) {
217  }
218 
219  global_attributes["source"] = std::string("PISM ") + pism::revision;
220 
221  // Find the number of records in this file:
222  File file(m_grid->com, filename, io::PISM_NETCDF3, mode); // OK to use NetCDF3.
223  size_t start = static_cast<size_t>(file.dimension_length("N"));
224 
225  io::write_attributes(file, global_attributes, io::PISM_DOUBLE);
226 
227  {
228  VariableMetadata err("N", sys);
229  io::define_timeseries(err, "N", file, io::PISM_DOUBLE);
230  io::write_timeseries(file, err, start, { (double)(start + 1) });
231  }
232  {
233  VariableMetadata dx{"dx", sys};
234  dx.units("meters");
235  io::define_timeseries(dx, "N", file, io::PISM_DOUBLE);
236  io::write_timeseries(file, dx, start, { m_grid->dx() });
237  }
238  {
239  VariableMetadata dy{"dy", sys};
240  dy.units("meters");
241  io::define_timeseries(dy, "N", file, io::PISM_DOUBLE);
242  io::write_timeseries(file, dy, start, { m_grid->dy() });
243  }
244  {
245  VariableMetadata test{"test", sys};
246  test.units("1");
247  io::define_timeseries(test, "N", file, io::PISM_INT);
248  io::write_timeseries(file, test, start, { (double)testname[0] });
249  }
250  {
251  VariableMetadata max_velocity{ "max_velocity", sys };
252  max_velocity.long_name("maximum ice velocity magnitude error").units("m year-1");
253  io::define_timeseries(max_velocity, "N", file, io::PISM_DOUBLE);
254  io::write_timeseries(file, max_velocity, start, { max_vector });
255  }
256  {
257  VariableMetadata rel_velocity{ "relative_velocity", sys };
258  rel_velocity.long_name("relative ice velocity magnitude error").units("percent");
259  io::define_timeseries(rel_velocity, "N", file, io::PISM_DOUBLE);
260  io::write_timeseries(file, rel_velocity, start, { rel_vector });
261  }
262  {
263  VariableMetadata maximum_u{ "maximum_u", sys };
264  maximum_u.long_name("maximum error in the X-component of the ice velocity").units("m year-1");
265  io::define_timeseries(maximum_u, "N", file, io::PISM_DOUBLE);
266  io::write_timeseries(file, maximum_u, start, { max_u });
267  }
268  {
269  VariableMetadata maximum_v{ "maximum_v", sys };
270  maximum_v.long_name("maximum error in the Y-component of the ice velocity").units("m year-1");
271  io::define_timeseries(maximum_v, "N", file, io::PISM_DOUBLE);
272  io::write_timeseries(file, maximum_v, start, { max_v });
273  }
274  {
275  VariableMetadata average_u{ "average_u", sys };
276  average_u.long_name("average error in the X-component of the ice velocity").units("m year-1");
277  io::define_timeseries(average_u, "N", file, io::PISM_DOUBLE);
278  io::write_timeseries(file, average_u, start, { avg_u });
279  }
280  {
281  VariableMetadata average_v{ "average_v", sys };
282  average_v.long_name("average error in the Y-component of the ice velocity").units("m year-1");
283  io::define_timeseries(average_v, "N", file, io::PISM_DOUBLE);
284  io::write_timeseries(file, average_v, start, { avg_v });
285  }
286  file.close();
287 }
288 
289 void SSATestCase::exactSolution(int /*i*/, int /*j*/, double /*x*/, double /*y*/, double *u,
290  double *v) {
291  *u = 0;
292  *v = 0;
293 }
294 
295 //! Save the computation and data to a file.
296 void SSATestCase::write(const std::string &filename) {
297 
298  // Write results to an output file:
299  File file(m_grid->com, filename, io::PISM_NETCDF3, io::PISM_READWRITE_MOVE);
300  io::define_time(file, *m_grid->ctx());
301  io::append_time(file, *m_config, 0.0);
302 
305  m_bc_mask.write(file);
306  m_tauc.write(file);
308  m_ice_enthalpy.write(file);
309  m_bc_values.write(file);
310 
311  m_ssa->velocity().write(file);
312 
313  array::Vector exact(m_grid, "_exact");
314  exact.metadata(0)
315  .long_name("X-component of the SSA exact solution")
316  .units("m s-1");
317  exact.metadata(1)
318  .long_name("Y-component of the SSA exact solution")
319  .units("m s-1");
320 
321  array::AccessScope list(exact);
322  for (auto p = m_grid->points(); p; p.next()) {
323  const int i = p.i(), j = p.j();
324 
325  exactSolution(i, j, m_grid->x(i), m_grid->y(j),
326  &(exact(i,j).u), &(exact(i,j).v));
327  }
328  exact.write(file);
329 
330  file.close();
331 }
332 
333 } // end of namespace stressbalance
334 } // end of namespace pism
std::shared_ptr< const Config > ConstPtr
void close()
Definition: File.cc:270
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition: File.cc:454
High-level PISM I/O class.
Definition: File.hh:56
void ensure_consistency(double ice_free_thickness_threshold)
Definition: Geometry.cc:112
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
Describes the PISM grid and the distribution of data across processors.
Definition: Grid.hh:282
VariableMetadata & long_name(const std::string &input)
VariableMetadata & output_units(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:65
void set_interpolation_type(InterpolationType type)
Definition: Array.cc:179
void write(const std::string &filename) const
Definition: Array.cc:800
void set(double c)
Result: v[j] <- c for all j.
Definition: Array.cc:707
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition: Array.cc:553
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
virtual void init()
Initialize the test case at the start of a run.
Definition: SSATestCase.cc:94
const units::System::Ptr m_sys
Definition: SSATestCase.hh:96
std::shared_ptr< Grid > m_grid
Definition: SSATestCase.hh:94
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)
Definition: SSATestCase.cc:198
virtual void exactSolution(int i, int j, double x, double y, double *u, double *v)
Definition: SSATestCase.cc:289
const std::shared_ptr< Context > m_ctx
Definition: SSATestCase.hh:91
SSATestCase(std::shared_ptr< Context > ctx, int Mx, int My, double Lx, double Ly, grid::Registration registration, grid::Periodicity periodicity)
Definition: SSATestCase.cc:31
virtual void report(const std::string &testname)
Report on the generated solution.
Definition: SSATestCase.cc:121
virtual void run()
Solve the SSA.
Definition: SSATestCase.cc:103
virtual void write(const std::string &filename)
Save the computation and data to a file.
Definition: SSATestCase.cc:296
virtual void update(const Inputs &inputs, bool full_update)
Update the SSA solution.
Definition: SSA.cc:152
virtual std::string stdout_report() const
Produce a report string for the standard output.
Definition: SSA.cc:379
const array::Vector1 & velocity() const
Get the thickness-advective 2D velocity.
std::shared_ptr< System > Ptr
Definition: Units.hh:47
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:165
@ WITH_GHOSTS
Definition: Array.hh:62
Periodicity
Definition: Grid.hh:51
Registration
Definition: Grid.hh:53
void append_time(const File &file, const Config &config, double time_seconds)
Prepare a file for output.
Definition: io_helpers.cc:247
@ 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:78
@ PISM_READWRITE
open an existing file for reading and writing
Definition: IO_Flags.hh:74
void write_attributes(const File &file, const VariableMetadata &variable, io::Type nctype)
Write variable attributes to a NetCDF file.
Definition: io_helpers.cc:1200
@ PISM_DOUBLE
Definition: IO_Flags.hh:52
@ PISM_INT
Definition: IO_Flags.hh:50
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.
Definition: io_helpers.cc:926
void define_time(const File &file, const Context &ctx)
Prepare a file for output.
Definition: io_helpers.cc:213
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.
Definition: io_helpers.cc:839
bool Bool(const std::string &option, const std::string &description)
Definition: options.cc:240
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)