PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
Diagnostic.hh
Go to the documentation of this file.
1// Copyright (C) 2010--2025 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#ifndef PISM_DIAGNOSTIC_HH
20#define PISM_DIAGNOSTIC_HH
21
22#include <map>
23#include <memory>
24#include <string>
25
26#include "pism/util/Config.hh"
27#include "pism/util/VariableMetadata.hh"
28#include "pism/util/array/Scalar.hh"
29#include "pism/util/error_handling.hh"
30#include "pism/util/io/File.hh"
31#include "pism/util/io/OutputWriter.hh"
32
33namespace pism {
34namespace grid {
36}
37
38class Grid;
39
40//! @brief Class representing diagnostic computations in PISM.
41/*!
42 * The main goal of this abstraction is to allow accessing metadata
43 * corresponding to a diagnostic quantity *before* it is computed.
44 *
45 * Another goal is to create an interface for computing diagnostics *without*
46 * knowing which PISM module is responsible for the computation.
47 *
48 * Technical note: to compute some diagnostic quantities we need access to
49 * protected members of classes. C++ forbids obtaining pointers to non-static
50 * methods of a class, but it is possible to define a (friend) function
51 *
52 * @code
53 * std::shared_ptr<array::Array> compute_bar(Foo* model, ...);
54 * @endcode
55 *
56 * which is the same as creating a method `Foo::compute_bar()`, but you *can*
57 * get a pointer to it.
58 *
59 * Diagnostic creates a common interface for all these compute_bar
60 * functions.
61 */
63public:
64 Diagnostic(std::shared_ptr<const Grid> g);
65 virtual ~Diagnostic() = default;
66
67 typedef std::shared_ptr<Diagnostic> Ptr;
68
69 // defined below
70 template <typename T>
71 static Ptr wrap(const T &input);
72
73 void update(double dt);
74 void reset();
75
76 //! @brief Compute a diagnostic quantity and return a pointer to a newly-allocated Array.
77 std::shared_ptr<array::Array> compute() const;
78
80
81 unsigned int n_variables() const;
82
83 VariableMetadata &metadata(unsigned int N = 0);
84
85 std::set<VariableMetadata> state() const;
86
87 void init(const File &input, unsigned int time);
88
89 void write_state(const OutputFile &output) const;
90
91protected:
92 virtual void init_impl(const File &input, unsigned int time);
93
94 virtual std::set<VariableMetadata> state_impl() const;
95 virtual void write_state_impl(const OutputFile &output) const;
96
97 virtual void update_impl(double dt);
98 virtual void reset_impl();
99
100 virtual std::shared_ptr<array::Array> compute_impl() const = 0;
101
102 double to_internal(double x) const;
103 double to_external(double x) const;
104
105 /*!
106 * Allocate storage for an array of type `T` and copy metadata from `m_vars`.
107 */
108 template<typename T>
109 std::shared_ptr<T> allocate(const std::string &name) const {
110 auto result = std::make_shared<T>(m_grid, name);
111 for (unsigned int k = 0; k < result->ndof(); ++k) {
112 result->metadata(k) = m_vars.at(k);
113 }
114 return result;
115 }
116
117 //! the grid
118 std::shared_ptr<const Grid> m_grid;
119 //! the unit system
121 //! Configuration flags and parameters
122 std::shared_ptr<const Config> m_config;
123 //! metadata corresponding to NetCDF variables
124 std::vector<VariableMetadata> m_vars;
125 //! fill value (used often enough to justify storing it)
127};
128
129typedef std::map<std::string, Diagnostic::Ptr> DiagnosticList;
130
131/*!
132 * Helper template wrapping quantities with dedicated storage in diagnostic classes.
133 *
134 * Note: Make sure that that created diagnostics don't outlast fields that they wrap (or you'll have
135 * dangling pointers).
136 */
137template <class T>
139public:
140 DiagWithDedicatedStorage(const T &input) : Diagnostic(input.grid()), m_input(input) {
141 for (unsigned int j = 0; j < input.ndof(); ++j) {
142 m_vars.emplace_back(input.metadata(j));
143 }
144 }
145
146protected:
147 std::shared_ptr<array::Array> compute_impl() const {
148 auto result = m_input.duplicate();
149
150 result->set_name(m_input.get_name());
151 for (unsigned int k = 0; k < m_vars.size(); ++k) {
152 result->metadata(k) = m_vars[k];
153 }
154
155 result->copy_from(m_input);
156
157 return result;
158 }
159
160 const T &m_input;
161};
162
163template <typename T>
165 return Ptr(new DiagWithDedicatedStorage<T>(input));
166}
167
168//! A template derived from Diagnostic, adding a "Model".
169template <class Model>
170class Diag : public Diagnostic {
171public:
172 Diag(const Model *m) : Diagnostic(m->grid()), model(m) {
173 }
174
175protected:
176 const Model *model;
177};
178
179/*!
180 * Report a time-averaged rate of change of a quantity by accumulating changes over several time
181 * steps.
182 */
183template <class M>
184class DiagAverageRate : public Diag<M> {
185public:
186 enum InputKind { TOTAL_CHANGE = 0, RATE = 1 };
187
188 DiagAverageRate(const M *m, const std::string &name, InputKind kind)
189 : Diag<M>(m),
190 m_factor(1.0),
191 m_input_kind(kind),
192 m_accumulator(Diagnostic::m_grid, name + "_accumulator"),
194 m_time_since_reset(name + "_time_since_reset", Diagnostic::m_sys) {
195
196 m_time_since_reset.units("seconds").long_name("time since " + m_accumulator.get_name() +
197 " was reset to 0");
198
199 m_accumulator.metadata().long_name("accumulator for the " + name + " diagnostic");
200
201 m_accumulator.set(0.0);
202 }
203
204protected:
205 void init_impl(const File &input, unsigned int time) {
207 m_accumulator.read(input, time);
208 } else {
209 m_accumulator.set(0.0);
210 }
211
213 input.read_variable(m_time_since_reset.get_name(), { time }, { 1 }, // start, count
215 } else {
216 m_interval_length = 0.0;
217 }
218 }
219
220 std::set<VariableMetadata> state_impl() const {
222 }
223
224 void write_state_impl(const OutputFile &output) const {
225 m_accumulator.write(output);
226
227 unsigned int time_length = output.time_dimension_length();
228 unsigned int t_start = time_length > 0 ? time_length - 1 : 0;
229 output.write_array(m_time_since_reset.get_name(), { t_start }, { 1 }, { m_interval_length });
230 }
231
232 virtual void update_impl(double dt) {
233 // Here the "factor" is used to convert units (from m to kg m^-2, for example) and (possibly)
234 // integrate over the time interval using the rectangle method.
235
236 double factor = m_factor * (m_input_kind == TOTAL_CHANGE ? 1.0 : dt);
237
238 m_accumulator.add(factor, this->model_input());
239
240 m_interval_length += dt;
241 }
242
243 virtual void reset_impl() {
244 m_accumulator.set(0.0);
245 m_interval_length = 0.0;
246 }
247
248 virtual std::shared_ptr<array::Array> compute_impl() const {
249 auto result = Diagnostic::allocate<array::Scalar>("diagnostic");
250
251 if (m_interval_length > 0.0) {
252 result->copy_from(m_accumulator);
253 result->scale(1.0 / m_interval_length);
254 } else {
256 }
257
258 return result;
259 }
260
261 // constants initialized in the constructor
262 double m_factor;
264 // the state (read from and written to files)
266 // length of the reporting interval, accumulated along with the cumulative quantity
269
270 // it should be enough to implement the constructor and this method
271 virtual const array::Scalar &model_input() {
272 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "no default implementation");
273 }
274};
275
276//! @brief PISM's scalar time-series diagnostics.
278public:
279 typedef std::shared_ptr<TSDiagnostic> Ptr;
280
281 TSDiagnostic(std::shared_ptr<const Grid> g, const std::string &name);
282 virtual ~TSDiagnostic();
283
284 /*!
285 * "Update" a scalar diagnostic for the time step from `t0` to `t1`. Estimates values of
286 * a diagnostic quantity for all requested times in the time interval `[t0, t1]` and
287 * saves them in a buffer.
288 */
289 void update(double t0, double t1);
290
291 /*!
292 * Flush the buffer to an output file.
293 */
294 void flush();
295
296 /*!
297 * Initialize a scalar diagnostic.
298 */
299 void init(std::shared_ptr<OutputFile> output_file,
300 std::shared_ptr<std::vector<double> > requested_times);
301
302 const VariableMetadata &metadata() const;
303
304protected:
305 virtual void update_impl(double t0, double t1) = 0;
306
307 /*!
308 * Compute the diagnostic. Regular (snapshot) quantity should be computed here; for rates of
309 * change, compute() should return the total change during the time step from t0 to t1. The rate
310 * itself is computed in evaluate_rate().
311 */
312 virtual double compute() = 0;
313
314 /*!
315 * Set internal (MKS) and "output" units.
316 */
317 void set_units(const std::string &units, const std::string &output_units);
318
319 //! the grid
320 std::shared_ptr<const Grid> m_grid;
321 //! Configuration flags and parameters
322 std::shared_ptr<const Config> m_config;
323 //! the unit system
324 std::shared_ptr<units::System> m_sys;
325
327
328 // buffer for diagnostic time series
329 std::vector<double> m_time;
330 std::vector<double> m_bounds;
331 std::vector<double> m_values;
332
333 //! requested times
334 std::shared_ptr<std::vector<double> > m_requested_times;
335 //! index into m_times
336 unsigned int m_current_time;
337
338 //! the file to save to (stored here because it is used by flush(), which is called from
339 //! update())
340 std::shared_ptr<OutputFile> m_output_file;
341 //! starting index used when flushing the buffer
342 unsigned int m_start;
343 //! size of the buffer used to store data
345};
346
347typedef std::map<std::string, TSDiagnostic::Ptr> TSDiagnosticList;
348
349//! Scalar diagnostic reporting a snapshot of a quantity modeled by PISM.
350/*!
351 * The method compute() should return the instantaneous "snapshot" value.
352 */
354public:
355 TSSnapshotDiagnostic(std::shared_ptr<const Grid> g, const std::string &name);
356
357private:
358 void update_impl(double t0, double t1);
359 void evaluate(double t0, double t1, double v);
360};
361
362//! Scalar diagnostic reporting the rate of change of a quantity modeled by PISM.
363/*!
364 * The rate of change is averaged in time over reporting intervals.
365 *
366 * The method compute() should return the instantaneous "snapshot" value of a quantity.
367 */
369public:
370 TSRateDiagnostic(std::shared_ptr<const Grid> g, const std::string &name);
371
372protected:
373 //! accumulator of changes (used to compute rates of change)
375 void evaluate(double t0, double t1, double change);
376
377private:
378 void update_impl(double t0, double t1);
379
380 //! last two values, used to compute the change during a time step
383};
384
385//! Scalar diagnostic reporting a "flux".
386/*!
387 * The flux is averaged over reporting intervals.
388 *
389 * The method compute() should return the change due to a flux over a time step.
390 *
391 * Fluxes can be computed using TSRateDiagnostic, but that would require keeping track of the total
392 * change due to a flux. It is possible for the magnitude of the total change to grow indefinitely,
393 * leading to the loss of precision; this is why we use changes over individual time steps instead.
394 *
395 * (The total change due to a flux can grow in magnitude even it the amount does not change. For
396 * example: if calving removes as much ice as we have added due to the SMB, the total mass is
397 * constant, but total SMB will grow.)
398 */
400public:
401 TSFluxDiagnostic(std::shared_ptr<const Grid> g, const std::string &name);
402
403private:
404 void update_impl(double t0, double t1);
405};
406
407template <class D, class M>
408class TSDiag : public D {
409public:
410 TSDiag(const M *m, const std::string &name)
411 : D(m->grid(), name), model(m) {
412 }
413protected:
414 const M *model;
415};
416
417} // end of namespace pism
418
419#endif /* PISM_DIAGNOSTIC_HH */
virtual void update_impl(double dt)
virtual const array::Scalar & model_input()
VariableMetadata m_time_since_reset
array::Scalar m_accumulator
virtual std::shared_ptr< array::Array > compute_impl() const
virtual void reset_impl()
void write_state_impl(const OutputFile &output) const
void init_impl(const File &input, unsigned int time)
std::set< VariableMetadata > state_impl() const
DiagAverageRate(const M *m, const std::string &name, InputKind kind)
std::shared_ptr< array::Array > compute_impl() const
DiagWithDedicatedStorage(const T &input)
const Model * model
Diag(const Model *m)
A template derived from Diagnostic, adding a "Model".
std::set< VariableMetadata > state() const
Definition Diagnostic.cc:84
std::vector< VariableMetadata > m_vars
metadata corresponding to NetCDF variables
virtual std::shared_ptr< array::Array > compute_impl() const =0
static Ptr wrap(const T &input)
virtual void reset_impl()
Definition Diagnostic.cc:55
VariableMetadata & metadata(unsigned int N=0)
Get a metadata object corresponding to variable number N.
virtual ~Diagnostic()=default
double m_fill_value
fill value (used often enough to justify storing it)
virtual std::set< VariableMetadata > state_impl() const
Definition Diagnostic.cc:88
const grid::DistributedGridInfo & grid_info() const
const units::System::Ptr m_sys
the unit system
std::shared_ptr< const Config > m_config
Configuration flags and parameters.
double to_internal(double x) const
Definition Diagnostic.cc:62
virtual void write_state_impl(const OutputFile &output) const
std::shared_ptr< Diagnostic > Ptr
Definition Diagnostic.hh:67
void init(const File &input, unsigned int time)
Definition Diagnostic.cc:92
virtual void update_impl(double dt)
Definition Diagnostic.cc:46
double to_external(double x) const
Definition Diagnostic.cc:72
unsigned int n_variables() const
Get the number of NetCDF variables corresponding to a diagnostic quantity.
Definition Diagnostic.cc:80
void update(double dt)
Definition Diagnostic.cc:42
std::shared_ptr< const Grid > m_grid
the grid
void write_state(const OutputFile &output) const
Definition Diagnostic.cc:96
virtual void init_impl(const File &input, unsigned int time)
std::shared_ptr< array::Array > compute() const
Compute a diagnostic quantity and return a pointer to a newly-allocated Array.
std::shared_ptr< T > allocate(const std::string &name) const
Class representing diagnostic computations in PISM.
Definition Diagnostic.hh:62
void read_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
Definition File.cc:704
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
Definition File.cc:378
High-level PISM I/O class.
Definition File.hh:57
Describes the PISM grid and the distribution of data across processors.
Definition Grid.hh:285
unsigned int time_dimension_length() const
Definition OutputFile.cc:86
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
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
TSDiag(const M *m, const std::string &name)
const M * model
virtual ~TSDiagnostic()
std::shared_ptr< units::System > m_sys
the unit system
std::vector< double > m_values
void set_units(const std::string &units, const std::string &output_units)
std::shared_ptr< std::vector< double > > m_requested_times
requested times
unsigned int m_current_time
index into m_times
unsigned int m_start
starting index used when flushing the buffer
std::vector< double > m_bounds
size_t m_buffer_size
size of the buffer used to store data
VariableMetadata m_variable
std::shared_ptr< const Config > m_config
Configuration flags and parameters.
std::shared_ptr< OutputFile > m_output_file
std::vector< double > m_time
virtual double compute()=0
const VariableMetadata & metadata() const
virtual void update_impl(double t0, double t1)=0
std::shared_ptr< const Grid > m_grid
the grid
void init(std::shared_ptr< OutputFile > output_file, std::shared_ptr< std::vector< double > > requested_times)
std::shared_ptr< TSDiagnostic > Ptr
void update(double t0, double t1)
PISM's scalar time-series diagnostics.
void update_impl(double t0, double t1)
Scalar diagnostic reporting a "flux".
double m_accumulator
accumulator of changes (used to compute rates of change)
void update_impl(double t0, double t1)
void evaluate(double t0, double t1, double change)
double m_v_previous
last two values, used to compute the change during a time step
Scalar diagnostic reporting the rate of change of a quantity modeled by PISM.
void evaluate(double t0, double t1, double v)
void update_impl(double t0, double t1)
Scalar diagnostic reporting a snapshot of a quantity modeled by PISM.
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
std::string get_name() const
void add(double alpha, const Array2D< T > &x)
Definition Array2D.hh:93
void read(const std::string &filename, unsigned int time)
Definition Array.cc:753
void write(const OutputFile &file) const
Definition Array.cc:859
const std::string & get_name() const
Get the name of an Array object.
Definition Array.cc:357
void set(double c)
Result: v[j] <- c for all j.
Definition Array.cc:659
VariableMetadata & metadata(unsigned int N=0)
Returns a reference to the VariableMetadata object containing metadata for the compoment N.
Definition Array.cc:467
std::shared_ptr< System > Ptr
Definition Units.hh:47
#define PISM_ERROR_LOCATION
static const double g
Definition exactTestP.cc:36
std::map< std::string, TSDiagnostic::Ptr > TSDiagnosticList
std::map< std::string, Diagnostic::Ptr > DiagnosticList
static const double k
Definition exactTestP.cc:42