PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
OptTillphiYieldStress.cc
Go to the documentation of this file.
1// Copyright (C) 2004--2023, 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#include "pism/basalstrength/OptTillphiYieldStress.hh"
20
21#include "pism/basalstrength/MohrCoulombYieldStress.hh"
22#include "pism/geometry/Geometry.hh"
23#include "pism/util/Grid.hh"
24#include "pism/util/array/CellType.hh"
25#include "pism/util/MaxTimestep.hh"
26#include "pism/util/Time.hh"
27#include "pism/util/error_handling.hh"
28#include "pism/util/io/File.hh"
29#include "pism/util/pism_utilities.hh"
30#include "pism/util/Logger.hh"
31#include "pism/util/io/IO_Flags.hh"
32#include "pism/util/io/io_helpers.hh"
33
34namespace pism {
35
36/*! Optimization of till friction angle for given target surface elevation, analogous to
37 Pollard et al. (2012), TC 6(5), "A simple inverse method for the distribution of basal
38 sliding coefficients under ice sheets, applied to Antarctica"
39*/
40OptTillphiYieldStress::OptTillphiYieldStress(std::shared_ptr<const Grid> grid)
42 m_mask(m_grid, "diff_mask"),
43 m_usurf_difference(m_grid, "usurf_difference"),
44 m_usurf_target(m_grid, "usurf"),
45 m_time_name(m_config->get_string("time.dimension_name") + "_tillphi_opt")
46{
47
48 // In this model tillphi is NOT time-independent.
50
51 m_name = "Iterative optimization of the till friction angle for the Mohr-Coulomb yield stress model";
52
54 .long_name("target surface elevation for tillphi optimization")
55 .units("m")
56 .set_time_dependent(false);
57
59 .long_name("difference between modeled and target surface elevations")
60 .units("m");
61
63
65 .long_name(
66 "one if the till friction angle was updated by the last iteration, zero otherwise ");
67
68 m_mask.metadata()["flag_values"] = {0.0, 1.0};
69 m_mask.metadata()["flag_meanings"] = "no_update updated_during_last_iteration";
70
71 {
72 // convergence threshold
73 m_dhdt_min = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.dhdt_min", "m / s");
74
75 // scale used to compute tillphi adjustment using the surface elevation mismatch:
76 m_dphi_scale = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.dphi_scale", "degree / m");
77 // upper and lower bounds of the tillphi adjustment during an iteration:
78 m_dphi_max = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.dphi_max");
80 // lower bound of tillphi:
81 m_phi0_min = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.phi0_min");
82 m_phi0_max = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.phi0_max");
83 m_topg_min = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.topg_min");
84 m_topg_max = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.topg_max");
85 // upper bound of tillphi:
86 m_phi_max = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.phi_max");
87
88 if (m_phi0_min >= m_phi_max) {
90 "basal_yield_stress.mohr_coulomb.tillphi_opt: phi0_min >= phi_max");
91 }
92
93 if (m_topg_min >= m_topg_max) {
95 "basal_yield_stress.mohr_coulomb.tillphi_opt: topg_min >= topg_max");
96 }
97 }
98
99 {
100 m_t_last = time().current();
101 m_update_interval = m_config->get_number("basal_yield_stress.mohr_coulomb.tillphi_opt.dt", "seconds");
102 m_t_eps = m_config->get_number("time_stepping.resolution", "seconds");
103 }
104
105 m_log->message(2,
106 " Using iterative optimization of the till friction angle.\n"
107 " Lower bound phi0 of the till friction angle is a piecewise-linear function of bed elevation (b):\n"
108 " / %5.2f for b < %.f\n"
109 " phi0 = | %5.2f + (b - (%.f)) * (%.2f / %.f) for %.f < b < %.f\n"
110 " \\ %5.2f for %.f < b\n",
114}
115
116/*!
117 * Initialize the last time tillphi was updated.
118 */
120
121 if (input_file.variable_exists(m_time_name)) {
122 auto t_length = input_file.nrecords(m_time_name, "", m_sys);
123 auto t_last = t_length > 0 ? t_length - 1 : 0;
124
125 auto t = io::read_timeseries_variable(input_file, m_time_name, time().units(), m_sys, t_last, 1);
126 } else {
127 m_t_last = time().current();
128 }
129}
130
131/*!
132 * Initialize the target ice surface elevation.
133 */
135 auto filename = m_config->get_string("basal_yield_stress.mohr_coulomb.tillphi_opt.file");
136
137 if (not filename.empty()) {
139 } else {
140 m_log->message(2, "* No file set to read target surface elevation from... using '%s'\n",
141 input_file.name().c_str());
142
144 }
145
146 m_usurf_target.metadata().set_name("usurf_target");
147}
148
149void OptTillphiYieldStress::restart_impl(const File &input_file, int record) {
150
151 MohrCoulombYieldStress::restart_impl(input_file, record);
152
153 init_t_last(input_file);
154
155 init_usurf_target(input_file);
156}
157
158//! Initialize the pseudo-plastic till mechanical model.
159//! Target surface elevation and initial iteration time are set.
161 const YieldStressInputs &inputs) {
162
163 MohrCoulombYieldStress::bootstrap_impl(input_file, inputs);
164
165 init_t_last(input_file);
166
167 init_usurf_target(input_file);
168}
169
171 (void) inputs;
173 "not implemented: till friction angle optimization "
174 "cannot be initialized without an input file");
175}
176
178 MaxTimestep dt_max;
179 {
180 if (t < m_t_last) {
182 "time %f is less than the previous time %f",
183 t, m_t_last);
184 }
185
186 // Find the smallest time of the form m_t_last + k * m_update_interval that is greater
187 // than t
188 double k = ceil((t - m_t_last) / m_update_interval);
189
190 double
191 t_next = m_t_last + k * m_update_interval,
192 dt = t_next - t;
193
194 if (dt < m_t_eps) {
196 }
197
198 dt_max = MaxTimestep(dt, "tillphi_opt");
199 }
200
201 auto dt_mohr_coulomb = MohrCoulombYieldStress::max_timestep_impl(t);
202
203 return std::min(dt_max, dt_mohr_coulomb);
204}
205
207 double t, double dt) {
208
209 double
210 t_next = m_t_last + m_update_interval,
211 t_final = t + dt;
212
213 if (t_final < m_t_last) {
214 throw RuntimeError(PISM_ERROR_LOCATION, "cannot go back in time");
215 }
216
217 if (std::abs(t_next - t_final) < m_t_eps) { // reached the next update time
219 inputs.geometry->bed_elevation,
220 inputs.geometry->cell_type);
221 m_t_last = t_final;
222 }
223
225}
226
227//! Perform an iteration to adjust the till friction angle according to the difference
228//! between target and modeled surface elevations.
229void OptTillphiYieldStress::update_tillphi(const array::Scalar &ice_surface_elevation,
230 const array::Scalar &bed_topography,
231 const array::CellType &cell_type) {
232
233 m_log->message(2, "* Updating till friction angle...\n");
234
236 { &m_till_phi, &m_usurf_target, &m_usurf_difference, &m_mask, &ice_surface_elevation, &bed_topography, &cell_type };
237
238 m_mask.set(0.0);
239
240 double slope = (m_phi0_max - m_phi0_min) / (m_topg_max - m_topg_min);
241
242 for (auto p : m_grid->points()) {
243 const int i = p.i(), j = p.j();
244
245 // Compute the lower bound of the till friction angle (default value corresponds
246 // to "bed_topography(i, j) > topg_max"):
247 double phi0 = m_phi0_max;
248 if (bed_topography(i, j) <= m_topg_min) {
249 phi0 = m_phi0_min;
250 } else if (bed_topography(i, j) <= m_topg_max) {
251 phi0 = m_phi0_min + (bed_topography(i, j) - m_topg_min) * slope;
252 }
253
254 if (cell_type.grounded_ice(i, j)) {
255 double dh_previous = m_usurf_difference(i, j);
256 m_usurf_difference(i, j) = m_usurf_target(i, j) - ice_surface_elevation(i, j);
257 double dh_change = std::abs(m_usurf_difference(i, j) - dh_previous);
258
259 if (dh_change / m_update_interval > m_dhdt_min) {
260 // Update tillphi if the rate of change of the surface elevation mismatch since
261 // the last iteration is greater than the convergence threshold m_dhdt_min.
262
263 // Mark this location as "updated by the last iteration":
264 m_mask(i, j) = 1.0;
265
266 // Compute (and clip) the tillphi adjustment
267 double dphi = m_usurf_difference(i, j) * m_dphi_scale;
268 dphi = pism::clip(dphi, m_dphi_min, m_dphi_max);
269
270 // Update (and clip) the till friction angle:
271 m_till_phi(i, j) += dphi;
272 m_till_phi(i, j) = pism::clip(m_till_phi(i, j), phi0, m_phi_max);
273 }
274 } else if (cell_type.ocean(i, j)) {
275 // Floating and ice free ocean: use the bed-elevation-dependent lower bound of
276 // tillphi:
277 m_till_phi(i, j) = phi0;
278 }
279 } // end of the loop over grid points
280}
281
282std::set<VariableMetadata> OptTillphiYieldStress::state_impl() const {
283 auto result = MohrCoulombYieldStress::state();
284
286 T.long_name("time of the last update of the till friction angle")
287 .units(time().units())
288 .set_time_dependent(true);
289 T["calendar"] = time().calendar();
290
291 result.insert(T);
292
293 return result;
294}
295
298
299 output.write_timeseries(m_time_name, { 0 }, { 1 }, { m_t_last });
300}
301
310
311} // 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 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
std::set< VariableMetadata > state() const
Definition Component.cc:124
std::shared_ptr< const Logger > m_log
logger (for easy access)
Definition Component.hh:164
static Ptr wrap(const T &input)
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 name() const
Definition File.cc:274
High-level PISM I/O class.
Definition File.hh:57
array::Scalar2 ice_surface_elevation
Definition Geometry.hh:57
array::CellType2 cell_type
Definition Geometry.hh:55
array::Scalar2 bed_elevation
Definition Geometry.hh:47
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
DiagnosticList spatial_diagnostics_impl() const
void restart_impl(const File &input_file, int record)
void update_impl(const YieldStressInputs &inputs, double t, double dt)
MaxTimestep max_timestep_impl(double t) const
void bootstrap_impl(const File &input_file, const YieldStressInputs &inputs)
Initialize the pseudo-plastic till mechanical model.
void write_state_impl(const OutputFile &output) const
The default (empty implementation).
PISM's default basal yield stress model which applies the Mohr-Coulomb model of deformable,...
MaxTimestep max_timestep_impl(double t) const
void bootstrap_impl(const File &input_file, const YieldStressInputs &inputs)
void init_t_last(const File &input_file)
void write_state_impl(const OutputFile &output) const
The default (empty implementation).
void init_impl(const YieldStressInputs &inputs)
std::set< VariableMetadata > state_impl() const
double m_update_interval
Update interval in seconds.
DiagnosticList spatial_diagnostics_impl() const
double m_t_last
time of the last till friction angle update
void update_tillphi(const array::Scalar &ice_surface_elevation, const array::Scalar &bed_topography, const array::CellType &mask)
OptTillphiYieldStress(std::shared_ptr< const Grid > g)
void restart_impl(const File &input_file, int record)
std::string m_time_name
Name of the variable used to store the last update time.
double m_t_eps
Temporal resolution to use when checking whether it's time to update.
void update_impl(const YieldStressInputs &inputs, double t, double dt)
void init_usurf_target(const File &input_file)
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
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)
VariableMetadata & set_name(const std::string &name)
VariableMetadata & set_time_dependent(bool flag)
const Geometry * geometry
std::string m_name
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:66
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
bool grounded_ice(int i, int j) const
Definition CellType.hh:46
bool ocean(int i, int j) const
Definition CellType.hh:34
"Cell type" mask. Adds convenience methods to array::Scalar.
Definition CellType.hh:30
static Default Nil()
Definition IO_Flags.hh:94
#define PISM_ERROR_LOCATION
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)
T clip(T x, T a, T b)
std::map< std::string, Diagnostic::Ptr > DiagnosticList
static const double k
Definition exactTestP.cc:42
T combine(const T &a, const T &b)