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
pism_utilities.cc
Go to the documentation of this file.
1/* Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 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
20#include "pism/util/pism_utilities.hh"
21
22#include <cstdarg> // va_list, va_start(), va_end()
23#include <sstream> // istringstream, ostringstream
24#include <cstdio> // vsnprintf
25#include <cassert> // assert
26
27#include <mpi.h> // MPI_Get_library_version
28#include <fftw3.h> // fftw_version
29#include <gsl/gsl_version.h> // GSL_VERSION
30
31#include "pism/pism_config.hh" // Pism_USE_XXX, version info
32
33// The following is a stupid kludge necessary to make NetCDF 4.x work in
34// serial mode in an MPI program:
35#ifndef MPI_INCLUDED
36#define MPI_INCLUDED 1
37#endif
38#include <netcdf.h> // nc_inq_libvers
39#ifdef NC_HAVE_META_H
40#include <netcdf_meta.h> // NC_VERSION_MAJOR, etc
41#endif
42
43#if (Pism_USE_PROJ==1)
44#include "pism/util/Proj.hh" // pj_release
45#endif
46
47#if (Pism_USE_JANSSON==1)
48#include <jansson.h> // JANSSON_VERSION
49#endif
50
51#include <petsctime.h> // PetscTime
52
53#include <cstdlib> // strtol(), strtod()
54
55#include "pism/util/error_handling.hh"
56
57namespace pism {
58
59std::string string_strip(const std::string &input) {
60 if (input.empty()) {
61 return "";
62 }
63
64 std::string tmp = input;
65
66 // strip leading spaces
67 tmp.erase(0, tmp.find_first_not_of(" \t"));
68
69 // strip trailing spaces
70 tmp.substr(tmp.find_last_not_of(" \t"));
71
72 return tmp;
73}
74
75//! Returns true if `str` ends with `suffix` and false otherwise.
76bool ends_with(const std::string &str, const std::string &suffix) {
77 if (suffix.size() > str.size()) {
78 return false;
79 }
80
81 return (str.rfind(suffix) + suffix.size() == str.size());
82}
83
84template <class T>
85std::string join_impl(const T& input, const std::string& separator) {
86 if (input.empty()) {
87 return "";
88 }
89
90 auto j = input.begin();
91 std::string result = *j;
92 ++j;
93 while (j != input.end()) {
94 result += separator + *j;
95 ++j;
96 }
97 return result;
98}
99
100//! Concatenate `strings`, inserting `separator` between elements.
101std::string join(const std::vector<std::string> &strings, const std::string &separator) {
102 return join_impl(strings, separator);
103}
104
105std::string set_join(const std::set<std::string> &input, const std::string& separator) {
106 return join_impl(input, separator);
107}
108
109/*!
110 * Replace all occurrences of the character `from` with `to` and return the resulting string.
111 *
112 * We could use std::regex_replace(), but this will do for now.
113 */
114std::string replace_character(const std::string &input, char from, char to) {
115 std::string result;
116 for (const char &c : input) {
117 result += c == from ? to : c;
118 }
119 return result;
120}
121
122
123//! Transform a `separator`-separated list (a string) into a vector of strings.
124std::vector<std::string> split(const std::string &input, char separator) {
125 std::istringstream input_list(input);
126 std::string token;
127 std::vector<std::string> result;
128
129 while (getline(input_list, token, separator)) {
130 if (not token.empty()) {
131 result.emplace_back(token);
132 }
133 }
134 return result;
135}
136
137//! Transform a `separator`-separated list (a string) into a set of strings.
138std::set<std::string> set_split(const std::string &input, char separator) {
139 std::set<std::string> result;
140 for (const auto &token : split(input, separator)) {
141 result.insert(token);
142 }
143 return result;
144}
145
146//! Checks if a vector of doubles is strictly increasing.
147bool is_increasing(const std::vector<double> &a) {
148 int len = (int)a.size();
149 for (int k = 0; k < len-1; k++) {
150 if (a[k] >= a[k+1]) {
151 return false;
152 }
153 }
154 return true;
155}
156
157bool member(const std::string &string, const std::set<std::string> &set) {
158 return (set.find(string) != set.end());
159}
160
161void GlobalReduce(MPI_Comm comm, double *local, double *result, int count, MPI_Op op) {
162 assert(local != result);
163 int err = MPI_Allreduce(local, result, count, MPI_DOUBLE, op, comm);
164 PISM_C_CHK(err, 0, "MPI_Allreduce");
165}
166
167void GlobalReduce(MPI_Comm comm, int *local, int *result, int count, MPI_Op op) {
168 assert(local != result);
169 int err = MPI_Allreduce(local, result, count, MPI_INT, op, comm);
170 PISM_C_CHK(err, 0, "MPI_Allreduce");
171}
172
173void GlobalMin(MPI_Comm comm, double *local, double *result, int count) {
174 GlobalReduce(comm, local, result, count, MPI_MIN);
175}
176
177void GlobalMax(MPI_Comm comm, double *local, double *result, int count) {
178 GlobalReduce(comm, local, result, count, MPI_MAX);
179}
180
181void GlobalMax(MPI_Comm comm, int *local, int *result, int count) {
182 GlobalReduce(comm, local, result, count, MPI_MAX);
183}
184
185void GlobalSum(MPI_Comm comm, double *local, double *result, int count) {
186 GlobalReduce(comm, local, result, count, MPI_SUM);
187}
188
189void GlobalSum(MPI_Comm comm, int *local, int *result, int count) {
190 GlobalReduce(comm, local, result, count, MPI_SUM);
191}
192
193unsigned int GlobalSum(MPI_Comm comm, unsigned int input) {
194 unsigned int result;
195 int err = MPI_Allreduce(&input, &result, 1, MPI_UNSIGNED, MPI_SUM, comm);
196 PISM_C_CHK(err, 0, "MPI_Allreduce");
197 return result;
198}
199
200int GlobalSum(MPI_Comm comm, int input) {
201 int result;
202 int err = MPI_Allreduce(&input, &result, 1, MPI_INT, MPI_SUM, comm);
203 PISM_C_CHK(err, 0, "MPI_Allreduce");
204 return result;
205}
206
207double GlobalMin(MPI_Comm comm, double local) {
208 double result;
209 GlobalMin(comm, &local, &result, 1);
210 return result;
211}
212
213double GlobalMax(MPI_Comm comm, double local) {
214 double result;
215 GlobalMax(comm, &local, &result, 1);
216 return result;
217}
218
219double GlobalSum(MPI_Comm comm, double local) {
220 double result;
221 GlobalSum(comm, &local, &result, 1);
222 return result;
223}
224
225static const int TEMPORARY_STRING_LENGTH = 32768;
226
227std::string version() {
228 char buffer[TEMPORARY_STRING_LENGTH];
229 std::string result;
230
231 result += pism::printf("PISM (%s)\n", pism::revision);
232 result += pism::printf("CMake %s.\n", pism::cmake_version);
233
234 PetscGetVersion(buffer, TEMPORARY_STRING_LENGTH);
235 result += buffer;
236 result += "\n";
237 result += pism::printf("PETSc configure: %s\n", pism::petsc_configure_flags);
238
239 // OpenMPI added MPI_Get_library_version in version 1.7 (relatively recently).
240#ifdef OPEN_MPI
241 result += pism::printf("OpenMPI %d.%d.%d\n",
242 OMPI_MAJOR_VERSION,
243 OMPI_MINOR_VERSION,
244 OMPI_RELEASE_VERSION);
245#else
246 // Assume that other MPI libraries implement this part of the MPI-3 standard...
247 int string_length = TEMPORARY_STRING_LENGTH;
248 MPI_Get_library_version(buffer, &string_length);
249 result += buffer;
250#endif
251
252 result += pism::printf("NetCDF %s.\n", nc_inq_libvers());
253 result += pism::printf("FFTW %s.\n", fftw_version);
254 result += pism::printf("GSL %s.\n", GSL_VERSION);
255
256#if (Pism_USE_PROJ==1)
257 result += pism::printf("PROJ %s.\n", pj_release);
258#endif
259
260#if (Pism_USE_JANSSON==1)
261 result += pism::printf("Jansson %s.\n", JANSSON_VERSION);
262#endif
263
264#if (Pism_BUILD_PYTHON_BINDINGS==1)
265 result += pism::printf("SWIG %s.\n", pism::swig_version);
266 result += pism::printf("petsc4py %s.\n", pism::petsc4py_version);
267#endif
268
269 return result;
270}
271
273#ifdef NC_HAVE_META_H
274 return 100 * NC_VERSION_MAJOR + 10 * NC_VERSION_MINOR + NC_VERSION_PATCH;
275#else
276 return 0; // unknown
277#endif
278}
279
280
281//! Return time since the beginning of the run, in hours.
282double wall_clock_hours(MPI_Comm com, double start_time) {
283 const double seconds_per_hour = 3600.0;
284
285 return (get_time(com) - start_time) / seconds_per_hour;
286}
287
288//! Creates a time-stamp used for the history NetCDF attribute.
289std::string timestamp(MPI_Comm com) {
290 int rank = 0;
291 MPI_Comm_rank(com, &rank);
292
293 char date_str[50];
294 if (rank == 0) {
295 time_t now;
296 tm tm_now;
297 now = time(NULL);
298 localtime_r(&now, &tm_now);
299 // Format specifiers for strftime():
300 // %F = ISO date format, %T = Full 24 hour time, %Z = Time Zone name
301 strftime(date_str, sizeof(date_str), "%F %T %Z", &tm_now);
302 }
303
304 MPI_Bcast(date_str, 50, MPI_CHAR, 0, com);
305
306 return std::string(date_str);
307}
308
309//! Creates a string with the user name, hostname and the time-stamp (for history strings).
310std::string username_prefix(MPI_Comm com) {
311 PetscErrorCode ierr;
312
313 char username[50];
314 ierr = PetscGetUserName(username, sizeof(username));
315 PISM_CHK(ierr, "PetscGetUserName");
316 if (ierr != 0) {
317 username[0] = '\0';
318 }
319 char hostname[100];
320 ierr = PetscGetHostName(hostname, sizeof(hostname));
321 PISM_CHK(ierr, "PetscGetHostName");
322 if (ierr != 0) {
323 hostname[0] = '\0';
324 }
325
326 auto time = timestamp(com);
327 std::string result = pism::printf("%s@%s %s: ", username, hostname, time.c_str());
328
329 unsigned int length = result.size();
330 MPI_Bcast(&length, 1, MPI_UNSIGNED, 0, com);
331
332 result.resize(length);
333 MPI_Bcast(&result[0], length, MPI_CHAR, 0, com);
334
335 return result;
336}
337
338//! \brief Uses argc and argv to create the string with current PISM
339//! command-line arguments.
340std::string args_string() {
341 int argc;
342 char **argv;
343 PetscErrorCode ierr = PetscGetArgs(&argc, &argv);
344 PISM_CHK(ierr, "PetscGetArgs");
345
346 std::string cmdstr;
347 std::string argument;
348 for (int j = 0; j < argc; j++) {
349 argument = argv[j];
350
351 // enclose arguments containing spaces with double quotes:
352 if (argument.find(' ') != std::string::npos) {
353 argument = "\"" + argument + "\"";
354 }
355
356 cmdstr += std::string(" ") + argument;
357 }
358 cmdstr += "\n";
359
360 return cmdstr;
361}
362
363//! \brief Adds a suffix to a filename.
364/*!
365 * Returns filename + separator + suffix + .nc if the original filename had the
366 * .nc suffix, otherwise filename + separator. If the old filename had the form
367 * "name + separator + more stuff + .nc", then removes the string after the
368 * separator.
369 */
370std::string filename_add_suffix(const std::string &filename, const std::string &separator,
371 const std::string &suffix) {
372 std::string basename = filename;
373 std::string result;
374
375 // find where the separator begins:
376 std::string::size_type j = basename.rfind(separator);
377 if (j == std::string::npos) {
378 j = basename.rfind(".nc");
379 }
380
381 // if the separator was not found, find the .nc suffix:
382 if (j == std::string::npos) {
383 j = basename.size();
384 }
385
386 // cut off everything starting from the separator (or the .nc suffix):
387 basename.resize(static_cast<int>(j));
388
389 result = basename + separator + suffix;
390
391 if (ends_with(filename, ".nc")) {
392 result += ".nc";
393 }
394
395 return result;
396}
397
398double get_time(MPI_Comm comm) {
399 int rank = 0;
400 double result = 0.0;
401
402 MPI_Comm_rank(comm, &rank);
403
404 ParallelSection rank0(comm);
405 try {
406 if (rank == 0) {
407 PetscLogDouble tmp;
408 PetscErrorCode ierr = PetscTime(&tmp); PISM_CHK(ierr, "PetscTime");
409 result = tmp;
410 }
411 } catch (...) {
412 rank0.failed();
413 }
414 rank0.check();
415
416 MPI_Bcast(&result, 1, MPI_DOUBLE, 0, comm);
417
418 return result;
419}
420
421std::string printf(const char *format, ...) {
422 std::string result(1024, ' ');
423 va_list arglist;
424 size_t length;
425
426 va_start(arglist, format);
427 if((length = vsnprintf(&result[0], result.size(), format, arglist)) > result.size()) {
428 result.reserve(length);
429 vsnprintf(&result[0], result.size(), format, arglist);
430 }
431 va_end(arglist);
432 return result.substr(0, length);
433}
434
435/*!
436 * Validate a format string. In this application a format string should contain `%` exactly
437 * once, followed by `s` (i.e. `%s`).
438 *
439 * Throws RuntimeError if the provided string is invalid.
440 */
441void validate_format_string(const std::string &format) {
442 if (format.find("%s") == std::string::npos) {
443 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "format string %s does not contain %%s",
444 format.c_str());
445 }
446
447 if (format.find('%') != format.rfind('%')) {
448 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "format string %s contains more than one %%",
449 format.c_str());
450 }
451}
452
453double vector_min(const std::vector<double> &input) {
454 double my_min = input[0];
455 for (auto x : input) {
456 my_min = std::min(x, my_min);
457 }
458 return my_min;
459}
460
461double vector_max(const std::vector<double> &input) {
462 double my_max = input[0];
463 for (auto x : input) {
464 my_max = std::max(x, my_max);
465 }
466 return my_max;
467}
468
469
470/*!
471 * Fletcher's checksum
472 *
473 * See https://en.wikipedia.org/wiki/Fletcher%27s_checksum#Optimizations
474 */
475uint64_t fletcher64(const uint32_t *data, size_t length) {
476 // Accumulating a sum of block_size unsigned 32-bit integers in an unsigned 64-bit
477 // integer will not lead to an overflow.
478 //
479 // This constant is found by solving n * (n + 1) / 2 * (2^32 - 1) < (2^64 - 1).
480 static const size_t block_size = 92681;
481
482 uint64_t c0 = 0;
483 uint64_t c1 = 0;
484 while (length != 0) {
485 size_t block = std::min(block_size, length);
486
487 for (size_t i = 0; i < block; ++i) {
488 c0 = c0 + *data++;
489 c1 = c1 + c0;
490 }
491
492 c0 = c0 % UINT32_MAX;
493 c1 = c1 % UINT32_MAX;
494
495 length = length > block_size ? length - block_size : 0;
496 }
497 return (c1 << 32 | c0);
498}
499
500/*!
501 * Call fletcher64() to compute the checksum and print it.
502 */
503void print_checksum(MPI_Comm com,
504 const std::vector<double> &data,
505 const char *label) {
506 int rank = 0;
507 MPI_Comm_rank(com, &rank);
508
509 uint64_t sum = fletcher64((uint32_t*)data.data(), data.size() * 2);
510
511 PetscPrintf(PETSC_COMM_SELF, "[%d] %s: %016llx\n",
512 rank, label, (unsigned long long int)sum);
513}
514
515void print_vector(MPI_Comm com,
516 const std::vector<double> &data,
517 const char *label) {
518 int rank = 0;
519 MPI_Comm_rank(com, &rank);
520
521 std::vector<std::string> tmp;
522 tmp.reserve(data.size());
523 for (const auto &f : data) {
524 tmp.emplace_back(pism::printf("%f", f));
525 }
526
527 auto str = join(tmp, ",");
528
529 PetscPrintf(PETSC_COMM_SELF, "[%d] %s: %s\n",
530 rank, label, str.c_str());
531}
532
533void print_vector(MPI_Comm com,
534 const std::vector<int> &data,
535 const char *label) {
536 int rank = 0;
537 MPI_Comm_rank(com, &rank);
538
539 std::vector<std::string> tmp;
540 tmp.reserve(data.size());
541 for (const auto &f : data) {
542 tmp.emplace_back(pism::printf("%d", f));
543 }
544
545 auto str = join(tmp, ",");
546
547 PetscPrintf(PETSC_COMM_SELF, "[%d] %s: %s\n",
548 rank, label, str.c_str());
549}
550
551/*!
552 * Compute water column pressure vertically-averaged over the height of an ice cliff at a
553 * margin.
554 */
555double average_water_column_pressure(double ice_thickness, double bed,
556 double floatation_level, double rho_ice,
557 double rho_water, double g) {
558
559 double ice_bottom = std::max(bed, floatation_level - rho_ice / rho_water * ice_thickness);
560 double water_column_height = std::max(floatation_level - ice_bottom, 0.0);
561
562 if (ice_thickness > 0.0) {
563 return 0.5 * rho_water * g * pow(water_column_height, 2.0) / ice_thickness;
564 }
565 return 0.0;
566}
567
568double parse_number(const std::string &input) {
569 char *endptr = NULL;
570 double result = strtod(input.c_str(), &endptr);
571 if (*endptr != '\0') {
573 "Can't parse %s (expected a floating point number)",
574 input.c_str());
575 }
576 return result;
577}
578
579std::vector<double> parse_number_list(const std::string &input) {
580 std::vector<double> result;
581
582 for (const auto &p : split(input, ',')) {
583 result.push_back(parse_number(p));
584 }
585 return result;
586}
587
588long int parse_integer(const std::string &input) {
589 char *endptr = NULL;
590 long int result = strtol(input.c_str(), &endptr, 10);
591 if (*endptr != '\0') {
593 "Can't parse %s (expected an integer)",
594 input.c_str());
595 }
596 return result;
597}
598
599std::vector<long> parse_integer_list(const std::string &input) {
600 std::vector<long> result;
601
602 for (const auto &p : split(input, ',')) {
603 result.push_back(parse_integer(p));
604 }
605 return result;
606}
607
608} // end of namespace pism
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
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
#define PISM_C_CHK(errcode, success, name)
const double rho_ice
Definition exactTestK.c:31
double get_time(MPI_Comm comm)
bool is_increasing(const std::vector< double > &a)
Checks if a vector of doubles is strictly increasing.
double parse_number(const std::string &input)
double average_water_column_pressure(double ice_thickness, double bed, double floatation_level, double rho_ice, double rho_water, double g)
double wall_clock_hours(MPI_Comm com, double start_time)
Return time since the beginning of the run, in hours.
std::vector< long > parse_integer_list(const std::string &input)
void print_checksum(MPI_Comm com, const std::vector< double > &data, const char *label)
static const double g
Definition exactTestP.cc:36
bool ends_with(const std::string &str, const std::string &suffix)
Returns true if str ends with suffix and false otherwise.
std::vector< double > parse_number_list(const std::string &input)
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
std::string set_join(const std::set< std::string > &input, const std::string &separator)
std::string printf(const char *format,...)
int netcdf_version()
return NetCDF version as an integer
void print_vector(MPI_Comm com, const std::vector< double > &data, const char *label)
uint64_t fletcher64(const uint32_t *data, size_t length)
static const double k
Definition exactTestP.cc:42
void validate_format_string(const std::string &format)
std::set< std::string > set_split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a set of strings.
double vector_min(const std::vector< double > &input)
std::string string_strip(const std::string &input)
std::string filename_add_suffix(const std::string &filename, const std::string &separator, const std::string &suffix)
Adds a suffix to a filename.
std::string version()
std::string timestamp(MPI_Comm com)
Creates a time-stamp used for the history NetCDF attribute.
bool member(const std::string &string, const std::set< std::string > &set)
static const double c1
Definition exactTestP.cc:44
void GlobalMin(MPI_Comm comm, double *local, double *result, int count)
static double start_time(const Config &config, const File *file, const std::string &reference_date, const std::string &calendar, const units::Unit &time_units)
Definition Time.cc:349
std::string args_string()
Uses argc and argv to create the string with current PISM command-line arguments.
long int parse_integer(const std::string &input)
static const int TEMPORARY_STRING_LENGTH
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
std::string username_prefix(MPI_Comm com)
Creates a string with the user name, hostname and the time-stamp (for history strings).
std::string join_impl(const T &input, const std::string &separator)
void GlobalReduce(MPI_Comm comm, double *local, double *result, int count, MPI_Op op)
std::string replace_character(const std::string &input, char from, char to)
double vector_max(const std::vector< double > &input)
std::vector< std::string > split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a vector of strings.
int count
Definition test_cube.c:16