PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
Diagnostic.cc
Go to the documentation of this file.
1/* Copyright (C) 2015, 2016, 2017, 2019, 2020, 2021, 2022, 2023, 2024, 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#include <cmath>
20#include <memory>
21
22#include "pism/util/Context.hh"
23#include "pism/util/Diagnostic.hh"
24#include "pism/util/Logger.hh"
25#include "pism/util/Time.hh"
26#include "pism/util/Units.hh"
27#include "pism/util/VariableMetadata.hh"
28#include "pism/util/error_handling.hh"
29#include "pism/util/io/OutputWriter.hh"
30#include "pism/util/pism_utilities.hh"
31
32namespace pism {
33
34Diagnostic::Diagnostic(std::shared_ptr<const Grid> grid)
35 : m_grid(grid),
36 m_sys(grid->ctx()->unit_system()),
37 m_config(grid->ctx()->config()),
38 m_fill_value(m_config->get_number("output.fill_value")) {
39 // empty
40}
41
42void Diagnostic::update(double dt) {
43 this->update_impl(dt);
44}
45
46void Diagnostic::update_impl(double dt) {
47 (void) dt;
48 // empty
49}
50
52 this->reset_impl();
53}
54
56 // empty
57}
58
59/*!
60 * Convert from external (output) units to internal units.
61 */
62double Diagnostic::to_internal(double x) const {
63 std::string
64 out = m_vars.at(0)["output_units"],
65 in = m_vars.at(0)["units"];
66 return convert(m_sys, x, out, in);
67}
68
69/*!
70 * Convert from internal to external (output) units.
71 */
72double Diagnostic::to_external(double x) const {
73 std::string
74 out = m_vars.at(0)["output_units"],
75 in = m_vars.at(0)["units"];
76 return convert(m_sys, x, in, out);
77}
78
79//! Get the number of NetCDF variables corresponding to a diagnostic quantity.
80unsigned int Diagnostic::n_variables() const {
81 return m_vars.size();
82}
83
84std::set<VariableMetadata> Diagnostic::state() const {
85 return state_impl();
86}
87
88std::set<VariableMetadata> Diagnostic::state_impl() const {
89 return {};
90}
91
92void Diagnostic::init(const File &input, unsigned int time) {
93 this->init_impl(input, time);
94}
95
96void Diagnostic::write_state(const OutputFile &output) const {
97 this->write_state_impl(output);
98}
99
100void Diagnostic::init_impl(const File &input, unsigned int time) {
101 (void) input;
102 (void) time;
103 // empty
104}
105
106void Diagnostic::write_state_impl(const OutputFile &output) const {
107 (void) output;
108 // empty
109}
110
111//! Get a metadata object corresponding to variable number N.
113 if (N >= m_vars.size()) {
115 "variable metadata index %d is out of bounds",
116 N);
117 }
118
119 return m_vars[N];
120}
121
123 return m_grid->info();
124}
125
126std::shared_ptr<array::Array> Diagnostic::compute() const {
127 std::vector<std::string> names;
128 for (const auto &v : m_vars) {
129 names.push_back(v.get_name());
130 }
131 std::string all_names = join(names, ",");
132
133 m_grid->ctx()->log()->message(3, "- Computing %s...\n", all_names.c_str());
134 auto result = this->compute_impl();
135 m_grid->ctx()->log()->message(3, "- Done computing %s.\n", all_names.c_str());
136
137 return result;
138}
139
140TSDiagnostic::TSDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
141 : m_grid(grid),
142 m_config(grid->ctx()->config()),
143 m_sys(grid->ctx()->unit_system()),
144 m_variable(name, m_sys) {
145
146 m_current_time = 0;
147 m_start = 0;
148
149 m_buffer_size = static_cast<size_t>(m_config->get_number("output.scalar.buffer_size"));
150
151 m_variable["ancillary_variables"] = name + "_aux";
153}
154
158
159void TSDiagnostic::set_units(const std::string &units,
160 const std::string &output_units) {
161 m_variable.units(units);
162 m_variable.output_units(output_units);
163}
164
165TSSnapshotDiagnostic::TSSnapshotDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
166 : TSDiagnostic(grid, name) {
167 // empty
168}
169
170TSRateDiagnostic::TSRateDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
171 : TSDiagnostic(grid, name), m_accumulator(0.0), m_v_previous(0.0), m_v_previous_set(false) {
172 // empty
173}
174
175TSFluxDiagnostic::TSFluxDiagnostic(std::shared_ptr<const Grid> grid, const std::string &name)
176 : TSRateDiagnostic(grid, name) {
177 // empty
178}
179
180void TSSnapshotDiagnostic::evaluate(double t0, double t1, double v) {
181
182 // skip times before the beginning of this time step
183 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] < t0) {
184 m_current_time += 1;
185 }
186
187 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] <= t1) {
188 const unsigned int k = m_current_time;
189 m_current_time += 1;
190
191 // skip the first time: it defines the beginning of a reporting interval
192 if (k == 0) {
193 continue;
194 }
195
196 const double t_s = (*m_requested_times)[k - 1];
197 const double t_e = (*m_requested_times)[k];
198
199 // store computed data in the buffer
200 {
201 m_time.push_back(t_e);
202 m_values.push_back(v);
203 m_bounds.push_back(t_s);
204 m_bounds.push_back(t_e);
205 }
206 }
207}
208
209void TSRateDiagnostic::evaluate(double t0, double t1, double change) {
210 static const double epsilon = 1e-4; // seconds
211 assert(t1 > t0);
212
213 // skip times before and including the beginning of this time step
214 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] <= t0) {
215 m_current_time += 1;
216 }
217
218 // number of requested times in this time step
219 unsigned int N = 0;
220
221 // loop through requested times that are within this time step
222 while (m_current_time < m_requested_times->size() and (*m_requested_times)[m_current_time] <= t1) {
223 const unsigned int k = m_current_time;
224 m_current_time += 1;
225
226 N += 1;
227
228 // skip the first time: it defines the beginning of a reporting interval
229 if (k == 0) {
230 continue;
231 }
232
233 const double t_s = (*m_requested_times)[k - 1];
234 const double t_e = (*m_requested_times)[k];
235
236 double rate = 0.0;
237 if (N == 1) {
238 // it is the right end-point of the first reporting interval in this time step: count the
239 // contribution from the last time step plus the one from the beginning of this time step
240 const double
241 total_change = m_accumulator + change * (t_e - t0) / (t1 - t0);
242 const double dt = t_e - t_s;
243
244 rate = total_change / dt;
245
246 } else {
247 // this reporting interval is completely contained within the time step, so the rate of change
248 // does not depend on its length
249 rate = change / (t1 - t0);
250 }
251
252 // store computed data in the buffer
253 {
254 m_time.push_back(t_e);
255 m_values.push_back(rate);
256 m_bounds.push_back(t_s);
257 m_bounds.push_back(t_e);
258 }
259
260 m_accumulator = 0.0;
261 }
262
263 if (N == 0) {
264 // if this time step contained no requested times we need to add the whole change to the
265 // accumulator
266 m_accumulator += change;
267 } else {
268 // if this time step contained some requested times we need to add the change since the last one
269 // to the accumulator
270 const double dt = t1 - (*m_requested_times)[m_current_time - 1];
271 if (dt > epsilon) {
272 m_accumulator += change * (dt / (t1 - t0));
273 }
274 }
275}
276
277void TSDiagnostic::update(double t0, double t1) {
278 this->update_impl(t0, t1);
279}
280
281void TSSnapshotDiagnostic::update_impl(double t0, double t1) {
282 static const double epsilon = 1e-4; // seconds
283
284 if (fabs(t1 - t0) < epsilon) {
285 return;
286 }
287
288 assert(t1 > t0);
289
290 evaluate(t0, t1, this->compute());
291}
292
293void TSRateDiagnostic::update_impl(double t0, double t1) {
294 const double v = this->compute();
295
296 if (m_v_previous_set) {
297 assert(t1 > t0);
298 evaluate(t0, t1, v - m_v_previous);
299 }
300
301 m_v_previous = v;
302 m_v_previous_set = true;
303}
304
305void TSFluxDiagnostic::update_impl(double t0, double t1) {
306 static const double epsilon = 1e-4; // seconds
307
308 if (fabs(t1 - t0) < epsilon) {
309 return;
310 }
311
312 assert(t1 > t0);
313
314 evaluate(t0, t1, this->compute());
315}
316
318
319 if (m_time.empty()) {
320 return;
321 }
322
323 auto &file = *m_output_file;
324 auto len = file.time_dimension_length();
325
326 if (len > 0) {
327 // Note: does not perform unit conversion of the time read from the file. This should
328 // be OK because this file was written by PISM.
329 double last_time = file.last_time_value();
330
331 if (last_time < m_time.front()) {
332 m_start = len;
333 }
334 }
335
336 if (len == m_start) {
337 bool with_bounds = true;
338 auto time = m_grid->ctx()->time()->metadata(with_bounds).get_name();
339 auto time_bounds = m_grid->ctx()->time()->bounds_metadata().get_name();
340
341 // write requested times
342 file.write_array(time, { m_start }, { (unsigned int)m_time.size() }, m_time);
343 // write time bounds
344 file.write_array(time_bounds, { m_start, 0 }, { (unsigned int)m_bounds.size() / 2, 2 },
345 m_bounds);
346 }
347
348 // Convert to output units, if necessary
349 //
350 // Note that data in m_values are not used after this call, so we can perform this
351 // conversion in place (without making a copy).
352 if (not m_config->get_flag("output.use_MKS")) {
353
354 std::string units = metadata()["units"];
355 std::string output_units = metadata()["output_units"];
356
357 bool use_output_units =
358 (not units.empty() and not output_units.empty() and units != output_units);
359
360 if (use_output_units) {
361 units::Converter converter(metadata().unit_system(), units, output_units);
362
363 converter.convert_doubles(m_values.data(), m_values.size());
364 }
365 }
366
367 // write values of a diagnostic
368 file.write_timeseries(m_variable.get_name(), { m_start }, { (unsigned int)m_values.size() },
369 m_values);
370
371 file.sync();
372
373 m_start += m_time.size();
374
375 {
376 m_time.clear();
377 m_bounds.clear();
378 m_values.clear();
379 }
380}
381
382void TSDiagnostic::init(std::shared_ptr<OutputFile> output_file,
383 std::shared_ptr<std::vector<double>> requested_times) {
384 m_output_file = output_file;
385
386 m_requested_times = std::move(requested_times);
387
388 // Get the number of records in the file (for appending):
389 m_start = output_file->time_dimension_length();
390}
391
393 return m_variable;
394}
395
396} // end of namespace pism
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
virtual void reset_impl()
Definition Diagnostic.cc:55
VariableMetadata & metadata(unsigned int N=0)
Get a metadata object corresponding to variable number N.
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
double to_internal(double x) const
Definition Diagnostic.cc:62
virtual void write_state_impl(const OutputFile &output) const
void init(const File &input, unsigned int time)
Definition Diagnostic.cc:92
virtual void update_impl(double dt)
Definition Diagnostic.cc:46
Diagnostic(std::shared_ptr< const Grid > g)
Definition Diagnostic.cc:34
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.
High-level PISM I/O class.
Definition File.hh:57
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
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
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
TSDiagnostic(std::shared_ptr< const Grid > g, const std::string &name)
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)
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 & units(const std::string &input)
VariableMetadata & output_units(const std::string &input)
VariableMetadata & set_time_dependent(bool flag)
std::string get_name() const
void convert_doubles(double *data, size_t length) const
Definition Units.cc:255
#define PISM_ERROR_LOCATION
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.