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
output.cc
Go to the documentation of this file.
1// Copyright (C) 2004-2024 Jed Brown, Ed Bueler and Constantine Khroulev
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 <gsl/gsl_interp.h> // gsl_interp_bsearch()
20
21#include <algorithm>
22#include <set>
23
24#include "pism/icemodel/IceModel.hh"
25
26#include "pism/util/Grid.hh"
27#include "pism/util/ConfigInterface.hh"
28#include "pism/util/Diagnostic.hh"
29#include "pism/util/Time.hh"
30#include "pism/util/io/File.hh"
31
32#include "pism/util/io/io_helpers.hh"
33#include "pism/util/Profiling.hh"
34#include "pism/util/pism_utilities.hh"
35#include "pism/util/projection.hh"
36#include "pism/util/Component.hh"
37
38
39namespace pism {
40
41MaxTimestep reporting_max_timestep(const std::vector<double> &times, double t,
42 double resolution,
43 const std::string &description) {
44
45 const size_t N = times.size();
46 if (t >= times.back()) {
47 return MaxTimestep();
48 }
49
50 size_t j = 0;
51 double dt = 0.0;
52 if (t < times[0]) {
53 j = -1;
54 } else {
55 j = gsl_interp_bsearch(times.data(), t, 0, N - 1);
56 }
57
58 dt = times[j + 1] - t;
59
60 // now make sure that we don't end up taking a time-step of less than "resolution"
61 // second long
62 if (dt < resolution) {
63 if (j + 2 < N) {
64 return MaxTimestep(times[j + 2] - t, description);
65 }
66 return MaxTimestep(description);
67 }
68 return MaxTimestep(dt, description);
69}
70
71//! Write time-independent metadata to a file.
72void IceModel::write_metadata(const File &file, MappingTreatment mapping_flag,
73 HistoryTreatment history_flag) const {
74 if (mapping_flag == WRITE_MAPPING) {
75 write_mapping(file, m_grid->get_mapping_info());
76 }
77
78 m_config->write(file);
79
80 if (history_flag == PREPEND_HISTORY) {
82
83 std::string old_history = file.read_text_attribute("PISM_GLOBAL", "history");
84
85 tmp.set_name("PISM_GLOBAL");
86 tmp["history"] = std::string(tmp["history"]) + old_history;
87
89 } else {
91 }
92}
93
94//! Save model state in NetCDF format.
95/*!
96Calls save_variables() to do the actual work.
97 */
99 {
100 auto stats = run_stats();
101
102 auto str = pism::printf(
103 "PISM done. Performance stats: %.4f wall clock hours, %.4f proc.-hours, %.4f model years per proc.-hour.",
104 (double)stats["wall_clock_hours"],
105 (double)stats["processor_hours"],
106 (double)stats["model_years_per_processor_hour"]);
107
108 prepend_history(str);
109 }
110
111 std::string filename = m_config->get_string("output.file");
112
113 if (filename.empty()) {
114 m_log->message(2, "WARNING: output.file is empty. Using unnamed.nc instead.\n");
115 filename = "unnamed.nc";
116 }
117
118 if (not ends_with(filename, ".nc")) {
119 m_log->message(2,
120 "PISM WARNING: output file name does not have the '.nc' suffix!\n");
121 }
122
123 const Profiling &profiling = m_ctx->profiling();
124
125 profiling.begin("io.model_state");
126 if (m_config->get_string("output.size") != "none") {
127 m_log->message(2, "Writing model state to file `%s'...\n", filename.c_str());
128 File file(m_grid->com,
129 filename,
130 string_to_backend(m_config->get_string("output.format")),
132
134
135 write_run_stats(file, run_stats());
136
138 m_time->current());
139 }
140 profiling.end("io.model_state");
141}
142
143void write_run_stats(const File &file, const pism::VariableMetadata &stats) {
144 if (not file.variable_exists(stats.get_name())) {
145 file.define_variable(stats.get_name(), io::PISM_DOUBLE, {});
146 }
148}
149
151 OutputKind kind,
152 const std::set<std::string> &variables,
153 double time,
154 io::Type default_diagnostics_type) const {
155
156 // Compress 2D and 3D variables if output.compression_level > 0 and the output.format
157 // supports it.
158 file.set_compression_level(m_config->get_number("output.compression_level"));
159
160 // define the time dimension if necessary (no-op if it is already defined)
161 io::define_time(file, *m_grid->ctx());
162
163 // define the "timestamp" (wall clock time since the beginning of the run)
164 // Note: it is time-dependent, so we need to define time first.
165 VariableMetadata timestamp("timestamp", m_sys);
166 timestamp.long_name("wall-clock time since the beginning of the run").units("hours");
168 m_config->get_string("time.dimension_name"),
169 file, io::PISM_FLOAT);
170 // append to the time dimension
171 io::append_time(file, *m_config, time);
172
173 // Write metadata *before* everything else:
174 //
175 // FIXME: we should write this to variables instead of attributes because NetCDF-4 crashes after
176 // about 2^16 attribute modifications per variable. :-(
177 write_run_stats(file, run_stats());
178
179 if (kind == INCLUDE_MODEL_STATE) {
180 define_model_state(file);
181 }
182 define_diagnostics(file, variables, default_diagnostics_type);
183
184 // Done defining variables
185
186 {
187 // Note: we don't use "variables" (an argument of this method) here because it
188 // contains PISM's names of diagnostic quantities which (in some cases) map to more
189 // than one NetCDF variable. Moreover, here we're concerned with file contents, not
190 // the list of requested variables.
191 std::set<std::string> var_names;
192 unsigned int n_vars = file.nvariables();
193 for (unsigned int k = 0; k < n_vars; ++k) {
194 var_names.insert(file.variable_name(k));
195 }
196
197 auto grid_mapping_name = m_grid->get_mapping_info().cf_mapping.get_name();
198 bool set_grid_mapping = member(grid_mapping_name, var_names);
199
200 // If this output file contains variables lat and lon...
201 if (member("lat", var_names) and member("lon", var_names)) {
202
203 // add the coordinates attribute to all variables that use x and y dimensions
204 for (const auto& v : var_names) {
205 std::set<std::string> dims;
206 for (const auto& d : file.dimensions(v)) {
207 dims.insert(d);
208 }
209
210 if (not member(v, {"lat", "lon", "lat_bnds", "lon_bnds"}) and
211 member("x", dims) and member("y", dims)) {
212 file.write_attribute(v, "coordinates", "lat lon");
213 }
214
215 if (set_grid_mapping and member("x", dims) and member("y", dims)) {
216 file.write_attribute(v, "grid_mapping", grid_mapping_name);
217 }
218 }
219
220 // and if it also contains lat_bnds and lon_bnds, add the bounds attribute to lat
221 // and lon.
222 if (member("lat_bnds", var_names) and member("lon_bnds", var_names)) {
223 file.write_attribute("lat", "bounds", "lat_bnds");
224 file.write_attribute("lon", "bounds", "lon_bnds");
225 }
226 }
227 }
228
229 if (kind == INCLUDE_MODEL_STATE) {
230 write_model_state(file);
231 }
232 write_diagnostics(file, variables);
233
234 // find out how much time passed since the beginning of the run and save it to the output file
235 {
236 unsigned int time_length = file.dimension_length(m_config->get_string("time.dimension_name"));
237 size_t start = time_length > 0 ? static_cast<size_t>(time_length - 1) : 0;
238 io::write_timeseries(file, timestamp, start,
240 }
241}
242
243void IceModel::define_diagnostics(const File &file, const std::set<std::string> &variables,
244 io::Type default_type) const {
245 for (const auto& variable : variables) {
246 auto diag = m_diagnostics.find(variable);
247
248 if (diag != m_diagnostics.end()) {
249 diag->second->define(file, default_type);
250 }
251 }
252}
253
254//! \brief Writes variables listed in vars to filename, using nctype to write
255//! fields stored in dedicated Arrays.
256void IceModel::write_diagnostics(const File &file, const std::set<std::string> &variables) const {
257 for (const auto& variable : variables) {
258 auto diag = m_diagnostics.find(variable);
259
260 if (diag != m_diagnostics.end()) {
261 diag->second->compute()->write(file);
262 }
263 }
264}
265
266void IceModel::define_model_state(const File &file) const {
267 for (auto *v : m_model_state) {
268 v->define(file, io::PISM_DOUBLE);
269 }
270
271 for (const auto& m : m_submodels) {
272 m.second->define_model_state(file);
273 }
274
275 for (const auto& d : m_diagnostics) {
276 d.second->define_state(file);
277 }
278}
279
280void IceModel::write_model_state(const File &file) const {
281 for (auto *v : m_model_state) {
282 v->write(file);
283 }
284
285 for (const auto& m : m_submodels) {
286 m.second->write_model_state(file);
287 }
288
289 for (const auto& d : m_diagnostics) {
290 d.second->write_state(file);
291 }
292}
293
294} // end of namespace pism
unsigned int nvariables() const
Definition File.cc:742
void set_compression_level(int level) const
Definition File.cc:189
std::string variable_name(unsigned int id) const
Definition File.cc:755
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
Definition File.cc:378
void define_variable(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Define a variable.
Definition File.cc:543
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:590
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition File.cc:420
std::vector< std::string > dimensions(const std::string &variable_name) const
Definition File.cc:390
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
Definition File.cc:645
High-level PISM I/O class.
Definition File.hh:55
std::map< std::string, const Component * > m_submodels
the list of sub-models, for writing model states and obtaining diagnostics
Definition IceModel.hh:252
virtual void define_diagnostics(const File &file, const std::set< std::string > &variables, io::Type default_type) const
Definition output.cc:243
double m_start_time
Definition IceModel.hh:474
VariableMetadata run_stats() const
Definition utilities.cc:91
const Time::Ptr m_time
Time manager.
Definition IceModel.hh:246
std::shared_ptr< Context > m_ctx
Execution context.
Definition IceModel.hh:240
virtual void save_results()
Save model state in NetCDF format.
Definition output.cc:98
const Logger::Ptr m_log
Logger.
Definition IceModel.hh:244
VariableMetadata m_output_global_attributes
stores global attributes saved in a PISM output file
Definition IceModel.hh:249
virtual void prepend_history(const std::string &string)
Get time and user/host name and add it to the given string.
Definition utilities.cc:114
virtual void write_model_state(const File &file) const
Definition output.cc:280
Config::Ptr m_config
Configuration flags and parameters.
Definition IceModel.hh:238
virtual void save_variables(const File &file, OutputKind kind, const std::set< std::string > &variables, double time, io::Type default_diagnostics_type=io::PISM_FLOAT) const
Definition output.cc:150
virtual void define_model_state(const File &file) const
Definition output.cc:266
std::set< array::Array * > m_model_state
Definition IceModel.hh:415
virtual void write_diagnostics(const File &file, const std::set< std::string > &variables) const
Writes variables listed in vars to filename, using nctype to write fields stored in dedicated Arrays.
Definition output.cc:256
const units::System::Ptr m_sys
Unit system.
Definition IceModel.hh:242
std::map< std::string, Diagnostic::Ptr > m_diagnostics
Requested spatially-variable diagnostics.
Definition IceModel.hh:417
virtual void write_metadata(const File &file, MappingTreatment mapping_flag, HistoryTreatment history_flag) const
Write time-independent metadata to a file.
Definition output.cc:72
std::set< std::string > m_output_vars
Definition IceModel.hh:422
const std::shared_ptr< Grid > m_grid
Computational grid.
Definition IceModel.hh:236
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
void begin(const char *name) const
Definition Profiling.cc:75
void end(const char *name) const
Definition Profiling.cc:91
VariableMetadata & set_name(const std::string &name)
std::string get_name() const
void append_time(const File &file, const Config &config, double time_seconds)
Prepare a file for output.
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition IO_Flags.hh:74
void write_attributes(const File &file, const VariableMetadata &variable, io::Type nctype)
Write variable attributes to a NetCDF file.
@ PISM_FLOAT
Definition IO_Flags.hh:51
@ 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.
MaxTimestep reporting_max_timestep(const std::vector< double > &times, double t, double eps, const std::string &description)
Definition output.cc:41
io::Backend string_to_backend(const std::string &backend)
Definition File.cc:56
void write_mapping(const File &file, const pism::MappingInfo &info)
double wall_clock_hours(MPI_Comm com, double start_time)
Return time since the beginning of the run, in hours.
bool ends_with(const std::string &str, const std::string &suffix)
Returns true if str ends with suffix and false otherwise.
std::string printf(const char *format,...)
static const double k
Definition exactTestP.cc:42
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)
void write_run_stats(const File &file, const pism::VariableMetadata &stats)
Definition output.cc:143