PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
BlatterTestHalfar.cc
Go to the documentation of this file.
1/* Copyright (C) 2020, 2021, 2022, 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
20#include "pism/stressbalance/blatter/verification/BlatterTestHalfar.hh"
21
22#include "pism/rheology/IsothermalGlen.hh"
23
24#include "pism/stressbalance/blatter/verification/manufactured_solutions.hh"
25
26namespace pism {
27namespace stressbalance {
28
29BlatterTestHalfar::BlatterTestHalfar(std::shared_ptr<const Grid> grid,
30 int Mz, int coarsening_factor)
31 : Blatter(grid, Mz, coarsening_factor) {
32
33 // use the isothermal Glen flow law
34 double n = m_config->get_number("stress_balance.blatter.Glen_exponent");
36
37 // make sure we use the same Glen exponent
38 assert(m_flow_law->exponent() == 3.0);
39
40 // make sure the grid is periodic in the Y direction
41 assert(m_grid->periodicity() == grid::Y_PERIODIC);
42
43 // store constant ice hardness (enthalpy and pressure values are irrelevant)
44 m_B = m_flow_law->hardness(1e5, 0);
45
46 // dome radius
47 m_R0 = 750e3;
48
49 // ice thickness (m)
50 m_H0 = 3600.0;
51
52 m_rho = m_config->get_number("constants.ice.density");
53
54 m_g = m_config->get_number("constants.standard_gravity");
55}
56
57bool BlatterTestHalfar::dirichlet_node(const DMDALocalInfo &info,
59 // use Dirichlet BC at x == 0 and the "cliff" near the right boundary
60 return I.i == 0 or I.i == info.mx - 1;
61}
62
63Vector2d BlatterTestHalfar::u_bc(double x, double y, double z) const {
64 (void) y;
65
66 return blatter_xz_halfar_exact(x, z, m_H0, m_R0, m_rho, m_g, m_B);
67}
68
69double BlatterTestHalfar::H_exact(double x) const {
70 if (fabs(x) >= m_R0) {
71 return 0.0;
72 }
73 return m_H0 * pow(1.0 - pow(fabs(x) / m_R0, 4.0 / 3.0), 3.0 / 7.0);
74}
75
76double BlatterTestHalfar::u_exact(double x, double z) const {
77 return u_bc(x, 0.0, z).u;
78}
79
81 const double *surface,
82 const double *bed,
83 Vector2d *residual) {
84
85 (void) surface;
86 (void) bed;
87
88 // compute x and z coordinates of quadrature points
89 double
90 *x = m_work[0],
91 *z = m_work[1];
92 {
93 double
94 *x_nodal = m_work[2],
95 *z_nodal = m_work[3];
96
97 for (int n = 0; n < fem::q13d::n_chi; ++n) {
98 x_nodal[n] = element.x(n);
99 z_nodal[n] = element.z(n);
100 }
101
102 element.evaluate(x_nodal, x);
103 element.evaluate(z_nodal, z);
104 }
105
106 // loop over all quadrature points
107 for (unsigned int q = 0; q < element.n_pts(); ++q) {
108 auto W = element.weight(q) / m_scaling;
109
110 auto F = blatter_xz_halfar_source(x[q], z[q], m_H0, m_R0, m_rho, m_g, m_B);
111
112 // loop over all test functions
113 for (int t = 0; t < element.n_chi(); ++t) {
114 const auto &psi = element.chi(q, t);
115
116 residual[t] += W * psi.val * F;
117 }
118 }
119}
120
122 const fem::Q1Element3Face &face,
123 const double *surface_nodal,
124 const double *z_nodal,
125 const double *sl_nodal,
126 Vector2d *residual) {
127 (void) surface_nodal;
128 (void) sl_nodal;
129
130 // compute x and z coordinates of quadrature points
131 double
132 *x = m_work[0],
133 *z = m_work[1];
134 {
135 double
136 *x_nodal = m_work[2];
137
138 for (int n = 0; n < element.n_chi(); ++n) {
139 x_nodal[n] = element.x(n);
140 }
141
142 face.evaluate(x_nodal, x);
143 face.evaluate(z_nodal, z);
144 }
145
146 // loop over all quadrature points
147 for (unsigned int q = 0; q < face.n_pts(); ++q) {
148 auto W = face.weight(q) / m_scaling;
149 auto N3 = face.normal(q);
150 Vector2d N = {N3.x, N3.y};
151
152 double F = 0.0;
153 if (x[q] > 0.0) {
154 // this lateral BC is not defined at the left (x = 0) boundary
156 }
157
158 // loop over all test functions
159 for (int t = 0; t < element.n_chi(); ++t) {
160 auto psi = face.chi(q, t);
161
162 residual[t] += W * psi * F * N;
163 }
164 }
165}
166
167/*!
168 * Top surface contribution to the residual.
169 */
171 const fem::Q1Element3Face &face,
172 Vector2d *residual) {
173
174 // compute x coordinates of quadrature points
175 double
176 *x = m_work[0];
177 {
178 double *x_nodal = m_work[1];
179
180 for (int n = 0; n < fem::q13d::n_chi; ++n) {
181 x_nodal[n] = element.x(n);
182 }
183
184 face.evaluate(x_nodal, x);
185 }
186
187 for (unsigned int q = 0; q < face.n_pts(); ++q) {
188 auto W = face.weight(q) / m_scaling;
189
191
192 // loop over all test functions
193 for (int t = 0; t < element.n_chi(); ++t) {
194 auto psi = face.chi(q, t);
195
196 residual[t] += - W * psi * F;
197 }
198 }
199}
200
201/*!
202 * Basal contribution to the residual.
203 */
205 const fem::Q1Element3Face &face,
206 const double *tauc_nodal,
207 const double *f_nodal,
208 const Vector2d *u_nodal,
209 Vector2d *residual) {
210 (void) tauc_nodal;
211 (void) f_nodal;
212 (void) u_nodal;
213
214 // compute x coordinates of quadrature points
215 double *x = m_work[0];
216 {
217 double *x_nodal = m_work[1];
218
219 for (int n = 0; n < fem::q13d::n_chi; ++n) {
220 x_nodal[n] = element.x(n);
221 }
222
223 face.evaluate(x_nodal, x);
224 }
225
226 for (unsigned int q = 0; q < face.n_pts(); ++q) {
227 auto W = face.weight(q) / m_scaling;
228
230
231 // loop over all test functions
232 for (int t = 0; t < element.n_chi(); ++t) {
233 auto psi = face.chi(q, t);
234
235 residual[t] += - W * psi * F;
236 }
237 }
238}
239
241 const double *tauc_nodal,
242 const double *f_nodal,
243 const Vector2d *u_nodal,
244 double K[2 * fem::q13d::n_chi][2 * fem::q13d::n_chi]) {
245 (void) face;
246 (void) tauc_nodal;
247 (void) f_nodal;
248 (void) u_nodal;
249 (void) K;
250 // empty: residual contribution from the basal boundary does not depend on ice velocity
251}
252
253} // end of namespace stressbalance
254} // end of namespace pism
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
This class represents a 2D vector field (such as ice velocity) at a certain grid point.
Definition Vector2d.hh:29
void evaluate(const T *x, T *vals, T *dx, T *dy, T *dz) const
Given nodal values, compute the values and partial derivatives at the quadrature points.
Definition Element.hh:285
int n_chi() const
Definition Element.hh:65
double weight(unsigned int q) const
Weight of the quadrature point q
Definition Element.hh:85
const Germ & chi(unsigned int q, unsigned int k) const
Definition Element.hh:73
unsigned int n_pts() const
Number of quadrature points.
Definition Element.hh:80
double weight(unsigned int q) const
Definition Element.hh:414
unsigned int n_pts() const
Number of quadrature points.
Definition Element.hh:411
const double & chi(unsigned int q, unsigned int k) const
Definition Element.hh:419
void evaluate(const T *x, T *result) const
Definition Element.hh:434
const Vector3 & normal(unsigned int q) const
Definition Element.hh:426
double z(int n) const
Definition Element.hh:381
double x(int n) const
Definition Element.hh:371
3D Q1 element
Definition Element.hh:358
Isothermal Glen ice allowing extra customization.
void residual_basal(const fem::Q1Element3 &element, const fem::Q1Element3Face &face, const double *tauc_nodal, const double *f_nodal, const Vector2d *u_nodal, Vector2d *residual)
void residual_lateral(const fem::Q1Element3 &element, const fem::Q1Element3Face &face, const double *surface_nodal, const double *z_nodal, const double *sl_nodal, Vector2d *residual)
double u_exact(double x, double z) const
bool dirichlet_node(const DMDALocalInfo &info, const fem::Element3::GlobalIndex &I)
void residual_surface(const fem::Q1Element3 &element, const fem::Q1Element3Face &face, Vector2d *residual)
Vector2d u_bc(double x, double y, double z) const
void residual_source_term(const fem::Q1Element3 &element, const double *surface, const double *bed, Vector2d *residual)
BlatterTestHalfar(std::shared_ptr< const Grid > grid, int Mz, int coarsening_factor)
void jacobian_basal(const fem::Q1Element3Face &face, const double *tauc_nodal, const double *f_nodal, const Vector2d *u_nodal, double K[2 *fem::q13d::n_chi][2 *fem::q13d::n_chi])
double m_work[m_n_work][m_Nq]
Definition Blatter.hh:109
std::shared_ptr< rheology::FlowLaw > m_flow_law
std::shared_ptr< EnthalpyConverter > m_EC
#define n
Definition exactTestM.c:37
const int n_chi
Number of shape functions on a Q1 element.
Definition FEM.hh:213
static double F(double SL, double B, double H, double alpha)
Vector2d blatter_xz_halfar_source_lateral(double x, double z, double H_0, double R_0, double rho_i, double g, double B)
Vector2d blatter_xz_halfar_source(double x, double z, double H_0, double R_0, double rho_i, double g, double B)
Vector2d blatter_xz_halfar_exact(double x, double z, double H_0, double R_0, double rho_i, double g, double B)
Vector2d blatter_xz_halfar_source_surface(double x, double H_0, double R_0, double rho_i, double g, double B)
Vector2d blatter_xz_halfar_source_base(double x, double H_0, double R_0, double rho_i, double g, double B)