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