PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
output_helpers.cc
Go to the documentation of this file.
1/* Copyright (C) 2025, 2026 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 <set>
21
22#include "pism/util/error_handling.hh"
23#include "pism/util/io/io_helpers.hh"
24#include "pism/util/VariableMetadata.hh"
25#include "pism/util/io/OutputWriter.hh"
26#include "pism/util/pism_utilities.hh"
27#include "pism/util/Config.hh"
28
29namespace pism {
30namespace io {
31
33 const std::set<VariableMetadata> &variables,
34 const VariableMetadata &mapping,
35 bool use_internal_units) {
36
37 std::string mapping_variable_name{};
38 if (mapping.has_attributes()) {
39 mapping_variable_name = mapping.get_name();
40 }
41
42 std::set<std::string> variable_names;
43 for (const auto &v : variables) {
44 variable_names.insert(v.get_name());
45 }
46
47 for (auto var : variables) {
48 if (var.get_name() == "PISM_GLOBAL") {
49 file.append_history(var["history"]);
50
51 // clear the history attribute
52 auto tmp = var;
53 tmp["history"] = "";
54 file.set_global_attributes(tmp.all_strings(), tmp.all_doubles());
55
56 continue;
57 }
58
59 if (use_internal_units) {
60 var.output_units(var["units"]);
61 }
62
63 auto var_name = var.get_name();
64
65 if (var_name == "lat" and set_member("lat_bnds", variable_names)) {
66 var["bounds"] = "lat_bnds";
67 }
68 if (var_name == "lon" and set_member("lon_bnds", variable_names)) {
69 var["bounds"] = "lon_bnds";
70 }
71
72 // add extra attributes such as "grid_mapping" and "coordinates". Variables lat, lon,
73 // lat_bnds, and lon_bnds should not have these attributes to support CDO (see issue
74 // #384).
75 //
76 // We check names of x and y dimensions to avoid setting extra attributes for variables
77 // that use a different grid (e.g. viscous_bed_displacement written by the Lingle-Clark
78 // bed deformation model).
79 bool have_lat_lon = set_member("lat", variable_names) and set_member("lon", variable_names);
80 auto dim_names = var.dimension_names();
81 if (vector_member("x", dim_names) and vector_member("y", dim_names)) {
82 if (have_lat_lon and not set_member(var_name, { "lat_bnds", "lon_bnds", "lat", "lon" })) {
83 var["coordinates"] = "lat lon";
84 }
85
86 if (not mapping_variable_name.empty()) {
87 var["grid_mapping"] = mapping_variable_name;
88 }
89 }
90
91 file.define_variable(var);
92 } // end of the loop over variables
93}
94
95void write_config(const Config &config, const std::string &variable_name, const OutputFile &file) {
96
97 std::string data = config.json();
98
99 if ((int)data.size() + 1 > Config::max_length) {
102 "unable to save configuration parameters to a file: JSON string length exceeds %d",
104 }
105
106 std::vector<unsigned int> start = { 0 };
107 std::vector<unsigned int> count = { (unsigned int)data.size() + 1 };
108
109 if (not config.get_string("output.experiment_id").empty()) {
110 start.insert(start.cbegin(), 0);
111 count.insert(count.cbegin(), 1);
112 }
113 file.write_text(variable_name, start, count, data);
114}
115
116
117//! \brief Moves the file aside (file.nc -> file.nc~).
118/*!
119 * Note: only one processor does the renaming.
120 */
121void move_if_exists(MPI_Comm com, const std::string &file_to_move, int rank_to_use) {
122 int stat = 0, rank = 0;
123 MPI_Comm_rank(com, &rank);
124 std::string backup_filename = file_to_move + "~";
125
126 if (rank == rank_to_use) {
127 bool exists = false;
128
129 // Check if the file exists:
130 if (FILE *f = fopen(file_to_move.c_str(), "r")) {
131 fclose(f);
132 exists = true;
133 } else {
134 exists = false;
135 }
136
137 if (exists) {
138 stat = rename(file_to_move.c_str(), backup_filename.c_str());
139 }
140 } // end of "if (rank == rank_to_use)"
141
142 int global_stat = 0;
143 MPI_Allreduce(&stat, &global_stat, 1, MPI_INT, MPI_SUM, com);
144
145 if (global_stat != 0) {
146 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "PISM ERROR: can't move '%s' to '%s'",
147 file_to_move.c_str(), backup_filename.c_str());
148 }
149}
150
151//! \brief Check if a file is present are remove it.
152/*!
153 * Note: only processor 0 does the job.
154 */
155void remove_if_exists(MPI_Comm com, const std::string &file_to_remove, int rank_to_use) {
156 int stat = 0, rank = 0;
157 MPI_Comm_rank(com, &rank);
158
159 if (rank == rank_to_use) {
160 bool exists = false;
161
162 // Check if the file exists:
163 if (FILE *f = fopen(file_to_remove.c_str(), "r")) {
164 fclose(f);
165 exists = true;
166 } else {
167 exists = false;
168 }
169
170 if (exists) {
171 stat = remove(file_to_remove.c_str());
172 }
173 } // end of "if (rank == rank_to_use)"
174
175 int global_stat = 0;
176 MPI_Allreduce(&stat, &global_stat, 1, MPI_INT, MPI_SUM, com);
177
178 if (global_stat != 0) {
179 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "PISM ERROR: can't remove '%s'",
180 file_to_remove.c_str());
181 }
182}
183
184} // namespace io
185} // namespace pism
std::string json() const
Definition Config.cc:891
std::string get_string(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
Definition Config.cc:322
static int max_length
Maximum length of the JSON string (for writing to output files)
Definition Config.hh:73
A class for storing and accessing PISM configuration flags and parameters.
Definition Config.hh:56
void set_global_attributes(const std::map< std::string, std::string > &strings, const std::map< std::string, std::vector< double > > &numbers) const
Definition OutputFile.cc:35
void append_history(const std::string &text) const
Definition OutputFile.cc:45
void write_text(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::string &input) const
Definition OutputFile.cc:68
void define_variable(const VariableMetadata &variable) const
Definition OutputFile.cc:31
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
std::string get_name() const
#define PISM_ERROR_LOCATION
void write_config(const Config &config, const std::string &variable_name, const OutputFile &file)
void move_if_exists(MPI_Comm com, const std::string &file_to_move, int rank_to_use=0)
Moves the file aside (file.nc -> file.nc~).
void define_variables(const OutputFile &file, const std::set< VariableMetadata > &variables, const VariableMetadata &mapping, bool use_internal_units)
void remove_if_exists(MPI_Comm com, const std::string &file_to_remove, int rank_to_use=0)
Check if a file is present are remove it.
bool vector_member(const std::string &string, const std::vector< std::string > &vector)
bool set_member(const std::string &string, const std::set< std::string > &set)
int count
Definition test_cube.c:16