PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
SteadyState.cc
Go to the documentation of this file.
1 /* Copyright (C) 2019, 2020, 2021, 2023 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 
20 #include "pism/hydrology/SteadyState.hh"
21 
22 #include <gsl/gsl_interp.h> // gsl_interp_bsearch
23 
24 #include "pism/hydrology/EmptyingProblem.hh"
25 
26 #include "pism/util/Time.hh" // time().current()
27 #include "pism/util/Profiling.hh"
28 #include "pism/util/Context.hh"
29 #include "pism/util/MaxTimestep.hh"
30 
31 /* FIXMEs
32  *
33  * - At some later date I might want to support water input rates coming from the model.
34  * In that case I will have to accumulate input at every time step and then use the
35  * average (in time) when it's time to update the model.
36  *
37  * - IceModel::step() updates ice geometry before calling IceModel::hydrology_step(). This
38  * means that a hydrology model has to be able to provide its outputs before the first
39  * update() call. We save subglacial water flux to ensure that this is true for
40  * re-started runs, but in runs started using bootstrapping the first time step will see
41  * "bootstrapped" hydrology outputs (in this case: zero flux).
42  *
43  * - In this context (i.e. computing water flux using piecewise-in-time forcing data) it
44  * makes sense to update the flux at the beginning of an "update interval". In the case
45  * described above (water input coming from the model) we would want to update at the
46  * *end* of an interval.
47  *
48  */
49 
50 namespace pism {
51 namespace hydrology {
52 
54  m_log->message(2,
55  "* Initializing the \"steady state\" subglacial hydrology model ...\n");
56 }
57 
58 SteadyState::SteadyState(std::shared_ptr<const Grid> grid)
59  : NullTransport(grid) {
60 
61  m_time_name = m_config->get_string("time.dimension_name") + "_hydrology_steady";
62  m_t_last = time().current();
63  m_update_interval = m_config->get_number("hydrology.steady.flux_update_interval", "seconds");
64  m_t_eps = 1.0;
65  m_bootstrap = false;
66 
68 
69  if (m_config->get_flag("hydrology.add_water_input_to_till_storage")) {
71  "'steady' hydrology requires hydrology.add_water_input_to_till_storage == false");
72  }
73 
74  if (m_config->get_string("hydrology.surface_input.file").empty()) {
76  "'steady' hydrology requires hydrology.surface_input.file");
77  }
78 }
79 
80 void SteadyState::update_impl(double t, double dt, const Inputs& inputs) {
81  NullTransport::update_impl(t, dt, inputs);
82 
83  double t_next = m_t_last + max_timestep(m_t_last).value();
84 
85  if (t >= t_next or std::abs(t_next - t) < m_t_eps or
86  m_bootstrap) {
87 
88  m_log->message(3, " Updating the steady-state subglacial water flux...\n");
89 
90  profiling().begin("steady_emptying");
91 
92  m_emptying_problem->update(*inputs.geometry,
93  inputs.no_model_mask,
95 
96  profiling().end("steady_emptying");
98 
99  m_t_last = t;
100  m_bootstrap = false;
101  }
102 }
103 
104 std::map<std::string, Diagnostic::Ptr> SteadyState::diagnostics_impl() const {
105  auto hydro_diagnostics = NullTransport::diagnostics_impl();
106 
107  return combine(m_emptying_problem->diagnostics(), hydro_diagnostics);
108 }
109 
111 
112  // compute the maximum time step coming from the forcing (water input rate)
113  double dt_forcing = 0.0;
114  if (m_time.size() > 0) {
115 
116  // the right end point of the last time interval in the forcing file
117  double t_last = m_time_bounds.back();
118 
119  double t_next = 0.0;
120  if (t < m_time[0]) {
121  // Allow stepping until the left end point of the first interval.
122  t_next = m_time[0];
123  } else if (t >= t_last) {
124  // Went past the right end point of the last forcing intervals: no time step
125  // restriction from forcing.
126  t_next = t + m_update_interval;
127  } else {
128  // find the index k such that m_time[k] <= t < m_time[k + 1]
129  size_t k = gsl_interp_bsearch(m_time.data(), t, 0, m_time.size());
130 
131  assert(m_time[k] <= t);
132  assert(k + 1 == m_time.size() or t < m_time[k + 1]);
133 
134  t_next = m_time_bounds[2 * k + 1];
135 
136  if (std::abs(t_next - t) < m_t_eps) {
137  // reached t_next; use the right end point of the next interval
138  if (k + 1 < m_time.size()) {
139  t_next = m_time_bounds[2 * (k + 1) + 1];
140  } else {
141  // No time step restriction from forcing. We set dt_forcing to m_update_interval
142  // because dt_interval below will not exceed this, effectively selecting
143  // dt_interval.
144  t_next = t + m_update_interval;
145  }
146  }
147  }
148 
149  dt_forcing = t_next - t;
150 
151  assert(dt_forcing > 0.0);
152  } else {
153  dt_forcing = m_update_interval;
154  }
155 
156  // compute the maximum time step using the update interval
157  double dt_interval = 0.0;
158  {
159  if (t < m_t_last) {
161  "time %f is less than the previous time %f",
162  t, m_t_last);
163  }
164 
165  // Find the smallest time of the form m_t_last + k * m_update_interval that is greater
166  // than t
167  double k = ceil((t - m_t_last) / m_update_interval);
168 
169  double
170  t_next = m_t_last + k * m_update_interval;
171 
172  dt_interval = t_next - t;
173 
174  if (dt_interval < m_t_eps) {
175  dt_interval = m_update_interval;
176  }
177  }
178 
179  double dt = std::min(dt_forcing, dt_interval);
180 
181  auto dt_null = NullTransport::max_timestep_impl(t);
182  if (dt_null.finite()) {
183  dt = std::min(dt, dt_null.value());
184  }
185 
186  return MaxTimestep(dt, "hydrology 'steady'");
187 }
188 
189 void SteadyState::define_model_state_impl(const File& output) const {
191 
192  if (not output.find_variable(m_time_name)) {
194 
195  output.write_attribute(m_time_name, "long_name",
196  "time of the last update of the steady state subglacial water flux");
197  output.write_attribute(m_time_name, "calendar", time().calendar());
198  output.write_attribute(m_time_name, "units", time().units_string());
199  }
200 
201  m_Q.define(output, io::PISM_DOUBLE);
202 }
203 
204 void SteadyState::write_model_state_impl(const File& output) const {
206 
207  output.write_variable(m_time_name, {0}, {1}, &m_t_last);
208  m_Q.write(output);
209 }
210 
211 void SteadyState::restart_impl(const File &input_file, int record) {
212  NullTransport::restart_impl(input_file, record);
213 
214  init_time(m_config->get_string("hydrology.surface_input.file"));
215 
216  // Read m_t_last
217  {
218  if (input_file.find_variable(m_time_name)) {
219  input_file.read_variable(m_time_name, {0}, {1}, &m_t_last);
220  } else {
221  m_t_last = time().current();
222  }
223  }
224 
225  m_Q.read(input_file, record);
226 
227  regrid("hydrology 'steady'", m_Q, REGRID_WITHOUT_REGRID_VARS);
228 }
229 
230 void SteadyState::bootstrap_impl(const File &input_file,
231  const array::Scalar &ice_thickness) {
232  NullTransport::bootstrap_impl(input_file, ice_thickness);
233 
234  init_time(m_config->get_string("hydrology.surface_input.file"));
235 
236  // Read m_t_last
237  {
238  if (input_file.find_variable(m_time_name)) {
239  input_file.read_variable(m_time_name, {0}, {1}, &m_t_last);
240  } else {
241  m_t_last = time().current();
242  }
243  }
244 
245  // Read water flux
246  if (input_file.find_variable(m_Q.metadata().get_name())) {
247  // Regrid from the input file.
248  m_Q.regrid(input_file, io::Default::Nil());
249 
250  // Allow regridding from a different file.
251  regrid("hydrology 'steady'", m_Q, REGRID_WITHOUT_REGRID_VARS);
252  } else {
253  // No water flux in the input file; try regridding from a different file.
254  auto n = m_Q.state_counter();
255 
256  regrid("hydrology 'steady'", m_Q, REGRID_WITHOUT_REGRID_VARS);
257 
258  if (n == m_Q.state_counter()) {
259  // Regridding did not fill m_Q: we need to bootstrap during the first update_impl()
260  // call.
261  m_bootstrap = true;
262  }
263  }
264 }
265 
267  const array::Scalar &W,
268  const array::Scalar &P) {
269  NullTransport::init_impl(W_till, W, P);
270 
271  m_Q.set(0.0);
272 
273  m_bootstrap = true;
274 }
275 
276 
277 /*!
278  * Read time bounds corresponding to the water input rate in the forcing file.
279  *
280  * These times are used to compute the maximum time step the model can take while still
281  * capturing temporal variability of the forcing.
282  */
283 void SteadyState::init_time(const std::string &input_file) {
284 
285  std::string variable_name = "water_input_rate";
286 
287  File file(m_grid->com, input_file, io::PISM_GUESS, io::PISM_READONLY);
288 
289  auto time_name = io::time_dimension(m_grid->ctx()->unit_system(),
290  file, variable_name);
291 
292  if (time_name.empty()) {
293  // Water input rate is time-independent. m_time and m_time_bounds are left empty.
294  return;
295  }
296 
297  std::string bounds_name = file.read_text_attribute(time_name, "bounds");
298 
299  if (bounds_name.empty()) {
300  // no time bounds attribute
302  "Variable '%s' does not have the time_bounds attribute.\n"
303  "Cannot use time-dependent water input rate without time bounds.",
304  time_name.c_str());
305  }
306 
307  // read time bounds data from a file
308  VariableMetadata tb(bounds_name, m_grid->ctx()->unit_system());
309  tb["units"] = time().units_string();
310 
312 
313  // time bounds data overrides the time variable: we make t[j] be the
314  // left end-point of the j-th interval
315  m_time.resize(m_time_bounds.size() / 2);
316  for (unsigned int k = 0; k < m_time.size(); ++k) {
317  m_time[k] = m_time_bounds[2*k];
318  }
319 
320  if (not is_increasing(m_time)) {
322  "time bounds in %s are invalid", input_file.c_str());
323  }
324 }
325 
326 } // end of namespace hydrology
327 } // end of namespace pism
const Time & time() const
Definition: Component.cc:109
std::shared_ptr< const Grid > grid() const
Definition: Component.cc:105
const Config::ConstPtr m_config
configuration database used by this component
Definition: Component.hh:158
const Logger::ConstPtr m_log
logger (for easy access)
Definition: Component.hh:162
@ REGRID_WITHOUT_REGRID_VARS
Definition: Component.hh:151
const std::shared_ptr< const Grid > m_grid
grid used by this component
Definition: Component.hh:156
void regrid(const std::string &module_name, array::Array &variable, RegriddingFlag flag=NO_REGRID_WITHOUT_REGRID_VARS)
Definition: Component.cc:159
const Profiling & profiling() const
Definition: Component.cc:113
MaxTimestep max_timestep(double t) const
Reports the maximum time-step the model can take at time t.
Definition: Component.cc:183
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:747
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
void write_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *op) const
Definition: File.cc:760
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
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
double value() const
Get the value of the maximum time step.
Definition: MaxTimestep.cc:55
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
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
std::string units_string() const
Internal time units as a string.
Definition: Time.cc:477
double current() const
Current time, in seconds.
Definition: Time.cc:465
std::string get_name() const
void copy_from(const Array2D< T > &source)
Definition: Array2D.hh:73
void read(const std::string &filename, unsigned int time)
Definition: Array.cc:809
void define(const File &file, io::Type default_type) const
Define variables corresponding to an Array in a file opened using file.
Definition: Array.cc:540
void write(const std::string &filename) const
Definition: Array.cc:800
int state_counter() const
Get the object state counter.
Definition: Array.cc:128
void set(double c)
Result: v[j] <- c for all j.
Definition: Array.cc:707
void regrid(const std::string &filename, io::Default default_value)
Definition: Array.cc:814
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
Definition: Array.cc:553
array::Scalar m_surface_input_rate
Definition: Hydrology.hh:179
virtual void define_model_state_impl(const File &output) const
The default (empty implementation).
Definition: Hydrology.cc:467
virtual std::map< std::string, Diagnostic::Ptr > diagnostics_impl() const
Definition: Hydrology.cc:443
virtual void write_model_state_impl(const File &output) const
The default (empty implementation).
Definition: Hydrology.cc:471
const Geometry * geometry
Definition: Hydrology.hh:37
const array::Scalar1 * no_model_mask
Definition: Hydrology.hh:35
virtual void restart_impl(const File &input_file, int record)
virtual void init_impl(const array::Scalar &W_till, const array::Scalar &W, const array::Scalar &P)
virtual void bootstrap_impl(const File &input_file, const array::Scalar &ice_thickness)
virtual void update_impl(double t, double dt, const Inputs &inputs)
Solves an implicit step of a highly-simplified ODE.
virtual MaxTimestep max_timestep_impl(double t) const
double m_update_interval
Update interval in seconds.
Definition: SteadyState.hh:63
void init_time(const std::string &input_file)
Definition: SteadyState.cc:283
void write_model_state_impl(const File &output) const
The default (empty implementation).
Definition: SteadyState.cc:204
double m_t_last
time of the last water flux update
Definition: SteadyState.hh:61
void update_impl(double t, double dt, const Inputs &inputs)
Solves an implicit step of a highly-simplified ODE.
Definition: SteadyState.cc:80
std::vector< double > m_time_bounds
Time bounds corresponding to records in the input file.
Definition: SteadyState.hh:72
MaxTimestep max_timestep_impl(double t) const
Definition: SteadyState.cc:110
std::string m_time_name
Name of the variable used to store the last update time.
Definition: SteadyState.hh:67
void restart_impl(const File &input_file, int record)
Definition: SteadyState.cc:211
bool m_bootstrap
Set to true in bootstrap_impl() if update_impl() has to bootstrap m_Q.
Definition: SteadyState.hh:75
std::map< std::string, Diagnostic::Ptr > diagnostics_impl() const
Definition: SteadyState.cc:104
std::shared_ptr< EmptyingProblem > m_emptying_problem
Definition: SteadyState.hh:58
void define_model_state_impl(const File &output) const
The default (empty implementation).
Definition: SteadyState.cc:189
std::vector< double > m_time
Times corresponding to records in the input file.
Definition: SteadyState.hh:70
void initialization_message() const
Definition: SteadyState.cc:53
SteadyState(std::shared_ptr< const Grid > g)
Definition: SteadyState.cc:58
void bootstrap_impl(const File &input_file, const array::Scalar &ice_thickness)
Definition: SteadyState.cc:230
void init_impl(const array::Scalar &W_till, const array::Scalar &W, const array::Scalar &P)
Definition: SteadyState.cc:266
double m_t_eps
Temporal resolution to use when checking whether it's time to update.
Definition: SteadyState.hh:65
static Default Nil()
Definition: IO_Flags.hh:97
#define PISM_ERROR_LOCATION
#define n
Definition: exactTestM.c:37
double min(const array::Scalar &input)
Finds minimum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:193
@ PISM_GUESS
Definition: IO_Flags.hh:56
@ PISM_READONLY
open an existing file for reading only
Definition: IO_Flags.hh:72
@ PISM_DOUBLE
Definition: IO_Flags.hh:52
void read_time_bounds(const File &file, const VariableMetadata &metadata, const Logger &log, std::vector< double > &data)
Definition: io_helpers.cc:974
std::string time_dimension(units::System::Ptr unit_system, const File &file, const std::string &variable_name)
Definition: io_helpers.cc:1343
bool is_increasing(const std::vector< double > &a)
Checks if a vector of doubles is strictly increasing.
static const double k
Definition: exactTestP.cc:42
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
Definition: Time.cc:146
T combine(const T &a, const T &b)