PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
OutputWriter.hh
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#ifndef PISM_OUTPUTWRITER_H
21#define PISM_OUTPUTWRITER_H
22
23#include <map>
24#include <memory>
25#include <set>
26#include <string>
27#include <vector>
28
29#include <mpi.h>
30
31namespace pism {
32
33class Config;
34class VariableMetadata;
35class VariableAttributes;
36
37namespace io {
38enum Type : int;
39}
40
41/*!
42 * File output API
43 *
44 * PISM writes the following kinds of output:
45 *
46 * 1) output at the end of a run (used to re-start the model),
47 *
48 * 2) 2D and 3D diagnostic output saved at specified model times during a run (affects
49 * model time stepping),
50 *
51 * 3) Same as 2), but each time record is saved to a separate file,
52 *
53 * 4) Same as 2), but appending to a file created by an earlier run,
54 *
55 * 5) Snapshots of the model state at times *close to* specified model times during a run
56 * (does not affect model time stepping; can be used to re-start a failed run)
57 *
58 * 6) Same as 5, but each snapshot is saved to separate file,
59 *
60 * 7) Scalar time-dependent diagnostics (value are stored *redundantly* on all MPI ranks),
61 *
62 * 8) Same as 7), but appending to a file created by an earlier run,
63 *
64 * 9) Snapshots of the model state saved after a specified *wall clock time* interval
65 * passed (used to re-start a failed run).
66 *
67 * All files contain *one* unlimited dimension (time).
68 *
69 * File contents are determined at run time.
70 *
71 * A file may contain a mix of 1D, 2D, and 3D time-dependent and time-independent
72 * variables.
73 *
74 * A file may contain more than one x,y grid and more than one set of vertical (z) levels.
75 *
76 * Appending to a file requires being able to get the current length of the time dimension
77 * in a file and the last value of the corresponding coordinate variable.
78 *
79 * In this API, the first call using `file_name` opens the file `file_name`. If the file already exists
80 * it is moved to `file_name` + "~" (a "backup" file).
81 *
82 * If the first call using `file_name` is `append(file_name)`, the file is opened for
83 * appending.
84 *
85 * An opened file remains open until `close()` is called or until an instance of
86 * `OutputWriter` is de-allocated (i.e. until the end of a model run).
87 *
88 * PISM defines all variables before writing *any* of the associated data. Attributes are
89 * set *once* and not modified afterwards. (This should make it possible to aggregate all
90 * metadata and write all of it at once.)
91 *
92 * All variables associated with a grid (2D and 3D arrays, etc) have to be "declared"
93 * using a call to `initialize(array_variables)`. This allows a class derived from
94 * OutputWriter to set up infrastructure needed to write these variables. For example: an
95 * implementation of asynchronous I/O can set up communication for all files *at once*.
96 *
97 * PISM buffers scalar time-dependent diagnostics to reduce the number of I/O operations.
98 * 2D and 3D arrays are written one time record at a time (increase the length of time
99 * dimension by one, write a bunch of variables, increase the length of time dimension by
100 * one, write more, etc).
101 */
103public:
104 OutputWriter(MPI_Comm comm, const Config &config);
105 virtual ~OutputWriter();
106
107 /*!
108 * Initialize the output writer. The set `array_variables` contains all array variables
109 * that may be written to output files (not necessarily to the same output file).
110 *
111 * This initialization step allows an OutputWriter implementation to use grid
112 * information for all distributed arrays to setup up communication between
113 * "computational" and "I/O" MPI ranks.
114 */
115 void initialize(const std::set<VariableMetadata> &array_variables, bool relaxed_mode = false);
116
117 /*!
118 * Define a variable given its metadata.
119 *
120 * No-op if the variable already exists.
121 */
122 void define_variable(const std::string &file_name, const VariableMetadata &variable);
123
124 /*!
125 * Set global attributes for a given output file.
126 *
127 * Numbers are written as NC_DOUBLE.
128 */
129 void set_global_attributes(const std::string &file_name,
130 const std::map<std::string, std::string> &strings,
131 const std::map<std::string, std::vector<double> > &numbers);
132
133 /*!
134 * Append to the global attribute "history" in the output file.
135 *
136 * The "history" attribute is treated as a newline-delimited list. This call adds a "\n"
137 * followed by the string in `text` to the attribute "history" in the file `file_name`.
138 */
139 void append_history(const std::string &file_name, const std::string &text);
140
141 /*!
142 * Increase the length of the time dimension by one, appending the value `time_seconds`.
143 *
144 * This should increase the value returned by `time_dimension_length()` by one.
145 */
146 void append_time(const std::string &file_name, double time_seconds);
147
148 /*!
149 * Write a 1D array `input` to a variable `variable_name` in the file `file_name`.
150 *
151 * Data in `input` are written without modification.
152 *
153 * The array `input` is stored *redundantly* on all MPI ranks.
154 *
155 * FIXME: writing to the time variable will change the length of the time dimension.
156 */
157 void write_array(const std::string &file_name, const std::string &variable_name,
158 const std::vector<unsigned int> &start, const std::vector<unsigned int> &count,
159 const std::vector<double> &input);
160
161 /*!
162 * Write a text (or string; type NC_CHAR) variable.
163 *
164 * The string `input` is stored *redundantly* on all MPI ranks.
165 */
166 void write_text(const std::string &file_name, const std::string &variable_name,
167 const std::vector<unsigned int> &start, const std::vector<unsigned int> &count,
168 const std::string &input);
169
170 /*!
171 * Write a 2D or 3D array `input` to the variable `variable_name` in file `file_name`.
172 *
173 * Write coordinate variables (`x`, `y`, `z`, etc) required by this variable.
174 *
175 * May be a no-op if this variable is time-independent and was written already.
176 *
177 * The array `input` is distributed across MPI ranks in the communicator used to create
178 * this `OutputWriter` instance. Uses domain decomposition information provided to
179 * `define_variable()` and `initialize(array_variables)`.
180 */
181 void write_distributed_array(const std::string &file_name, const std::string &variable_name,
182 const double *input);
183
184 /*!
185 * Write a scalar time-dependent variable.
186 *
187 * `input` is stored redundantly by each MPI process.
188 */
189
190 void write_timeseries(const std::string &file_name, const std::string &variable_name,
191 const std::vector<unsigned int> &start,
192 const std::vector<unsigned int> &count, const std::vector<double> &input);
193 /*!
194 * Indicate that the file `file_name` should be open for appending.
195 *
196 * This implies that if `file_name` should not be deleted if it already exists.
197 *
198 * May require reading time dimension length and the last value of time from `file_name`.
199 */
200 void append(const std::string &file_name);
201
202 /*!
203 * Ensure that all requested write operations are complete.
204 *
205 * May be a no-op in the context of asynchronous writing.
206 */
207 void sync(const std::string &file_name);
208
209 /*!
210 * Possibly close the file `file_name`. Used to indicate that the user does not intend
211 * to write to the file `file_name` any more.
212 */
213 void close(const std::string &file_name);
214
215 /*!
216 * Return the length of the time dimension (possibly cached to avoid reading from the
217 * file or communication).
218 */
219 unsigned int time_dimension_length(const std::string &file_name);
220
221 /*!
222 * Return the last value of the coordinate variable "time" (possibly cached to avoid
223 * reading from the file or communication).
224 *
225 * Used when appending to an existing file.
226 */
227 double last_time_value(const std::string &file_name);
228
229 /*!
230 * Return the MPI communicator
231 */
232 MPI_Comm comm() const;
233
234 /*!
235 * Return true if this writer is asynchronous, false otherwise.
236 */
237 bool is_async() const;
238
239protected:
240 /*!
241 * Define a dimension.
242 *
243 * No-op if the dimension already exists.
244 *
245 * `length` of zero corresponds to an unlimited dimension.
246 */
247 void define_dimension(const std::string &file_name, const std::string &dimension_name,
248 unsigned int length);
249
250 /*!
251 * Define a variable given a list of dimension names and set its attributes.
252 *
253 * Use this method to define coordinate variables (`x`, `y`, `time`, etc).
254 *
255 * No-op if the variable already exists.
256 *
257 */
258 void define_variable(const std::string &file_name, const std::string &variable_name,
259 const std::vector<std::string> &dims, io::Type type,
260 const VariableAttributes &attributes);
261
262 /*!
263 * Add a variable to the list of variables that can be written to output files.
264 *
265 * This has to be done before a spatial variable is *defined* in an output file.
266 *
267 * @param[in] metadata variable metadata (name, attributes, grid info, etc)
268 */
269 void add_variable(const VariableMetadata &metadata);
270
271 /*!
272 * Return the metadata for the variable `variable_name`.
273 */
274 const VariableMetadata &variable_info(const std::string &variable_name) const;
275
276 bool variable_info_is_available(const std::string &variable_name) const;
277
278 /*!
279 * Return `true` if variable `variable_name` was already written to the file
280 * `file_name`. Used to avoid writing coordinate variables and time-independent 2D and
281 * 3D arrays more than once.
282 */
283 bool &already_written(const std::string &file_name, const std::string &variable_name,
284 bool time_dependent);
285
286 /*!
287 * Return the name of the time dimension and the corresponding coordinate variable.
288 */
289 const std::string &time_name() const;
290
291 /*!
292 * Define dimensions corresponding to a `variable`. If a dimension has the corresponding
293 * coordinate variable this method will define *both* the dimension and coordinate
294 * variable.
295 *
296 * Returns the list of dimension names for `variable` that can be used to define it. (A
297 * NetCDF variable may depend on other dimensions *in addition* to the ones in
298 * `variable`, for example the time dimension or the dimension corresponding to
299 * "output.experiment_id")
300 */
301 std::vector<std::string> define_dimensions(const std::string &file_name,
302 const VariableMetadata &variable);
303
304 /*!
305 * Write dimensions corresponding to a `variable` (except for the time dimension).
306 */
307 void write_dimensions(const std::string &file_name, const VariableMetadata &variable);
308
309 /*!
310 * Implementation of initialize()
311 */
312 virtual void initialize_impl(const std::set<VariableMetadata> &array_variables) = 0;
313
314 /*!
315 * Implementation of set_global_attributes()
316 */
317 virtual void
318 set_global_attributes_impl(const std::string &file_name,
319 const std::map<std::string, std::string> &strings,
320 const std::map<std::string, std::vector<double> > &numbers) = 0;
321
322 /*!
323 * Implementation of define_dimension()
324 */
325 virtual void define_dimension_impl(const std::string &file_name, const std::string &name,
326 unsigned int length) = 0;
327
328 /*!
329 * Implementation of define_variable()
330 */
331 virtual void define_variable_impl(const std::string &file_name, const std::string &variable_name,
332 const std::vector<std::string> &dims, io::Type type,
333 const VariableAttributes &attributes) = 0;
334
335 /*!
336 * Implementation of append_time()
337 */
338 virtual void append_time_impl(const std::string &file_name, double time_seconds) = 0;
339
340 /*!
341 * Implementation of append_history()
342 */
343 virtual void append_history_impl(const std::string &file_name, const std::string &text) = 0;
344
345 /*!
346 * Implementation of time_dimension_length()
347 */
348 virtual unsigned int time_dimension_length_impl(const std::string &file_name) = 0;
349
350 /*!
351 * Implementation of last_time_value()
352 */
353 virtual double last_time_value_impl(const std::string &file_name) = 0;
354
355 /*!
356 * Implementation of write_array()
357 */
358 virtual void write_array_impl(const std::string &file_name, const std::string &variable_name,
359 const std::vector<unsigned int> &start,
360 const std::vector<unsigned int> &count, const double *data) = 0;
361
362 /*!
363 * Implementation of write_text()
364 */
365 virtual void write_text_impl(const std::string &file_name, const std::string &variable_name,
366 const std::vector<unsigned int> &start,
367 const std::vector<unsigned int> &count,
368 const std::string &input) = 0;
369
370 /*!
371 * Implementation of write_distributed_array()
372 */
373 virtual void write_distributed_array_impl(const std::string &file_name,
374 const std::string &variable_name,
375 const double *data) = 0;
376
377 /*!
378 * Implementation of append()
379 */
380 virtual void append_impl(const std::string &file_name) = 0;
381
382 /*!
383 * Implementation of sync()
384 */
385 virtual void sync_impl(const std::string &file_name) = 0;
386
387 /*!
388 * Implementation of close()
389 */
390 virtual void close_impl(const std::string &file_name) = 0;
391
392 const std::string &experiment_id() const;
393
394 /*!
395 * Allows derived classes to set m_impl->is_async.
396 */
397 void set_is_async(bool flag);
398
399private:
400 void write_experiment_id(const std::string &file_name);
401
402 struct Impl;
404};
405
406/*!
407 * Wrapper class used to make OutputWriter a bit easier to use.
408 *
409 * See documentation of OutputWriter for details.
410 *
411 * Does not open the file when created, allowing one to call `append()` to indicate that a
412 * file should not be over-written.
413 */
415public:
416 OutputFile(std::shared_ptr<OutputWriter> writer, const std::string &name);
417
418 void define_variable(const VariableMetadata &variable) const;
419
420 void set_global_attributes(const std::map<std::string, std::string> &strings,
421 const std::map<std::string, std::vector<double> > &numbers) const;
422
423 void append_time(double time_seconds) const;
424
425 void append_history(const std::string &text) const;
426
427 void write_array(const std::string &variable_name, const std::vector<unsigned int> &start,
428 const std::vector<unsigned int> &count, const std::vector<double> &input) const;
429
430 void write_distributed_array(const std::string &variable_name, const double *input) const;
431
432 void write_timeseries(const std::string &variable_name, const std::vector<unsigned int> &start,
433 const std::vector<unsigned int> &count,
434 const std::vector<double> &input) const;
435
436 void write_text(const std::string &variable_name, const std::vector<unsigned int> &start,
437 const std::vector<unsigned int> &count, const std::string &input) const;
438
439 void append();
440
441 void sync();
442
443 void close();
444
445 unsigned int time_dimension_length() const;
446
447 double last_time_value() const;
448
449 const std::string &name() const;
450
451private:
452 std::string m_file_name;
453 std::shared_ptr<OutputWriter> m_writer;
454};
455
456} // namespace pism
457
458#endif /* PISM_OUTPUTWRITER_H */
A class for storing and accessing PISM configuration flags and parameters.
Definition Config.hh:56
std::string m_file_name
void write_distributed_array(const std::string &variable_name, const double *input) const
Definition OutputFile.cc:56
void append_time(double time_seconds) const
Definition OutputFile.cc:41
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
const std::string & name() const
Definition OutputFile.cc:94
std::shared_ptr< OutputWriter > m_writer
void append_history(const std::string &text) const
Definition OutputFile.cc:45
unsigned int time_dimension_length() const
Definition OutputFile.cc:86
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 write_timeseries(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::vector< double > &input) const
Definition OutputFile.cc:61
void define_variable(const VariableMetadata &variable) const
Definition OutputFile.cc:31
void write_array(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::vector< double > &input) const
Definition OutputFile.cc:49
double last_time_value() const
Definition OutputFile.cc:90
void write_text(const std::string &file_name, const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::string &input)
unsigned int time_dimension_length(const std::string &file_name)
void close(const std::string &file_name)
bool is_async() const
void append(const std::string &file_name)
void add_variable(const VariableMetadata &metadata)
void set_is_async(bool flag)
virtual void write_array_impl(const std::string &file_name, const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *data)=0
virtual double last_time_value_impl(const std::string &file_name)=0
const std::string & time_name() const
virtual void set_global_attributes_impl(const std::string &file_name, const std::map< std::string, std::string > &strings, const std::map< std::string, std::vector< double > > &numbers)=0
void write_dimensions(const std::string &file_name, const VariableMetadata &variable)
bool & already_written(const std::string &file_name, const std::string &variable_name, bool time_dependent)
void define_variable(const std::string &file_name, const VariableMetadata &variable)
virtual void define_variable_impl(const std::string &file_name, const std::string &variable_name, const std::vector< std::string > &dims, io::Type type, const VariableAttributes &attributes)=0
virtual void define_dimension_impl(const std::string &file_name, const std::string &name, unsigned int length)=0
void set_global_attributes(const std::string &file_name, const std::map< std::string, std::string > &strings, const std::map< std::string, std::vector< double > > &numbers)
MPI_Comm comm() const
double last_time_value(const std::string &file_name)
void define_dimension(const std::string &file_name, const std::string &dimension_name, unsigned int length)
virtual void initialize_impl(const std::set< VariableMetadata > &array_variables)=0
void write_timeseries(const std::string &file_name, const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::vector< double > &input)
void append_time(const std::string &file_name, double time_seconds)
virtual void write_distributed_array_impl(const std::string &file_name, const std::string &variable_name, const double *data)=0
virtual void append_time_impl(const std::string &file_name, double time_seconds)=0
virtual void write_text_impl(const std::string &file_name, const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::string &input)=0
void initialize(const std::set< VariableMetadata > &array_variables, bool relaxed_mode=false)
virtual unsigned int time_dimension_length_impl(const std::string &file_name)=0
const std::string & experiment_id() const
void sync(const std::string &file_name)
std::vector< std::string > define_dimensions(const std::string &file_name, const VariableMetadata &variable)
virtual void close_impl(const std::string &file_name)=0
void write_experiment_id(const std::string &file_name)
void write_distributed_array(const std::string &file_name, const std::string &variable_name, const double *input)
virtual void sync_impl(const std::string &file_name)=0
const VariableMetadata & variable_info(const std::string &variable_name) const
bool variable_info_is_available(const std::string &variable_name) const
void append_history(const std::string &file_name, const std::string &text)
virtual void append_history_impl(const std::string &file_name, const std::string &text)=0
void write_array(const std::string &file_name, const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::vector< double > &input)
virtual void append_impl(const std::string &file_name)=0
int count
Definition test_cube.c:16