PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
output.cc
Go to the documentation of this file.
1 // Copyright (C) 2004-2023 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 
39 namespace pism {
40 
41 MaxTimestep 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.
72 void 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 /*!
96 Calls 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  m_ctx->pio_iosys_id());
133 
135 
136  write_run_stats(file, run_stats());
137 
139  m_time->current());
140  }
141  profiling.end("io.model_state");
142 }
143 
144 void write_mapping(const File &file, const pism::MappingInfo &info) {
145 
146  const auto &mapping = info.mapping;
147  std::string name = mapping.get_name();
148  if (mapping.has_attributes()) {
149  if (not file.find_variable(name)) {
150  file.define_variable(name, io::PISM_DOUBLE, {});
151  }
152  io::write_attributes(file, mapping, io::PISM_DOUBLE);
153 
154  // Write the PROJ string to mapping:proj_params (for CDO).
155  std::string proj = info.proj;
156  if (not proj.empty()) {
157  file.write_attribute(name, "proj_params", proj);
158  }
159  }
160 }
161 
162 void write_run_stats(const File &file, const pism::VariableMetadata &stats) {
163  if (not file.find_variable(stats.get_name())) {
164  file.define_variable(stats.get_name(), io::PISM_DOUBLE, {});
165  }
167 }
168 
170  OutputKind kind,
171  const std::set<std::string> &variables,
172  double time,
173  io::Type default_diagnostics_type) const {
174 
175  // Compress 2D and 3D variables if output.compression_level > 0 and the output.format
176  // supports it.
177  file.set_compression_level(m_config->get_number("output.compression_level"));
178 
179  // define the time dimension if necessary (no-op if it is already defined)
180  io::define_time(file, *m_grid->ctx());
181  // define the "timestamp" (wall clock time since the beginning of the run)
182  // Note: it is time-dependent, so we need to define time first.
184  m_config->get_string("time.dimension_name"),
185  file, io::PISM_FLOAT);
186  // append to the time dimension
187  io::append_time(file, *m_config, time);
188 
189  // Write metadata *before* everything else:
190  //
191  // FIXME: we should write this to variables instead of attributes because NetCDF-4 crashes after
192  // about 2^16 attribute modifications per variable. :-(
193  write_run_stats(file, run_stats());
194 
195  if (kind == INCLUDE_MODEL_STATE) {
196  define_model_state(file);
197  }
198  define_diagnostics(file, variables, default_diagnostics_type);
199 
200  // Done defining variables
201 
202  {
203  // Note: we don't use "variables" (an argument of this method) here because it
204  // contains PISM's names of diagnostic quantities which (in some cases) map to more
205  // than one NetCDF variable. Moreover, here we're concerned with file contents, not
206  // the list of requested variables.
207  std::set<std::string> var_names;
208  unsigned int n_vars = file.nvariables();
209  for (unsigned int k = 0; k < n_vars; ++k) {
210  var_names.insert(file.variable_name(k));
211  }
212 
213  // If this output file contains variables lat and lon...
214  if (member("lat", var_names) and member("lon", var_names)) {
215 
216  // add the coordinates attribute to all variables that use x and y dimensions
217  for (const auto& v : var_names) {
218  std::set<std::string> dims;
219  for (const auto& d : file.dimensions(v)) {
220  dims.insert(d);
221  }
222 
223  if (not member(v, {"lat", "lon", "lat_bnds", "lon_bnds"}) and
224  member("x", dims) and member("y", dims)) {
225  file.write_attribute(v, "coordinates", "lat lon");
226  }
227  }
228 
229  // and if it also contains lat_bnds and lon_bnds, add the bounds attribute to lat
230  // and lon.
231  if (member("lat_bnds", var_names) and member("lon_bnds", var_names)) {
232  file.write_attribute("lat", "bounds", "lat_bnds");
233  file.write_attribute("lon", "bounds", "lon_bnds");
234  }
235  }
236  }
237 
238  if (kind == INCLUDE_MODEL_STATE) {
239  write_model_state(file);
240  }
241  write_diagnostics(file, variables);
242 
243  // find out how much time passed since the beginning of the run and save it to the output file
244  {
245  unsigned int time_length = file.dimension_length(m_config->get_string("time.dimension_name"));
246  size_t start = time_length > 0 ? static_cast<size_t>(time_length - 1) : 0;
247  io::write_timeseries(file, m_timestamp, start,
249  }
250 }
251 
252 void IceModel::define_diagnostics(const File &file, const std::set<std::string> &variables,
253  io::Type default_type) const {
254  for (const auto& variable : variables) {
255  auto diag = m_diagnostics.find(variable);
256 
257  if (diag != m_diagnostics.end()) {
258  diag->second->define(file, default_type);
259  }
260  }
261 }
262 
263 //! \brief Writes variables listed in vars to filename, using nctype to write
264 //! fields stored in dedicated Arrays.
265 void IceModel::write_diagnostics(const File &file, const std::set<std::string> &variables) const {
266  for (const auto& variable : variables) {
267  auto diag = m_diagnostics.find(variable);
268 
269  if (diag != m_diagnostics.end()) {
270  diag->second->compute()->write(file);
271  }
272  }
273 }
274 
275 void IceModel::define_model_state(const File &file) const {
276  for (auto *v : m_model_state) {
277  v->define(file, io::PISM_DOUBLE);
278  }
279 
280  for (const auto& m : m_submodels) {
281  m.second->define_model_state(file);
282  }
283 
284  for (const auto& d : m_diagnostics) {
285  d.second->define_state(file);
286  }
287 }
288 
289 void IceModel::write_model_state(const File &file) const {
290  for (auto *v : m_model_state) {
291  v->write(file);
292  }
293 
294  for (const auto& m : m_submodels) {
295  m.second->write_model_state(file);
296  }
297 
298  for (const auto& d : m_diagnostics) {
299  d.second->write_state(file);
300  }
301 }
302 
303 } // end of namespace pism
unsigned int nvariables() const
Definition: File.cc:803
void set_compression_level(int level) const
Definition: File.cc:222
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
Definition: File.cc:361
std::string variable_name(unsigned int id) const
Definition: File.cc:816
void define_variable(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Define a variable.
Definition: File.cc:573
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:638
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition: File.cc:454
std::vector< std::string > dimensions(const std::string &variable_name) const
Definition: File.cc:425
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
Definition: File.cc:693
High-level PISM I/O class.
Definition: File.hh:56
std::map< std::string, const Component * > m_submodels
the list of sub-models, for writing model states and obtaining diagnostics
Definition: IceModel.hh:259
virtual void define_diagnostics(const File &file, const std::set< std::string > &variables, io::Type default_type) const
Definition: output.cc:252
double m_start_time
Definition: IceModel.hh:477
VariableMetadata m_timestamp
Definition: IceModel.hh:476
VariableMetadata run_stats() const
Definition: utilities.cc:92
const Config::Ptr m_config
Configuration flags and parameters.
Definition: IceModel.hh:243
const Time::Ptr m_time
Time manager.
Definition: IceModel.hh:251
const std::shared_ptr< Context > m_ctx
Execution context.
Definition: IceModel.hh:245
virtual void save_results()
Save model state in NetCDF format.
Definition: output.cc:98
const Logger::Ptr m_log
Logger.
Definition: IceModel.hh:249
VariableMetadata m_output_global_attributes
stores global attributes saved in a PISM output file
Definition: IceModel.hh:256
virtual void prepend_history(const std::string &string)
Get time and user/host name and add it to the given string.
Definition: utilities.cc:115
virtual void write_model_state(const File &file) const
Definition: output.cc:289
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:169
virtual void define_model_state(const File &file) const
Definition: output.cc:275
std::set< array::Array * > m_model_state
Definition: IceModel.hh:417
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:265
std::map< std::string, Diagnostic::Ptr > m_diagnostics
Requested spatially-variable diagnostics.
Definition: IceModel.hh:419
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:424
const std::shared_ptr< Grid > m_grid
Computational grid.
Definition: IceModel.hh:241
std::string proj
Definition: projection.hh:47
VariableMetadata mapping
Definition: projection.hh:46
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
Definition: MaxTimestep.hh:31
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.
Definition: io_helpers.cc:247
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition: IO_Flags.hh:78
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_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.
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
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:57
void write_mapping(const File &file, const pism::MappingInfo &info)
Definition: output.cc:144
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
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:162