PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
Forcing.cc
Go to the documentation of this file.
1 // Copyright (C) 2009--2023 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 <petsc.h>
20 #include <cassert>
21 #include <cmath> // std::floor
22 #include <array>
23 
24 #include "pism/util/array/Forcing.hh"
25 #include "pism/util/io/File.hh"
26 #include "pism/util/pism_utilities.hh"
27 #include "pism/util/Time.hh"
28 #include "pism/util/Grid.hh"
29 #include "pism/util/ConfigInterface.hh"
30 
31 #include "pism/util/error_handling.hh"
32 #include "pism/util/io/io_helpers.hh"
33 #include "pism/util/Logger.hh"
34 #include "pism/util/interpolation.hh"
35 #include "pism/util/Context.hh"
36 #include "pism/util/array/Array_impl.hh"
37 #include "pism/util/VariableMetadata.hh"
38 #include "pism/util/io/IO_Flags.hh"
39 
40 namespace pism {
41 namespace array {
42 
43 struct Forcing::Data {
44  Data()
45  : array(nullptr),
46  first(-1),
47  n_records(0),
48  period(0.0),
49  period_start(0.0) {
50  // empty
51  }
52  //! all the times available in filename
53  std::vector<double> time;
54 
55  //! the range of times covered by data in `filename`
56  std::array<double,2> time_range;
57 
58  //! name of the file to read (regrid) from
59  std::string filename;
60 
61  //! DM with dof equal to buffer_size
62  std::shared_ptr<petsc::DM> da;
63 
64  //! a 3D Vec used to store records
66 
67  //! pointer used to access records stored in memory
68  double ***array;
69 
70  //! maximum number of records stored in memory
71  unsigned int buffer_size;
72 
73  //! in-file index of the first record stored in memory (a signed `int` to allow
74  //! `first==-1` as an *invalid* first value)
75  int first;
76 
77  //! number of records currently kept in memory
78  unsigned int n_records;
79 
80  //! temporal interpolation type
82 
83  //! temporal interpolation code
84  std::shared_ptr<Interpolation> interp;
85 
86  //! forcing period, in seconds
87  double period;
88 
89  //! start of the period, in seconds
90  double period_start;
91 
92  //! minimum time step length in max_timestep(), in seconds
93  double dt_min;
94 };
95 
96 /*!
97  * Allocate an instance that will be used to load and use a forcing field from a file.
98  *
99  * Checks the number of records in a file and allocates storage accordingly.
100  *
101  * If `periodic` is true, allocate enough storage to hold all the records, otherwise
102  * allocate storage for at most `max_buffer_size` records.
103  *
104  * @param[in] grid computational grid
105  * @param[in] file input file
106  * @param[in] short_name variable name in `file`
107  * @param[in] standard_name standard name (if available); leave blank to ignore
108  * @param[in] max_buffer_size maximum buffer size for non-periodic fields
109  * @param[in] periodic true if this forcing field should be interpreted as periodic
110  * @param[in] interpolation_type type of temporal interpolation (LINEAR or PIECEWISE_CONSTANT)
111  */
112 Forcing::Forcing(std::shared_ptr<const Grid> grid,
113  const File &file,
114  const std::string &short_name,
115  const std::string &standard_name,
116  unsigned int max_buffer_size,
117  bool periodic,
118  InterpolationType interpolation_type)
119  : array::Scalar(grid, short_name, 0),
120  m_data(new Data()) {
121 
122  unsigned int n_records = file.nrecords(short_name, standard_name,
123  grid->ctx()->unit_system());
124 
125  unsigned int buffer_size = 0;
126  if (periodic) {
127  // In the periodic case we try to keep all the records in RAM.
128  buffer_size = n_records;
129 
130  if (interpolation_type == LINEAR) {
131  // add two more records at the beginning and the end of the period to simplify
132  // interpolation in time
133  buffer_size += 2;
134  }
135  } else {
136  buffer_size = std::min(n_records, max_buffer_size);
137  }
138 
139  // Allocate storage for one record if the variable was not found. This is needed to be
140  // able to cheaply allocate and then discard an "-atmosphere given" model
141  // (atmosphere::Given) when "-surface given" (Given) is selected.
143 
144  allocate(buffer_size, interpolation_type);
145 }
146 
147 std::shared_ptr<Forcing> Forcing::Constant(std::shared_ptr<const Grid> grid,
148  const std::string &short_name,
149  double value) {
150  // note: cannot use std::make_shared because of a private constructor
151  std::shared_ptr<Forcing> result(new Forcing(grid, short_name, 1,
153 
154  // set constant value everywhere
155  result->set(value);
156  result->set_record(0);
157 
158  // set the time to zero
159  result->m_data->time = {0.0};
160  result->m_data->n_records = 1;
161  result->m_data->first = 0;
162 
163  // set fake time bounds:
164  double eps = 0.5 * result->m_data->dt_min;
165  result->m_data->time_range = {-eps, eps};
166 
167  return result;
168 }
169 
170 Forcing::Forcing(std::shared_ptr<const Grid> grid, const std::string &short_name,
171  unsigned int buffer_size,
172  InterpolationType interpolation_type)
173  : array::Scalar(grid, short_name, 0),
174  m_data(new Data()) {
175  allocate(buffer_size, interpolation_type);
176 }
177 
178 void Forcing::allocate(unsigned int buffer_size, InterpolationType interpolation_type) {
179  m_impl->report_range = false;
180 
181  m_data->interp_type = interpolation_type;
183 
184  auto config = m_impl->grid->ctx()->config();
185 
186  m_data->dt_min = config->get_number("time_stepping.resolution");
187 
188  if (not (m_data->interp_type == PIECEWISE_CONSTANT or
189  m_data->interp_type == LINEAR)) {
190  throw RuntimeError(PISM_ERROR_LOCATION, "unsupported interpolation type");
191  }
192 
193  // initialize the m_data->da member:
194  m_data->da = m_impl->grid->get_dm(buffer_size, this->m_impl->da_stencil_width);
195 
196  // allocate the 3D Vec:
197  PetscErrorCode ierr = DMCreateGlobalVector(*m_data->da, m_data->v.rawptr());
198  PISM_CHK(ierr, "DMCreateGlobalVector");
199 }
200 
202  delete m_data;
203 }
204 
205 unsigned int Forcing::buffer_size() {
206  return m_data->buffer_size;
207 }
208 
209 double*** Forcing::array3() {
210  return m_data->array;
211 }
212 
213 void Forcing::begin_access() const {
214  if (m_impl->access_counter == 0) {
215  PetscErrorCode ierr = DMDAVecGetArrayDOF(*m_data->da, m_data->v, &m_data->array);
216  PISM_CHK(ierr, "DMDAVecGetArrayDOF");
217  }
218 
219  // this call will increment the m_access_counter
221 }
222 
223 void Forcing::end_access() const {
224  // this call will decrement the m_access_counter
226 
227  if (m_impl->access_counter == 0) {
228  PetscErrorCode ierr = DMDAVecRestoreArrayDOF(*m_data->da, m_data->v, &m_data->array);
229  PISM_CHK(ierr, "DMDAVecRestoreArrayDOF");
230  m_data->array = nullptr;
231  }
232 }
233 
234 void Forcing::init(const std::string &filename, bool periodic) {
235  try {
236  auto ctx = m_impl->grid->ctx();
237  auto time = ctx->time();
238 
239  m_data->filename = filename;
240 
242  auto var = file.find_variable(m_impl->metadata[0].get_name(),
243  m_impl->metadata[0]["standard_name"]);
244  if (not var.exists) {
245  throw RuntimeError(PISM_ERROR_LOCATION, "variable not found");
246  }
247 
248  auto time_name = io::time_dimension(ctx->unit_system(), file, var.name);
249 
250  // dimension_length() will return 0 if a dimension is missing
251  bool one_record = file.dimension_length(time_name) < 2;
252 
253  if (not one_record) {
254  std::vector<double> times{};
255  std::vector<double> bounds{};
256  io::read_time_info(*ctx->log(), ctx->unit_system(),
257  file, time_name, time->units_string(),
258  times, bounds);
259 
260  if (periodic) {
261  m_data->period_start = bounds.front();
262  m_data->period = bounds.back() - bounds.front();
263  }
264 
266  // Time bounds data overrides the time variable: we make t[j] be the
267  // left end-point of the j-th interval
268  for (unsigned int k = 0; k < times.size(); ++k) {
269  times[k] = bounds[2*k + 0];
270  }
271  }
272 
273  m_data->time = times;
274  m_data->time_range = {bounds.front(), bounds.back()};
275 
276  bool extrapolate = ctx->config()->get_flag("input.forcing.time_extrapolation");
277  if (not (extrapolate or periodic)) {
278  check_forcing_duration(*time, bounds.front(), bounds.back());
279  }
280 
281  } else {
282  // Only one time record or no time dimension at all: set fake time bounds assuming
283  // that the user wants to use constant-in-time forcing for the whole simulation
284 
285  // this value does not matter
286  m_data->time = {0.0};
287 
288  // set fake time bounds:
289  double eps = 0.5 * m_data->dt_min;
290  m_data->time_range = {-eps, eps};
291 
292  // note that in this case all data is periodic and constant in time
293  m_data->period = 0.0;
294  m_data->period_start = 0.0;
295  }
296 
297  if (periodic) {
298  // read periodic data right away (we need to hold it all in memory anyway)
299  init_periodic_data(file);
300  }
301  } catch (RuntimeError &e) {
302  e.add_context("reading %s (%s) from '%s'",
303  m_impl->metadata[0].get_string("long_name").c_str(),
304  m_impl->metadata[0].get_name().c_str(),
305  m_data->filename.c_str());
306  throw;
307  }
308 }
309 
310 /*!
311  * Read all periodic data from the file and add two more records to simplify
312  * interpolation.
313  */
315 
316  auto ctx = grid()->ctx();
317 
318  auto name = get_name();
319  auto n_records = file.nrecords(name, metadata()["standard_name"],
320  ctx->unit_system());
321 
322  auto buffer_required = n_records + 2 * static_cast<int>(m_data->interp_type == LINEAR);
323 
324  if (m_data->buffer_size < buffer_required) {
326  "the buffer is too small to contain periodic data");
327  }
328 
329  int offset = m_data->interp_type == LINEAR ? 1 : 0;
330 
331  // Read all the records and store them. The index offset leaves room for an extra record
332  // needed to simplify interpolation
333  auto variable = m_impl->metadata[0];
334  auto V = file.find_variable(variable.get_name(), variable["standard_name"]);
335 
336  grid::InputGridInfo input_grid(file, V.name, variable.unit_system(), grid()->registration());
337 
338  LocalInterpCtx lic(input_grid, *grid(), levels(), m_impl->interpolation_type);
339 
340  for (unsigned int j = 0; j < n_records; ++j) {
341  {
342  lic.start[T_AXIS] = (int)j;
343  lic.count[T_AXIS] = 1;
344 
345  petsc::VecArray tmp_array(vec());
346  io::regrid_spatial_variable(variable, *grid(), lic, file, tmp_array.get());
347  }
348 
349  auto time = ctx->time();
350  auto log = ctx->log();
351  log->message(5, " %s: reading entry #%02d, time %s...\n", name.c_str(), j,
352  time->date(m_data->time[j]).c_str());
353 
354  set_record(offset + j);
355  }
356 
357  m_data->n_records = buffer_required;
358  m_data->first = 0;
359 
361  return;
362  }
363 
364  double t0 = m_data->time_range[0];
365  double t1 = m_data->time_range[1];
366 
367  // compute the interpolation factor used to find the value at the beginning of the
368  // period
369  double alpha = 0.0;
370  {
371  double dt1 = m_data->time.front() - t0;
372  double dt2 = t1 - m_data->time.back();
373 
374  alpha = dt1 + dt2 > 0 ? dt2 / (dt1 + dt2) : 0.0;
375  }
376 
377  // indexes used to access the first and the last entry in the buffer
378  int first = 1;
379  int last = buffer_required - 2;
380 
381  array::AccessScope list{ this };
382 
383  // compute values at the beginning (and so at the end) of the period
384  double **a2 = array();
385  double ***a3 = array3();
386  for (auto p = grid()->points(); p; p.next()) {
387  const int i = p.i(), j = p.j();
388 
389  a2[j][i] = (1.0 - alpha) * a3[j][i][last] + alpha * a3[j][i][first];
390  }
391 
392  set_record(0);
394 
395  // add two more records to m_data->time
396  {
397  if (m_data->time[0] - t0 > 0.0) {
398  m_data->time.insert(m_data->time.begin(), t0);
399  } else {
400  // The first time record is at the beginning of the time interval covered by
401  // forcing. This means that the first record we added (set_record(0) above) is the
402  // same as the second one (at index 1). Only one of them is needed.
403  //
404  // Here we use an arbitrary time *before* the beginning of the forcing time interval
405  // to ensure that m_data->time is strictly increasing.
406  //
407  // Note: this time will not be used.
408  const double dt = 1.0; // arbitrary; could be any positive number
409  m_data->time.insert(m_data->time.begin(), t0 - dt);
410  }
411  if (t1 - m_data->time.back() > 0.0) {
412  m_data->time.push_back(t1);
413  } else {
414  // The last time record is at the end of the time interval covered by forcing. This
415  // means that the last record we added (set_record(m_data->buffer_size - 1) above)
416  // is the same as the one before it. Only one of them is needed.
417  //
418  // Here we use an arbitrary time *after* the end of the forcing time interval to
419  // ensure that m_data->time is strictly increasing.
420  //
421  // Note: this time will not be used.
422  const double dt = 1.0; // arbitrary; could be any positive number
423  m_data->time.push_back(t1 + dt);
424  }
425  }
426 }
427 
428 //! Read some data to make sure that the interval (t, t + dt) is covered.
429 void Forcing::update(double t, double dt) {
430 
431  if (m_data->filename.empty()) {
432  // We are not reading data from a file.
433  return;
434  }
435 
436  if (m_data->period > 0.0) {
437  // we read all data in Forcing::init() (see above)
438  return;
439  }
440 
441  if (m_data->n_records > 0) {
442  // in-file index of the last record currently in memory
443  unsigned int last = m_data->first + (m_data->n_records - 1);
444 
445  double t0{}, t1{};
446  if (m_data->interp_type == LINEAR) {
447  t0 = m_data->time[m_data->first];
448  t1 = m_data->time[last];
449  } else {
450  // piece-wise constant
451  t0 = m_data->time[m_data->first];
452  if (last + 1 < m_data->time.size()) {
453  t1 = m_data->time[last + 1];
454  } else {
455  t1 = m_data->time_range[1];
456  }
457  }
458 
459  // just return if we have all the data we need:
460  if (t >= t0 and t + dt <= t1) {
461  return;
462  }
463  }
464 
465  Interpolation I(m_data->interp_type, m_data->time, { t, t + dt });
466 
467  unsigned int first = I.left(0), last = I.right(1), N = last - first + 1;
468 
469  // check if all the records necessary to cover this interval fit in the
470  // buffer:
471  if (N > m_data->buffer_size) {
473  "cannot read %d records of %s (buffer size: %d)", N,
474  m_impl->name.c_str(), m_data->buffer_size);
475  }
476 
477  update(first);
478 }
479 
480 //! Update by reading at most buffer_size records from the file.
481 void Forcing::update(unsigned int start) {
482 
483  unsigned int time_size = (int)m_data->time.size();
484 
485  if (start >= time_size) {
487  "Forcing::update(int start): start = %d is invalid", start);
488  }
489 
490  unsigned int missing = std::min(m_data->buffer_size, time_size - start);
491 
492  if (start == static_cast<unsigned int>(m_data->first)) {
493  // nothing to do
494  return;
495  }
496 
497  int kept = 0;
498  if (m_data->first >= 0) {
499  unsigned int last = m_data->first + (m_data->n_records - 1);
500  if ((m_data->n_records > 0) && (start >= (unsigned int)m_data->first) && (start <= last)) {
501  int discarded = start - m_data->first;
502  kept = last - start + 1;
503  discard(discarded);
504  missing -= kept;
505  start += kept;
506  m_data->first += discarded;
507  } else {
508  m_data->first = start;
509  }
510  } else {
511  m_data->first = start;
512  }
513 
514  if (missing <= 0) {
515  return;
516  }
517 
518  m_data->n_records = kept + missing;
519 
520  auto t = m_impl->grid->ctx()->time();
521 
522  auto log = m_impl->grid->ctx()->log();
523  if (this->buffer_size() > 1) {
524  log->message(4,
525  " reading \"%s\" into buffer\n"
526  " (short_name = %s): %d records, time %s through %s...\n",
527  metadata().get_string("long_name").c_str(), m_impl->name.c_str(), missing,
528  t->date(m_data->time[start]).c_str(),
529  t->date(m_data->time[start + missing - 1]).c_str());
530  m_impl->report_range = false;
531  } else {
532  m_impl->report_range = true;
533  }
534 
536 
537  auto variable = m_impl->metadata[0];
538 
539  try {
540  auto V = file.find_variable(variable.get_name(), variable["standard_name"]);
541  grid::InputGridInfo input_grid(file, V.name, variable.unit_system(), grid()->registration());
542 
543  LocalInterpCtx lic(input_grid, *grid(), levels(), m_impl->interpolation_type);
544 
545  for (unsigned int j = 0; j < missing; ++j) {
546  lic.start[T_AXIS] = (int)(start + j);
547  lic.count[T_AXIS] = 1;
548 
549  petsc::VecArray tmp_array(vec());
550  io::regrid_spatial_variable(variable, *m_impl->grid, lic, file, tmp_array.get());
551 
552  log->message(5, " %s: reading entry #%02d, year %s...\n", m_impl->name.c_str(), start + j,
553  t->date(m_data->time[start + j]).c_str());
554 
555  set_record(kept + j);
556  }
557  } catch (RuntimeError &e) {
558  e.add_context("regridding '%s' from '%s'", this->get_name().c_str(), m_data->filename.c_str());
559  throw;
560  }
561 }
562 
563 //! Discard the first N records, shifting the rest of them towards the "beginning".
564 void Forcing::discard(int number) {
565 
566  if (number == 0) {
567  return;
568  }
569 
570  m_data->n_records -= number;
571 
572  array::AccessScope l{this};
573 
574  double ***a3 = array3();
575  for (auto p = m_impl->grid->points(); p; p.next()) {
576  const int i = p.i(), j = p.j();
577 
578  for (unsigned int k = 0; k < m_data->n_records; ++k) {
579  a3[j][i][k] = a3[j][i][k + number];
580  }
581  }
582 }
583 
584 //! Sets the record number n to the contents of the (internal) Vec v.
586 
587  array::AccessScope l{this};
588 
589  double **a2 = array();
590  double ***a3 = array3();
591  for (auto p = m_impl->grid->points(); p; p.next()) {
592  const int i = p.i(), j = p.j();
593  a3[j][i][n] = a2[j][i];
594  }
595 }
596 
597 //! @brief Given the time t determines the maximum possible time-step this Forcing
598 //! allows.
600  auto time_size = m_data->time.size();
601 
602  if (m_data->period > 0.0) {
603  // all periodic data is stored in RAM and there is no time step restriction
604  return {};
605  }
606 
607  if (t >= m_data->time.back()) {
608  // Reached the end of forcing: no time step restriction. We will need only one record
609  // to use constant extrapolation and it will surely fit in the buffer.
610  return {};
611  }
612 
613  // find the index k such that m_data->time[k] <= x < m_data->time[k + 1]
614  // Note: `L` below will be strictly less than `time_size - 1`.
615  size_t L = gsl_interp_bsearch(m_data->time.data(), t, 0, time_size - 1);
616 
617  // find the index of the last record we could read in given the size of the buffer
618  size_t R = L + m_data->buffer_size - 1;
619 
620  if (R >= time_size - 1) {
621  // We can read all the remaining records: no time step restriction from now on
622  return {};
623  }
624 
626  // in the piece-wise constant case we can go all the way to the *next* record
627  R = std::min(R + 1, time_size - 1);
628  return m_data->time[R] - t;
629  }
630 
631  return m_data->time[R] - t;
632 }
633 
634 /*
635  * @brief Initialize Forcing with the value at time `t`.
636  *
637  * @note This method does not check if an update() call is necessary!
638  *
639  * @param[in] t requested time
640  *
641  */
642 void Forcing::interp(double t) {
643 
644  init_interpolation({t});
645 
646  // There is only one point to interpolate at ("t" above). Here we get interpolation
647  // indexes and the corresponding weight. Here L == R for the piecewise-constant
648  // interpolation.
649  int L = m_data->interp->left(0);
650  int R = m_data->interp->right(0);
651  double alpha = m_data->interp->alpha(0);
652 
653  array::AccessScope l{this};
654  double ***a3 = array3();
655  double **a2 = array();
656 
657  for (auto p = m_impl->grid->points(); p; p.next()) {
658  const int i = p.i(), j = p.j();
659  auto *column = a3[j][i];
660  // result (LHS) is a weighted average of two values.
661  a2[j][i] = column[L] + alpha * (column[R] - column[L]);
662  }
663 }
664 
665 
666 /**
667  * Compute the average value over the time interval `[t, t + dt]`.
668  *
669  * @param t start of the time interval, in seconds
670  * @param dt length of the time interval, in seconds
671  *
672  */
673 void Forcing::average(double t, double dt) {
674 
675  // if only one record, nothing to do
676  if (m_data->time.size() == 1 or
677  m_data->n_records == 1 or
678  dt == 0.0) {
679  interp(t);
680  return;
681  }
682 
683  const double *data = &m_data->time[m_data->first];
684  size_t data_size = m_data->n_records;
685  auto type = m_data->interp_type;
686 
687  std::map<size_t, double> weights{};
688 
689  if (m_data->period > 0.0) {
690  double a = t;
691  double b = t + dt;
692  double t0 = m_data->period_start;
693  double P = m_data->period;
694 
695  // N_periods is the number of complete periods in the *middle* of the integration
696  // interval
697  //
698  // Note that the total number of complete periods is equal to (N_periods + 1) *if*
699  // delta == 0.0.
700  double N_periods = 0.0;
701  double delta = 0.0;
702  double gamma = 0.0;
703  {
704  double N = std::floor((a - t0) / P);
705  double M = std::floor((b - t0) / P);
706 
707  N_periods = M - (N + 1);
708  delta = (a - t0) - P * N;
709  gamma = (b - t0) - P * M;
710  }
711 
712  if (N_periods >= 0.0) {
713  assert(t0 + delta < t0 + P);
714  auto W1 = integration_weights(data, data_size, type, t0 + delta, t0 + P);
715 
716  std::map<size_t, double> W2{};
717  if (N_periods > 0) {
718  // note: we know that t0 < t0 + P because P > 0
719  W2 = integration_weights(data, data_size, type, t0, t0 + P);
720  } else {
721  W2 = {};
722  }
723 
724  std::map<size_t, double> W3{};
725  if (gamma > 0.0) {
726  W3 = integration_weights(data, data_size, type, t0, t0 + gamma);
727  } else {
728  W3 = {};
729  }
730 
731  // before the first complete period:
732  weights = W1;
733  // an integer number of complete periods:
734  for (const auto &w : W2) {
735  weights[w.first] += N_periods * w.second;
736  }
737  // after the last complete period:
738  for (const auto &w : W3) {
739  weights[w.first] += w.second;
740  }
741  } else {
742  assert(t0 + delta < t0 + gamma);
743  weights = integration_weights(data, data_size, type, t0 + delta, t0 + gamma);
744  }
745  } else {
746  assert(dt > 0.0);
747  weights = integration_weights(data, data_size, type, t, t + dt);
748  }
749 
750  array::AccessScope l{this};
751  double **a2 = array();
752  double ***a3 = array3();
753 
754  for (auto p = m_impl->grid->points(); p; p.next()) {
755  const int i = p.i(), j = p.j();
756 
757  a2[j][i] = 0.0;
758  for (const auto &weight : weights) {
759  size_t k = weight.first;
760  double w = weight.second;
761  a2[j][i] += w * a3[j][i][k];
762  }
763  a2[j][i] /= dt;
764  }
765 }
766 
767 /**
768  * \brief Compute weights for the piecewise-constant interpolation.
769  * This is used *both* for time-series and "snapshots".
770  *
771  * @param ts requested times, in seconds
772  *
773  */
774 void Forcing::init_interpolation(const std::vector<double> &ts) {
775 
776  assert(m_data->first >= 0);
777 
778  // Compute "periodized" times if necessary.
779  std::vector<double> times_requested(ts.size());
780  if (m_data->period > 0.0) {
781  double P = m_data->period;
782  double T0 = m_data->period_start;
783 
784  for (unsigned int k = 0; k < ts.size(); ++k) {
785  double t = ts[k] - T0;
786 
787  t -= std::floor(t / P) * P;
788 
789  times_requested[k] = T0 + t;
790  }
791  } else {
792  times_requested = ts;
793  }
794 
796  &m_data->time[m_data->first],
797  m_data->n_records,
798  times_requested.data(),
799  times_requested.size()));
800 }
801 
802 /**
803  * \brief Compute values of the time-series using precomputed indices
804  * (and piece-wise constant or piece-wise linear interpolation).
805  *
806  * @param i,j map-plane grid point
807  * @param result pointer to an allocated array of the size matching the one passed to
808  * init_interpolation()
809  *
810  */
811 void Forcing::interp(int i, int j, std::vector<double> &result) {
812  double ***a3 = array3();
813 
814  result.resize(m_data->interp->alpha().size());
815 
816  m_data->interp->interpolate(a3[j][i], result.data());
817 }
818 
819 } // end of namespace array
820 } // end of namespace pism
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
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
Definition: File.cc:313
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition: File.cc:454
High-level PISM I/O class.
Definition: File.hh:56
std::array< int, 4 > count
std::array< int, 4 > start
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
Definition: MaxTimestep.hh:31
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
units::System::Ptr unit_system() const
T * rawptr()
Definition: Wrapper.hh:39
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition: Array.hh:65
virtual void end_access() const
Checks if an Array is allocated and calls DAVecRestoreArray.
Definition: Array.cc:667
virtual void begin_access() const
Checks if an Array is allocated and calls DAVecGetArray.
Definition: Array.cc:646
const std::vector< double > & levels() const
Definition: Array.cc:140
const std::string & get_name() const
Get the name of an Array object.
Definition: Array.cc:383
petsc::Vec & vec() const
Definition: Array.cc:339
std::shared_ptr< const Grid > grid() const
Definition: Array.cc:132
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition: Array.cc:553
MaxTimestep max_timestep(double t) const
Given the time t determines the maximum possible time-step this Forcing allows.
Definition: Forcing.cc:599
void begin_access() const
Checks if an Array is allocated and calls DAVecGetArray.
Definition: Forcing.cc:213
void average(double t, double dt)
Definition: Forcing.cc:673
void discard(int N)
Discard the first N records, shifting the rest of them towards the "beginning".
Definition: Forcing.cc:564
void init_periodic_data(const File &file)
Definition: Forcing.cc:314
void interp(double t)
Definition: Forcing.cc:642
void set_record(int n)
Sets the record number n to the contents of the (internal) Vec v.
Definition: Forcing.cc:585
void init(const std::string &filename, bool periodic)
Definition: Forcing.cc:234
double *** array3()
Definition: Forcing.cc:209
void allocate(unsigned int buffer_size, InterpolationType interpolation_type)
Definition: Forcing.cc:178
void end_access() const
Checks if an Array is allocated and calls DAVecRestoreArray.
Definition: Forcing.cc:223
Forcing(std::shared_ptr< const Grid > grid, const File &file, const std::string &short_name, const std::string &standard_name, unsigned int max_buffer_size, bool periodic, InterpolationType interpolation_type=PIECEWISE_CONSTANT)
Definition: Forcing.cc:112
void init_interpolation(const std::vector< double > &ts)
Compute weights for the piecewise-constant interpolation. This is used both for time-series and "snap...
Definition: Forcing.cc:774
static std::shared_ptr< Forcing > Constant(std::shared_ptr< const Grid > grid, const std::string &short_name, double value)
Definition: Forcing.cc:147
unsigned int buffer_size()
Definition: Forcing.cc:205
virtual ~Forcing()
Definition: Forcing.cc:201
void update(double t, double dt)
Read some data to make sure that the interval (t, t + dt) is covered.
Definition: Forcing.cc:429
Contains parameters of an input file grid.
Definition: Grid.hh:65
double * get()
Definition: Vec.cc:54
Wrapper around VecGetArray and VecRestoreArray.
Definition: Vec.hh:44
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
static const double L
Definition: exactTestL.cc:40
#define n
Definition: exactTestM.c:37
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:165
double min(const array::Scalar &input)
Finds minimum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:193
static Vector3 column(const double A[3][3], size_t k)
Definition: Element.cc:51
@ PISM_GUESS
Definition: IO_Flags.hh:56
void regrid_spatial_variable(SpatialVariableMetadata &variable, const Grid &internal_grid, const LocalInterpCtx &lic, const File &file, double *output)
Regrid from a NetCDF file into a distributed array output.
Definition: io_helpers.cc:793
@ PISM_READONLY
open an existing file for reading only
Definition: IO_Flags.hh:72
void read_time_info(const Logger &log, std::shared_ptr< units::System > unit_system, const File &file, const std::string &time_name, const std::string &time_units, std::vector< double > &times, std::vector< double > &bounds)
Definition: io_helpers.cc:1092
std::string time_dimension(units::System::Ptr unit_system, const File &file, const std::string &variable_name)
Definition: io_helpers.cc:1343
static int weight(int M_ij, int M_n, double h_ij, double h_n)
@ T_AXIS
Definition: IO_Flags.hh:33
InterpolationType
@ PIECEWISE_CONSTANT
std::map< size_t, double > integration_weights(const double *x, size_t x_size, InterpolationType type, double a, double b)
static const double k
Definition: exactTestP.cc:42
void check_forcing_duration(const Time &time, double forcing_start, double forcing_end)
Definition: Time.cc:934
const int I[]
Definition: ssafd_code.cc:24
std::shared_ptr< const Grid > grid
The computational grid.
Definition: Array_impl.hh:77
InterpolationType interpolation_type
Definition: Array_impl.hh:105
bool report_range
If true, report range when regridding.
Definition: Array_impl.hh:62
unsigned int da_stencil_width
stencil width supported by the DA
Definition: Array_impl.hh:83
std::vector< SpatialVariableMetadata > metadata
Metadata (NetCDF variable attributes)
Definition: Array_impl.hh:74
double period_start
start of the period, in seconds
Definition: Forcing.cc:90
unsigned int n_records
number of records currently kept in memory
Definition: Forcing.cc:78
std::vector< double > time
all the times available in filename
Definition: Forcing.cc:53
std::string filename
name of the file to read (regrid) from
Definition: Forcing.cc:59
double period
forcing period, in seconds
Definition: Forcing.cc:87
InterpolationType interp_type
temporal interpolation type
Definition: Forcing.cc:81
double dt_min
minimum time step length in max_timestep(), in seconds
Definition: Forcing.cc:93
std::array< double, 2 > time_range
the range of times covered by data in filename
Definition: Forcing.cc:56
unsigned int buffer_size
maximum number of records stored in memory
Definition: Forcing.cc:71
std::shared_ptr< Interpolation > interp
temporal interpolation code
Definition: Forcing.cc:84
std::shared_ptr< petsc::DM > da
DM with dof equal to buffer_size.
Definition: Forcing.cc:62
petsc::Vec v
a 3D Vec used to store records
Definition: Forcing.cc:65
double *** array
pointer used to access records stored in memory
Definition: Forcing.cc:68