PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
FlowLaw.cc
Go to the documentation of this file.
1// Copyright (C) 2004-2018, 2020, 2021, 2022, 2023, 2025, 2026 Jed Brown, Ed Bueler, and 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 "pism/rheology/FlowLaw.hh"
20
21#include <petsc.h>
22#include <limits>
23
24#include "pism/util/EnthalpyConverter.hh"
25#include "pism/util/array/Scalar.hh"
26#include "pism/util/array/Array3D.hh"
27
28#include "pism/util/Config.hh"
29#include "pism/util/Grid.hh"
30
31#include "pism/util/error_handling.hh"
32
33namespace pism {
34namespace rheology {
35
36FlowLaw::FlowLaw(double exponent, const Config &config,
37 std::shared_ptr<EnthalpyConverter> ec)
38 : m_EC(ec) {
39
40 if (not m_EC) {
41 throw RuntimeError(PISM_ERROR_LOCATION, "EC is NULL in FlowLaw::FlowLaw()");
42 }
43
44 auto rho = config.get_number("constants.ice.density");
45 auto standard_gravity = config.get_number("constants.standard_gravity");
46
47 m_ideal_gas_constant = config.get_number("constants.ideal_gas_constant");
48
49 m_rho_g = rho * standard_gravity;
50 m_beta_CC_grad = config.get_number("constants.ice.beta_Clausius_Clapeyron") * m_rho_g;
51 m_n = exponent;
52 m_viscosity_power = (1.0 - m_n) / (2.0 * m_n);
53 m_hardness_power = -1.0 / m_n;
54
55 m_A_cold = config.get_number("flow_law.Paterson_Budd.A_cold");
56 m_A_warm = config.get_number("flow_law.Paterson_Budd.A_warm");
57 m_Q_cold = config.get_number("flow_law.Paterson_Budd.Q_cold");
58 m_Q_warm = config.get_number("flow_law.Paterson_Budd.Q_warm");
59 m_crit_temp = config.get_number("flow_law.Paterson_Budd.T_critical");
60
61 double
62 schoofLen = config.get_number("flow_law.Schoof_regularizing_length", "m"),
63 schoofVel = config.get_number("flow_law.Schoof_regularizing_velocity", "m second-1");
64
65 m_schoofReg = PetscSqr(schoofVel / schoofLen);
66}
67
68std::string FlowLaw::name() const {
69 return m_name;
70}
71
72std::shared_ptr<EnthalpyConverter> FlowLaw::EC() const {
73 return m_EC;
74}
75
76double FlowLaw::exponent() const {
77 return m_n;
78}
79
80//! Return the softness parameter A(T) for a given temperature T.
81/*! This is not a natural part of all FlowLaw instances. */
82double FlowLaw::softness_paterson_budd(double T_pa) const {
83 const double A = T_pa < m_crit_temp ? m_A_cold : m_A_warm;
84 const double Q = T_pa < m_crit_temp ? m_Q_cold : m_Q_warm;
85
86 return A * exp(-Q / (m_ideal_gas_constant * T_pa));
87}
88
89//! The flow law itself.
90double FlowLaw::flow(double stress, double enthalpy,
91 double pressure, double grain_size) const {
92 return this->flow_impl(stress, enthalpy, pressure, grain_size);
93}
94
95double FlowLaw::flow_impl(double stress, double enthalpy,
96 double pressure, double /* gs */) const {
97 return softness(enthalpy, pressure) * pow(stress, m_n-1);
98}
99
100void FlowLaw::flow_n(const double *stress, const double *enthalpy,
101 const double *pressure, const double *grainsize,
102 unsigned int n, double *result) const {
103 this->flow_n_impl(stress, enthalpy, pressure, grainsize, n, result);
104}
105
106void FlowLaw::flow_n_impl(const double *stress, const double *enthalpy,
107 const double *pressure, const double *grainsize,
108 unsigned int n, double *result) const {
109 for (unsigned int k = 0; k < n; ++k) {
110 result[k] = this->flow(stress[k], enthalpy[k], pressure[k], grainsize[k]);
111 }
112}
113
114
115double FlowLaw::softness(double E, double p) const {
116 return this->softness_impl(E, p);
117}
118
119double FlowLaw::hardness(double E, double p) const {
120 return this->hardness_impl(E, p);
121}
122
123void FlowLaw::hardness_n(const double *enthalpy, const double *pressure,
124 unsigned int n, double *result) const {
125 this->hardness_n_impl(enthalpy, pressure, n, result);
126}
127
128void FlowLaw::hardness_n_impl(const double *enthalpy, const double *pressure,
129 unsigned int n, double *result) const {
130 for (unsigned int k = 0; k < n; ++k) {
131 result[k] = this->hardness(enthalpy[k], pressure[k]);
132 }
133}
134
135double FlowLaw::hardness_impl(double E, double p) const {
136 double A = softness(E, p);
137#if (Pism_DEBUG == 1)
138 if (A == 0.0) {
139 return std::numeric_limits<double>::max();
140 }
141#endif
142 return pow(A, m_hardness_power);
143}
144
145//! \brief Computes the regularized effective viscosity and its derivative with respect to the
146//! second invariant \f$ \gamma \f$.
147/*!
148 *
149 * @f{align*}{
150 * \nu &= \frac{1}{2} B \left( \epsilon + \gamma \right)^{(1-n)/(2n)},\\
151 * \diff{\nu}{\gamma} &= \frac{1}{2} B \cdot \frac{1-n}{2n} \cdot \left(\epsilon + \gamma \right)^{(1-n)/(2n) - 1}, \\
152 * &= \frac{1-n}{2n} \cdot \frac{1}{2} B \left( \epsilon + \gamma \right)^{(1-n)/(2n)} \cdot \frac{1}{\epsilon + \gamma}, \\
153 * &= \frac{1-n}{2n} \cdot \frac{\nu}{\epsilon + \gamma}.
154 * @f}
155 * Here @f$ \gamma @f$ is the second invariant
156 * @f{align*}{
157 * \gamma &= \frac{1}{2} D_{ij} D_{ij}\\
158 * &= \frac{1}{2}\, ((u_x)^2 + (v_y)^2 + (u_x + v_y)^2 + \frac{1}{2}\, (u_y + v_x)^2) \\
159 * @f}
160 * and
161 * @f[ D_{ij}(\mathbf{u}) = \frac{1}{2}\left(\diff{u_{i}}{x_{j}} + \diff{u_{j}}{x_{i}}\right). @f]
162 *
163 * Either one of \c nu and \c dnu can be NULL if the corresponding output is not needed.
164 *
165 * \param[in] B ice hardness
166 * \param[in] gamma the second invariant
167 * \param[out] nu effective viscosity
168 * \param[out] dnu derivative of \f$ \nu \f$ with respect to \f$ \gamma \f$
169 */
170void FlowLaw::effective_viscosity(double hardness, double gamma,
171 double *nu, double *dnu) const {
172 effective_viscosity(hardness, gamma, m_schoofReg, nu, dnu);
173}
174
175void FlowLaw::effective_viscosity(double hardness, double gamma, double eps,
176 double *nu, double *dnu) const {
177 const double
178 my_nu = 0.5 * hardness * pow(eps + gamma, m_viscosity_power);
179
180 if (PetscLikely(nu != NULL)) {
181 *nu = my_nu;
182 }
183
184 if (PetscLikely(dnu != NULL)) {
185 *dnu = m_viscosity_power * my_nu / (eps + gamma);
186 }
187}
188
190 const array::Scalar &thickness,
191 const array::Array3D &enthalpy,
192 array::Scalar &result) {
193
194 const Grid &grid = *thickness.grid();
195
196 array::AccessScope list{&thickness, &result, &enthalpy};
197
198 ParallelSection loop(grid.com);
199 try {
200 for (auto p : grid.points()) {
201 const int i = p.i(), j = p.j();
202
203 // Evaluate column integrals in flow law at every quadrature point's column
204 double H = thickness(i,j);
205 const double *enthColumn = enthalpy.get_column(i, j);
206 result(i,j) = averaged_hardness(ice, H, grid.kBelowHeight(H),
207 grid.z().data(), enthColumn);
208 }
209 } catch (...) {
210 loop.failed();
211 }
212 loop.check();
213
214 result.update_ghosts();
215}
216
217//! Computes vertical average of `B(E, p)` ice hardness, namely @f$\bar B(E, p)@f$.
218/*!
219 * See comment for hardness(). Note `E[0], ..., E[kbelowH]` must be valid.
220 */
221double averaged_hardness(const FlowLaw &ice,
222 double thickness,
223 unsigned int kbelowH,
224 const double *zlevels,
225 const double *enthalpy) {
226 double B = 0;
227
228 const auto &EC = *ice.EC();
229
230 // Use trapezoidal rule to integrate from 0 to zlevels[kbelowH]:
231 if (kbelowH > 0) {
232 double
233 p0 = EC.pressure(thickness),
234 E0 = enthalpy[0],
235 h0 = ice.hardness(E0, p0); // ice hardness at the left endpoint
236
237 for (unsigned int i = 1; i <= kbelowH; ++i) { // note the "1" and the "<="
238 const double
239 p1 = EC.pressure(thickness - zlevels[i]), // pressure at the right endpoint
240 E1 = enthalpy[i], // enthalpy at the right endpoint
241 h1 = ice.hardness(E1, p1); // ice hardness at the right endpoint
242
243 // The trapezoid rule sans the "1/2":
244 B += (zlevels[i] - zlevels[i-1]) * (h0 + h1);
245
246 h0 = h1;
247 }
248 }
249
250 // Add the "1/2":
251 B *= 0.5;
252
253 // use the "rectangle method" to integrate from
254 // zlevels[kbelowH] to thickness:
255 double
256 depth = thickness - zlevels[kbelowH],
257 p = EC.pressure(depth);
258
259 B += depth * ice.hardness(enthalpy[kbelowH], p);
260
261 // Now B is an integral of ice hardness; next, compute the average:
262 if (thickness > 0) {
263 B = B / thickness;
264 } else {
265 B = 0;
266 }
267
268 return B;
269}
270
271bool FlowLawUsesGrainSize(const FlowLaw &flow_law) {
272 static const double gs[] = {1e-4, 1e-3, 1e-2, 1}, s=1e4, E=400000, p=1e6;
273 double ref = flow_law.flow(s, E, p, gs[0]);
274 for (int i=1; i<4; i++) {
275 if (flow_law.flow(s, E, p, gs[i]) != ref) {
276 return true;
277 }
278 }
279 return false;
280}
281
282} // end of namespace rheology
283} // end of namespace pism
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
Definition Config.cc:188
A class for storing and accessing PISM configuration flags and parameters.
Definition Config.hh:56
Describes the PISM grid and the distribution of data across processors.
Definition Grid.hh:285
void failed()
Indicates a failure of a parallel section.
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:66
double * get_column(int i, int j)
Definition Array3D.cc:127
A virtual class collecting methods common to ice and bedrock 3D fields.
Definition Array3D.hh:33
std::shared_ptr< const Grid > grid() const
Definition Array.cc:134
void update_ghosts()
Updates ghost points.
Definition Array.cc:645
double m_crit_temp
critical temperature (cold – warm transition)
Definition FlowLaw.hh:145
double softness_paterson_budd(double T_pa) const
Return the softness parameter A(T) for a given temperature T.
Definition FlowLaw.cc:82
double m_schoofReg
regularization parameter for
Definition FlowLaw.hh:129
virtual double hardness_impl(double E, double p) const
Definition FlowLaw.cc:135
double m_A_warm
Paterson-Budd softness, warm case.
Definition FlowLaw.hh:139
double m_A_cold
Paterson-Budd softness, cold case.
Definition FlowLaw.hh:137
double m_Q_cold
Activation energy, cold case.
Definition FlowLaw.hh:141
double m_viscosity_power
; used to compute viscosity
Definition FlowLaw.hh:132
FlowLaw(double exponent, const Config &config, std::shared_ptr< EnthalpyConverter > EC)
Definition FlowLaw.cc:36
void flow_n(const double *stress, const double *E, const double *pressure, const double *grainsize, unsigned int n, double *result) const
Definition FlowLaw.cc:100
std::shared_ptr< EnthalpyConverter > m_EC
Definition FlowLaw.hh:124
virtual void hardness_n_impl(const double *enthalpy, const double *pressure, unsigned int n, double *result) const
Definition FlowLaw.cc:128
void effective_viscosity(double hardness, double gamma, double *nu, double *dnu) const
Computes the regularized effective viscosity and its derivative with respect to the second invariant ...
Definition FlowLaw.cc:170
void hardness_n(const double *enthalpy, const double *pressure, unsigned int n, double *result) const
Definition FlowLaw.cc:123
std::string name() const
Definition FlowLaw.cc:68
double m_rho_g
ice density times acceleration due to gravity
Definition FlowLaw.hh:120
double exponent() const
Definition FlowLaw.cc:76
double softness(double E, double p) const
Definition FlowLaw.cc:115
virtual double softness_impl(double E, double p) const =0
double m_hardness_power
; used to compute hardness
Definition FlowLaw.hh:134
double m_beta_CC_grad
Clausius-Clapeyron gradient.
Definition FlowLaw.hh:122
double flow(double stress, double enthalpy, double pressure, double grain_size) const
The flow law itself.
Definition FlowLaw.cc:90
double m_ideal_gas_constant
ideal gas constant
Definition FlowLaw.hh:148
virtual double flow_impl(double stress, double E, double pressure, double grainsize) const
Definition FlowLaw.cc:95
double m_Q_warm
Activation energy, warm case.
Definition FlowLaw.hh:143
double m_n
power law exponent
Definition FlowLaw.hh:150
double hardness(double E, double p) const
Definition FlowLaw.cc:119
virtual void flow_n_impl(const double *stress, const double *E, const double *pressure, const double *grainsize, unsigned int n, double *result) const
Definition FlowLaw.cc:106
std::shared_ptr< EnthalpyConverter > EC() const
Definition FlowLaw.cc:72
#define PISM_ERROR_LOCATION
#define n
Definition exactTestM.c:37
#define rho
Definition exactTestM.c:35
void averaged_hardness_vec(const FlowLaw &ice, const array::Scalar &thickness, const array::Array3D &enthalpy, array::Scalar &result)
Definition FlowLaw.cc:189
double averaged_hardness(const FlowLaw &ice, double thickness, unsigned int kbelowH, const double *zlevels, const double *enthalpy)
Computes vertical average of B(E, p) ice hardness, namely .
Definition FlowLaw.cc:221
bool FlowLawUsesGrainSize(const FlowLaw &flow_law)
Definition FlowLaw.cc:271
static const double k
Definition exactTestP.cc:42
static const double h0
Definition exactTestP.cc:49