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
Diagnostic.cc
Go to the documentation of this file.
1/* Copyright (C) 2015, 2016, 2017, 2019, 2020, 2021, 2022, 2023, 2024 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#include <cmath>
20
21#include "pism/util/Diagnostic.hh"
22#include "pism/util/Time.hh"
23#include "pism/util/error_handling.hh"
24#include "pism/util/io/io_helpers.hh"
25#include "pism/util/Logger.hh"
26#include "pism/util/pism_utilities.hh"
27#include "pism/util/Context.hh"
28
29namespace pism {
30
31Diagnostic::Diagnostic(std::shared_ptr<const Grid> grid)
32 : m_grid(grid),
33 m_sys(grid->ctx()->unit_system()),
34 m_config(grid->ctx()->config()),
35 m_fill_value(m_config->get_number("output.fill_value")) {
36 // empty
37}
38
39void Diagnostic::update(double dt) {
40 this->update_impl(dt);
41}
42
43void Diagnostic::update_impl(double dt) {
44 (void) dt;
45 // empty
46}
47
49 this->reset_impl();
50}
51
53 // empty
54}
55
56/*!
57 * Convert from external (output) units to internal units.
58 */
59double Diagnostic::to_internal(double x) const {
60 std::string
61 out = m_vars.at(0)["output_units"],
62 in = m_vars.at(0)["units"];
63 return convert(m_sys, x, out, in);
64}
65
66/*!
67 * Convert from internal to external (output) units.
68 */
69double Diagnostic::to_external(double x) const {
70 std::string
71 out = m_vars.at(0)["output_units"],
72 in = m_vars.at(0)["units"];
73 return convert(m_sys, x, in, out);
74}
75
76//! Get the number of NetCDF variables corresponding to a diagnostic quantity.
77unsigned int Diagnostic::n_variables() const {
78 return m_vars.size();
79}
80
81void Diagnostic::init(const File &input, unsigned int time) {
82 this->init_impl(input, time);
83}
84
85void Diagnostic::define_state(const File &output) const {
86 this->define_state_impl(output);
87}
88
89void Diagnostic::write_state(const File &output) const {
90 this->write_state_impl(output);
91}
92
93void Diagnostic::init_impl(const File &input, unsigned int time) {
94 (void) input;
95 (void) time;
96 // empty
97}
98
99void Diagnostic::define_state_impl(const File &output) const {
100 (void) output;
101 // empty
102}
103
104void Diagnostic::write_state_impl(const File &output) const {
105 (void) output;
106 // empty
107}
108
109//! Get a metadata object corresponding to variable number N.
111 if (N >= m_vars.size()) {
113 "variable metadata index %d is out of bounds",
114 N);
115 }
116
117 return m_vars[N];
118}
119
120void Diagnostic::define(const File &file, io::Type default_type) const {
121 this->define_impl(file, default_type);
122}
123
124//! Define NetCDF variables corresponding to a diagnostic quantity.
125void Diagnostic::define_impl(const File &file, io::Type default_type) const {
126 for (const auto &v : m_vars) {
127 io::define_spatial_variable(v, *m_grid, file, default_type);
128 }
129}
130
131std::shared_ptr<array::Array> Diagnostic::compute() const {
132 std::vector<std::string> names;
133 for (const auto &v : m_vars) {
134 names.push_back(v.get_name());
135 }
136 std::string all_names = join(names, ",");
137
138 m_grid->ctx()->log()->message(3, "- Computing %s...\n", all_names.c_str());
139 auto result = this->compute_impl();
140 m_grid->ctx()->log()->message(3, "- Done computing %s.\n", all_names.c_str());
141
142 return result;
143}
144
145TSDiagnostic::TSDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
146 : m_grid(grid),
147 m_config(grid->ctx()->config()),
148 m_sys(grid->ctx()->unit_system()),
149 m_time_name(grid->ctx()->config()->get_string("time.dimension_name")),
150 m_variable(name, m_sys),
151 m_dimension(m_time_name, m_sys),
152 m_time_bounds(m_time_name + "_bounds", m_sys) {
153
154 m_current_time = 0;
155 m_start = 0;
156
157 m_buffer_size = static_cast<size_t>(m_config->get_number("output.timeseries.buffer_size"));
158
159 m_variable["ancillary_variables"] = name + "_aux";
160
161 m_dimension.long_name("time").units(m_grid->ctx()->time()->units_string());
162 m_dimension["calendar"] = m_grid->ctx()->time()->calendar();
163 m_dimension["axis"] = "T";
164}
165
169
170void TSDiagnostic::set_units(const std::string &units,
171 const std::string &output_units) {
172 m_variable.units(units);
173
174 if (not m_config->get_flag("output.use_MKS")) {
175 m_variable.output_units(output_units);
176 }
177}
178
179TSSnapshotDiagnostic::TSSnapshotDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
180 : TSDiagnostic(grid, name) {
181 // empty
182}
183
184TSRateDiagnostic::TSRateDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
185 : TSDiagnostic(grid, name), m_accumulator(0.0), m_v_previous(0.0), m_v_previous_set(false) {
186 // empty
187}
188
189TSFluxDiagnostic::TSFluxDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
190 : TSRateDiagnostic(grid, name) {
191 // empty
192}
193
194void TSSnapshotDiagnostic::evaluate(double t0, double t1, double v) {
195
196 // skip times before the beginning of this time step
197 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] < t0) {
198 m_current_time += 1;
199 }
200
201 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] <= t1) {
202 const unsigned int k = m_current_time;
203 m_current_time += 1;
204
205 // skip the first time: it defines the beginning of a reporting interval
206 if (k == 0) {
207 continue;
208 }
209
210 const double t_s = (*m_requested_times)[k - 1];
211 const double t_e = (*m_requested_times)[k];
212
213 // store computed data in the buffer
214 {
215 m_time.push_back(t_e);
216 m_values.push_back(v);
217 m_bounds.push_back(t_s);
218 m_bounds.push_back(t_e);
219 }
220 }
221}
222
223void TSRateDiagnostic::evaluate(double t0, double t1, double change) {
224 static const double epsilon = 1e-4; // seconds
225 assert(t1 > t0);
226
227 // skip times before and including the beginning of this time step
228 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] <= t0) {
229 m_current_time += 1;
230 }
231
232 // number of requested times in this time step
233 unsigned int N = 0;
234
235 // loop through requested times that are within this time step
236 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] <= t1) {
237 const unsigned int k = m_current_time;
238 m_current_time += 1;
239
240 N += 1;
241
242 // skip the first time: it defines the beginning of a reporting interval
243 if (k == 0) {
244 continue;
245 }
246
247 const double t_s = (*m_requested_times)[k - 1];
248 const double t_e = (*m_requested_times)[k];
249
250 double rate = 0.0;
251 if (N == 1) {
252 // it is the right end-point of the first reporting interval in this time step: count the
253 // contribution from the last time step plus the one from the beginning of this time step
254 const double
255 total_change = m_accumulator + change * (t_e - t0) / (t1 - t0);
256 const double dt = t_e - t_s;
257
258 rate = total_change / dt;
259
260 } else {
261 // this reporting interval is completely contained within the time step, so the rate of change
262 // does not depend on its length
263 rate = change / (t1 - t0);
264 }
265
266 // store computed data in the buffer
267 {
268 m_time.push_back(t_e);
269 m_values.push_back(rate);
270 m_bounds.push_back(t_s);
271 m_bounds.push_back(t_e);
272 }
273
274 m_accumulator = 0.0;
275 }
276
277 if (N == 0) {
278 // if this time step contained no requested times we need to add the whole change to the
279 // accumulator
280 m_accumulator += change;
281 } else {
282 // if this time step contained some requested times we need to add the change since the last one
283 // to the accumulator
284 const double dt = t1 - (*m_requested_times)[m_current_time - 1];
285 if (dt > epsilon) {
286 m_accumulator += change * (dt / (t1 - t0));
287 }
288 }
289}
290
291void TSDiagnostic::update(double t0, double t1) {
292 this->update_impl(t0, t1);
293}
294
295void TSSnapshotDiagnostic::update_impl(double t0, double t1) {
296 static const double epsilon = 1e-4; // seconds
297
298 if (fabs(t1 - t0) < epsilon) {
299 return;
300 }
301
302 assert(t1 > t0);
303
304 evaluate(t0, t1, this->compute());
305}
306
307void TSRateDiagnostic::update_impl(double t0, double t1) {
308 const double v = this->compute();
309
310 if (m_v_previous_set) {
311 assert(t1 > t0);
312 evaluate(t0, t1, v - m_v_previous);
313 }
314
315 m_v_previous = v;
316 m_v_previous_set = true;
317}
318
319void TSFluxDiagnostic::update_impl(double t0, double t1) {
320 static const double epsilon = 1e-4; // seconds
321
322 if (fabs(t1 - t0) < epsilon) {
323 return;
324 }
325
326 assert(t1 > t0);
327
328 evaluate(t0, t1, this->compute());
329}
330
332
333 if (m_time.empty()) {
334 return;
335 }
336
337 auto time_name = m_config->get_string("time.dimension_name");
338
340 io::PISM_READWRITE); // OK to use netcdf3
341
342 unsigned int len = file.dimension_length(time_name);
343
344 if (len > 0) {
345 // Note: does not perform unit conversion of the time read from the file. This should
346 // be OK because this file was written by PISM.
347 double last_time = 0;
348 file.read_variable(time_name, {len - 1}, {1}, &last_time);
349
350 if (last_time < m_time.front()) {
351 m_start = len;
352 }
353 }
354
355 if (len == m_start) {
358
361 }
362
365
366 m_start += m_time.size();
367
368 {
369 m_time.clear();
370 m_bounds.clear();
371 m_values.clear();
372 }
373}
374
375void TSDiagnostic::init(const File &output_file,
376 std::shared_ptr<std::vector<double>> requested_times) {
377 m_output_filename = output_file.name();
378
379 m_requested_times = std::move(requested_times);
380
381 // Get the number of records in the file (for appending):
383}
384
386 return m_variable;
387}
388
389} // end of namespace pism
void write_state(const File &output) const
Definition Diagnostic.cc:89
virtual void write_state_impl(const File &output) const
virtual std::shared_ptr< array::Array > compute_impl() const =0
virtual void reset_impl()
Definition Diagnostic.cc:52
void define(const File &file, io::Type default_type) const
const units::System::Ptr m_sys
the unit system
double to_internal(double x) const
Definition Diagnostic.cc:59
std::vector< SpatialVariableMetadata > m_vars
metadata corresponding to NetCDF variables
virtual void define_state_impl(const File &output) const
Definition Diagnostic.cc:99
void init(const File &input, unsigned int time)
Definition Diagnostic.cc:81
virtual void update_impl(double dt)
Definition Diagnostic.cc:43
Diagnostic(std::shared_ptr< const Grid > g)
Definition Diagnostic.cc:31
double to_external(double x) const
Definition Diagnostic.cc:69
void define_state(const File &output) const
Definition Diagnostic.cc:85
virtual void define_impl(const File &file, io::Type default_type) const
Define NetCDF variables corresponding to a diagnostic quantity.
unsigned int n_variables() const
Get the number of NetCDF variables corresponding to a diagnostic quantity.
Definition Diagnostic.cc:77
void update(double dt)
Definition Diagnostic.cc:39
std::shared_ptr< const Grid > m_grid
the grid
virtual void init_impl(const File &input, unsigned int time)
Definition Diagnostic.cc:93
std::shared_ptr< array::Array > compute() const
Compute a diagnostic quantity and return a pointer to a newly-allocated Array.
SpatialVariableMetadata & metadata(unsigned int N=0)
Get a metadata object corresponding to variable number N.
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:699
std::string name() const
Definition File.cc:274
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition File.cc:420
High-level PISM I/O class.
Definition File.hh:55
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
Spatial NetCDF variable (corresponding to a 2D or 3D scalar field).
virtual ~TSDiagnostic()
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
VariableMetadata m_dimension
unsigned int m_current_time
index into m_times
unsigned int m_start
starting index used when flushing the buffer
VariableMetadata m_time_bounds
std::vector< double > m_bounds
size_t m_buffer_size
size of the buffer used to store data
VariableMetadata m_variable
TSDiagnostic(std::shared_ptr< const Grid > g, const std::string &name)
std::string m_output_filename
const Config::ConstPtr m_config
Configuration flags and parameters.
std::vector< double > m_time
virtual double compute()=0
void init(const File &output_file, std::shared_ptr< std::vector< double > > requested_times)
const VariableMetadata & metadata() const
virtual void update_impl(double t0, double t1)=0
std::shared_ptr< const Grid > m_grid
the grid
void update(double t0, double t1)
PISM's scalar time-series diagnostics.
void update_impl(double t0, double t1)
TSFluxDiagnostic(std::shared_ptr< const Grid > g, const std::string &name)
double m_accumulator
accumulator of changes (used to compute rates of change)
void update_impl(double t0, double t1)
TSRateDiagnostic(std::shared_ptr< const Grid > g, const std::string &name)
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)
TSSnapshotDiagnostic(std::shared_ptr< const Grid > g, const std::string &name)
void update_impl(double t0, double t1)
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & output_units(const std::string &input)
std::string get_name() const
#define PISM_ERROR_LOCATION
@ PISM_NETCDF3
Definition IO_Flags.hh:57
@ PISM_READWRITE
open an existing file for reading and writing
Definition IO_Flags.hh:70
@ PISM_DOUBLE
Definition IO_Flags.hh:52
void define_time_bounds(const VariableMetadata &var, const std::string &dimension_name, const std::string &bounds_name, const File &file, io::Type nctype)
void write_time_bounds(const File &file, const VariableMetadata &metadata, size_t t_start, const std::vector< double > &data)
void define_spatial_variable(const SpatialVariableMetadata &metadata, const Grid &grid, const File &file, io::Type default_type)
Define a NetCDF variable corresponding to a VariableMetadata object.
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_timeseries(const VariableMetadata &var, const std::string &dimension_name, const File &file, io::Type nctype)
Define a NetCDF variable corresponding to a time-series.
static const double k
Definition exactTestP.cc:42
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.