22#include "pism/stressbalance/sia/BedSmoother.hh"
23#include "pism/stressbalance/sia/SIAFD.hh"
24#include "pism/geometry/Geometry.hh"
25#include "pism/rheology/FlowLawFactory.hh"
26#include "pism/rheology/grain_size_vostok.hh"
27#include "pism/stressbalance/StressBalance.hh"
28#include "pism/util/EnthalpyConverter.hh"
29#include "pism/util/Grid.hh"
30#include "pism/util/Profiling.hh"
31#include "pism/util/Time.hh"
32#include "pism/util/array/CellType.hh"
33#include "pism/util/array/Scalar.hh"
34#include "pism/util/error_handling.hh"
35#include "pism/util/pism_utilities.hh"
36#include "pism/util/array/Vector.hh"
37#include "pism/util/Logger.hh"
40namespace stressbalance {
44 m_stencil_width(m_config->get_number(
"grid.max_stencil_width")),
45 m_work_2d_0(m_grid,
"work_vector_2d_0"),
46 m_work_2d_1(m_grid,
"work_vector_2d_1"),
49 m_D(m_grid,
"diffusivity"),
50 m_delta_0(m_grid,
"delta_0", array::WITH_GHOSTS, m_grid->z()),
51 m_delta_1(m_grid,
"delta_1", array::WITH_GHOSTS, m_grid->z()),
52 m_work_3d_0(m_grid,
"work_3d_0", array::WITH_GHOSTS, m_grid->z()),
53 m_work_3d_1(m_grid,
"work_3d_1", array::WITH_GHOSTS, m_grid->z()) {
61 m_config->get_number(
"stress_balance.sia.enhancement_factor_interglacial");
66 m_config->get_number(
"stress_balance.sia.Glen_exponent"));
69 const bool compute_grain_size_using_age =
70 m_config->get_flag(
"stress_balance.sia.grain_size_age_coupling");
71 const bool age_model_enabled =
m_config->get_flag(
"age.enabled");
72 const bool e_age_coupling =
m_config->get_flag(
"stress_balance.sia.e_age_coupling");
74 if (compute_grain_size_using_age) {
77 "flow law %s does not use grain size "
78 "but sia.grain_size_age_coupling was set",
82 if (not age_model_enabled) {
84 "SIAFD: age model is not active but\n"
85 "age is needed for grain-size-based flow law %s",
90 if (e_age_coupling and not age_model_enabled) {
92 "age is needed for age-dependent flow enhancement");
109 m_log->message(2,
"* Initializing the SIA stress balance modifier...\n");
110 m_log->message(2,
" [using the %s flow law]\n",
m_flow_law->name().c_str());
115 if (
m_config->get_flag(
"stress_balance.sia.e_age_coupling")) {
117 " using age-dependent enhancement factor:\n"
118 " e=%f for ice accumulated during interglacial periods\n"
119 " e=%f for ice accumulated during glacial periods\n",
193 const std::string method =
m_config->get_string(
"stress_balance.sia.surface_gradient_method");
195 if (method ==
"eta") {
199 }
else if (method ==
"haseloff") {
204 }
else if (method ==
"mahaffy") {
211 "value of sia.surface_gradient_method, option '-gradient %s', is not valid",
221 etapow = (2.0 *
n + 2.0) /
n,
222 invpow = 1.0 / etapow, dinvpow = (-
n - 2.0) / (2.0 *
n + 2.0);
233 for (
auto p :
m_grid->points_with_ghosts(GHOSTS)) {
234 const int i = p.i(), j = p.j();
236 eta(i, j) = pow(ice_thickness(i, j), etapow);
246 for (
auto p :
m_grid->points_with_ghosts(1)) {
247 const int i = p.i(), j = p.j();
249 auto b = bed_elevation.
box(i, j);
250 auto e = eta.
box(i, j);
254 double mean_eta = 0.5 * (e.e + e.c);
255 if (mean_eta > 0.0) {
256 double factor = invpow * pow(mean_eta, dinvpow);
257 h_x(i, j, 0) = factor * (e.e - e.c) / dx;
258 h_y(i, j, 0) = factor * (e.ne + e.n - e.se - e.s) / (4.0 * dy);
264 h_x(i, j, 0) += (b.e - b.c) / dx;
265 h_y(i, j, 0) += (b.ne + b.n - b.se - b.s) / (4.0 * dy);
270 double mean_eta = 0.5 * (e.n + e.c);
271 if (mean_eta > 0.0) {
272 double factor = invpow * pow(mean_eta, dinvpow);
273 h_x(i, j, 1) = factor * (e.ne + e.e - e.nw - e.w) / (4.0 * dx);
274 h_y(i, j, 1) = factor * (e.n - e.c) / dy;
280 h_x(i, j, 1) += (b.ne + b.e - b.nw - b.w) / (4.0 * dx);
281 h_y(i, j, 1) += (b.n - b.c) / dy;
303 for (
auto p :
m_grid->points_with_ghosts(1)) {
304 const int i = p.i(), j = p.j();
307 h_x(i, j, 0) = (h(i + 1, j) - h(i, j)) / dx;
308 h_y(i, j, 0) = (+h(i + 1, j + 1) + h(i, j + 1) - h(i + 1, j - 1) - h(i, j - 1)) / (4.0 * dy);
310 h_y(i, j, 1) = (h(i, j + 1) - h(i, j)) / dy;
311 h_x(i, j, 1) = (+h(i + 1, j + 1) + h(i + 1, j) - h(i - 1, j + 1) - h(i - 1, j)) / (4.0 * dx);
365 const double dx =
m_grid->dx(),
371 const auto &mask = cell_type;
375 assert(mask.stencil_width() >= 2);
380 assert(w_j.stencil_width() >= 1);
382 for (
auto p :
m_grid->points_with_ghosts(1)) {
383 const int i = p.i(), j = p.j();
387 if ((mask.floating_ice(i, j) && mask.ice_free_ocean(i + 1, j)) ||
388 (mask.ice_free_ocean(i, j) && mask.floating_ice(i + 1, j))) {
392 }
else if ((mask.icy(i, j) && mask.ice_free(i + 1, j) && h(i + 1, j) > h(i, j)) ||
393 (mask.ice_free(i, j) && mask.icy(i + 1, j) && h(i, j) > h(i + 1, j))) {
399 h_x(i, j, 0) = (h(i + 1, j) - h(i, j)) / dx;
406 if ((mask.floating_ice(i, j) && mask.ice_free_ocean(i, j + 1)) ||
407 (mask.ice_free_ocean(i, j) && mask.floating_ice(i, j + 1))) {
411 }
else if ((mask.icy(i, j) && mask.ice_free(i, j + 1) && h(i, j + 1) > h(i, j)) ||
412 (mask.ice_free(i, j) && mask.icy(i, j + 1) && h(i, j) > h(i, j + 1))) {
418 h_y(i, j, 1) = (h(i, j + 1) - h(i, j)) / dy;
424 for (
auto p :
m_grid->points()) {
425 const int i = p.i(), j = p.j();
430 double W = w_i(i, j) + w_i(i - 1, j) + w_i(i - 1, j + 1) + w_i(i, j + 1);
433 1.0 / W * (h_x(i, j, 0) + h_x(i - 1, j, 0) + h_x(i - 1, j + 1, 0) + h_x(i, j + 1, 0));
438 if (mask.icy(i, j)) {
439 double W = w_i(i, j) + w_i(i - 1, j);
441 h_x(i, j, 1) = 1.0 / W * (h_x(i, j, 0) + h_x(i - 1, j, 0));
446 double W = w_i(i, j + 1) + w_i(i - 1, j + 1);
448 h_x(i, j, 1) = 1.0 / W * (h_x(i - 1, j + 1, 0) + h_x(i, j + 1, 0));
459 double W = w_j(i, j) + w_j(i, j - 1) + w_j(i + 1, j - 1) + w_j(i + 1, j);
462 1.0 / W * (h_y(i, j, 1) + h_y(i, j - 1, 1) + h_y(i + 1, j - 1, 1) + h_y(i + 1, j, 1));
467 if (mask.icy(i, j)) {
468 double W = w_j(i, j) + w_j(i, j - 1);
470 h_y(i, j, 0) = 1.0 / W * (h_y(i, j, 1) + h_y(i, j - 1, 1));
475 double W = w_j(i + 1, j - 1) + w_j(i + 1, j);
477 h_y(i, j, 0) = 1.0 / W * (h_y(i + 1, j - 1, 1) + h_y(i + 1, j, 1));
542 D_limit =
m_config->get_number(
"stress_balance.sia.max_diffusivity");
544 const bool compute_grain_size_using_age =
545 m_config->get_flag(
"stress_balance.sia.grain_size_age_coupling"),
546 e_age_coupling =
m_config->get_flag(
"stress_balance.sia.e_age_coupling"),
547 limit_diffusivity =
m_config->get_flag(
"stress_balance.sia.limit_diffusivity"),
548 use_age = compute_grain_size_using_age or e_age_coupling;
568 list.add({ delta[0], delta[1] });
573 assert(theta.stencil_width() >= 2);
580 const std::vector<double> &z =
m_grid->z();
583 std::vector<double> depth(Mz), stress(Mz), pressure(Mz), E(Mz), flow(Mz);
584 std::vector<double> delta_ij(Mz);
585 std::vector<double> A(Mz),
586 ice_grain_size(Mz,
m_config->get_number(
"constants.ice.grain_size",
"m"));
590 int high_diffusivity_counter = 0;
591 for (
int o = 0; o < 2; o++) {
594 for (
auto p :
m_grid->points_with_ghosts(1)) {
595 const int i = p.i(), j = p.j();
599 const int oi = 1 - o, oj = o;
601 const double thk = 0.5 * (thk_smooth(i, j) + thk_smooth(i + oi, j + oj));
605 result(i, j, o) = 0.0;
612 const int ks =
m_grid->kBelowHeight(thk);
614 for (
int k = 0;
k <= ks; ++
k) {
615 depth[
k] = thk - z[
k];
620 m_EC->pressure(depth, ks, pressure);
624 *age_offset = age->
get_column(i + oi, j + oj);
626 for (
int k = 0;
k <= ks; ++
k) {
627 A[
k] = 0.5 * (age_ij[
k] + age_offset[
k]);
630 if (compute_grain_size_using_age) {
631 for (
int k = 0;
k <= ks; ++
k) {
637 if (e_age_coupling) {
638 for (
int k = 0;
k <= ks; ++
k) {
639 const double accumulation_time = current_time - A[
k];
650 const double *E_ij = enthalpy->
get_column(i, j),
651 *E_offset = enthalpy->
get_column(i + oi, j + oj);
652 for (
int k = 0;
k <= ks; ++
k) {
653 E[
k] = 0.5 * (E_ij[
k] + E_offset[
k]);
657 const double alpha = sqrt(PetscSqr(h_x(i, j, o)) + PetscSqr(h_y(i, j, o)));
658 for (
int k = 0;
k <= ks; ++
k) {
659 stress[
k] = alpha * pressure[
k];
662 m_flow_law->flow_n(stress.data(), E.data(), pressure.data(), ice_grain_size.data(), ks + 1,
665 const double theta_local = 0.5 * (theta(i, j) + theta(i + oi, j + oj));
666 for (
int k = 0;
k <= ks; ++
k) {
667 delta_ij[
k] = e_factor[
k] * theta_local * 2.0 * pressure[
k] * flow[
k];
672 for (
int k = 1;
k <= ks; ++
k) {
674 const double dz = z[
k] - z[
k - 1];
675 D += 0.5 * dz * ((depth[
k] + dz) * delta_ij[
k - 1] + depth[
k] * delta_ij[
k]);
678 const double dz = thk - z[ks];
679 D += 0.5 * dz * dz * delta_ij[ks];
699 if (limit_diffusivity and
D >= D_limit) {
701 high_diffusivity_counter += 1;
704 D_max = std::max(D_max,
D);
711 for (
unsigned int k = ks + 1;
k < Mz; ++
k) {
725 high_diffusivity_counter =
GlobalSum(
m_grid->com, high_diffusivity_counter);
733 "Maximum diffusivity of SIA flow (%f m2/s) is too high.\n"
734 "This probably means that the bed elevation or the ice thickness is "
736 "Increase stress_balance.sia.max_diffusivity to suppress this message.",
740 if (high_diffusivity_counter > 0) {
745 m_log->message(2,
" SIA diffusivity was capped at %.2f m2/s at %d locations.\n", D_limit,
746 high_diffusivity_counter);
755 for (
int o = 0; o < 2; o++) {
758 for (
auto p :
m_grid->points_with_ghosts(1)) {
759 const int i = p.i(), j = p.j();
761 const double slope = (o == 0) ? h_x(i, j, o) : h_y(i, j, o);
798 assert(I[0]->stencil_width() >= 1);
799 assert(I[1]->stencil_width() >= 1);
800 assert(delta[0]->stencil_width() >= 1);
801 assert(delta[1]->stencil_width() >= 1);
804 const unsigned int Mz =
m_grid->Mz();
806 std::vector<double> dz(Mz);
807 for (
unsigned int k = 1;
k < Mz; ++
k) {
811 for (
int o = 0; o < 2; ++o) {
814 for (
auto p :
m_grid->points_with_ghosts(1)) {
815 const int i = p.i(), j = p.j();
817 const int oi = 1 - o, oj = o;
818 const double thk = 0.5 * (thk_smooth(i, j) + thk_smooth(i + oi, j + oj));
820 const double *delta_ij = delta[o]->
get_column(i, j);
823 const unsigned int ks =
m_grid->kBelowHeight(thk);
827 double I_current = 0.0;
828 for (
unsigned int k = 1;
k <= ks; ++
k) {
830 I_current += 0.5 * dz[
k] * (delta_ij[
k - 1] + delta_ij[
k]);
835 for (
unsigned int k = ks + 1;
k < Mz; ++
k) {
875 const unsigned int Mz =
m_grid->Mz();
877 for (
auto p :
m_grid->points()) {
878 const int i = p.i(), j = p.j();
888 h_x_w = h_x(i - 1, j, 0),
889 h_x_e = h_x(i, j, 0),
890 h_x_n = h_x(i, j, 1),
891 h_x_s = h_x(i, j - 1, 1);
894 h_y_w = h_y(i - 1, j, 0),
895 h_y_e = h_y(i, j, 0),
896 h_y_n = h_y(i, j, 1),
897 h_y_s = h_y(i, j - 1, 1);
900 sliding_velocity_u = sliding_velocity(i, j).u,
901 sliding_velocity_v = sliding_velocity(i, j).v;
908 for (
unsigned int k = 0;
k < Mz; ++
k) {
909 u_ij[
k] = sliding_velocity_u - 0.25 * (I_e[
k] * h_x_e + I_w[
k] * h_x_w +
910 I_n[
k] * h_x_n + I_s[
k] * h_x_s);
912 for (
unsigned int k = 0;
k < Mz; ++
k) {
913 v_ij[
k] = sliding_velocity_v - 0.25 * (I_e[
k] * h_y_e + I_w[
k] * h_y_w +
914 I_n[
k] * h_y_n + I_s[
k] * h_y_s);
const units::System::Ptr m_sys
unit system used by this component
const Time & time() const
std::shared_ptr< const Config > m_config
configuration database used by this component
const std::shared_ptr< const Grid > m_grid
grid used by this component
const Profiling & profiling() const
std::shared_ptr< const Logger > m_log
logger (for easy access)
array::Scalar2 ice_surface_elevation
array::CellType2 cell_type
array::Scalar2 ice_thickness
array::Scalar2 bed_elevation
void failed()
Indicates a failure of a parallel section.
void begin(const char *name) const
void end(const char *name) const
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.
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
stencils::Box< T > box(int i, int j) const
void set_column(int i, int j, double c)
Set all values of scalar quantity to given a single value in a particular column.
double * get_column(int i, int j)
A virtual class collecting methods common to ice and bedrock 3D fields.
void set(double c)
Result: v[j] <- c for all j.
void update_ghosts()
Updates ghost points.
unsigned int stencil_width() const
Get the stencil width of the current Array. Returns 0 if ghosts are not available.
A class for storing and accessing internal staggered-grid 2D fields. Uses dof=2 storage....
std::shared_ptr< FlowLaw > create(const std::string &type_name, double exponent)
A relationship between the age of the ice and the grain size from the Vostok core.
void smoothed_thk(const array::Scalar &usurf, const array::Scalar &thk, const array::CellType2 &mask, array::Scalar &thksmooth) const
Computes a smoothed thickness map.
void theta(const array::Scalar &usurf, array::Scalar &result) const
void preprocess_bed(const array::Scalar &topg)
PISM bed smoother, plus bed roughness parameterization, based on Schoof (2003).
virtual void compute_diffusivity(bool full_update, const Geometry &geometry, const array::Array3D *enthalpy, const array::Array3D *age, const array::Staggered1 &h_x, const array::Staggered1 &h_y, array::Staggered1 &result)
Compute the SIA diffusivity. If full_update, also store delta on the staggered grid.
virtual void compute_surface_gradient(const Inputs &inputs, array::Staggered1 &h_x, array::Staggered1 &h_y)
Compute the ice surface gradient for the SIA.
virtual void surface_gradient_eta(const array::Scalar2 &ice_thickness, const array::Scalar2 &bed_elevation, array::Staggered1 &h_x, array::Staggered1 &h_y)
Compute the ice surface gradient using the eta-transformation.
virtual void update(const array::Vector &sliding_velocity, const Inputs &inputs, bool full_update)
Do the update; if full_update == false skip the update of 3D velocities and strain heating.
array::Scalar2 m_work_2d_0
temporary storage for eta, theta and the smoothed thickness
const array::Staggered & surface_gradient_x() const
bool interglacial(double accumulation_time) const
Determine if accumulation_time corresponds to an interglacial period.
virtual void surface_gradient_haseloff(const array::Scalar2 &ice_surface_elevation, const array::CellType2 &cell_type, array::Staggered1 &h_x, array::Staggered1 &h_y)
Compute the ice surface gradient using a modification of Marianne Haseloff's approach.
array::Staggered1 m_h_x
temporary storage for the surface gradient and the diffusivity
double m_seconds_per_year
const BedSmoother & bed_smoother() const
array::Array3D m_work_3d_1
const array::Staggered1 & diffusivity() const
virtual void compute_3d_horizontal_velocity(const Geometry &geometry, const array::Staggered &h_x, const array::Staggered &h_y, const array::Vector &sliding_velocity, array::Array3D &u_out, array::Array3D &v_out)
Compute horizontal components of the SIA velocity (in 3D).
virtual void surface_gradient_mahaffy(const array::Scalar &ice_surface_elevation, array::Staggered1 &h_x, array::Staggered1 &h_y)
Compute the ice surface gradient using the Mary Anne Mahaffy method; see [Mahaffy].
array::Array3D m_work_3d_0
temporary storage used to store I and strain_heating on the staggered grid
array::Array3D m_delta_0
temporary storage for delta on the staggered grid
double m_e_factor_interglacial
BedSmoother * m_bed_smoother
virtual void init()
Initialize the SIA module.
virtual void compute_I(const Geometry &geometry)
Compute I.
array::Scalar2 m_work_2d_1
virtual void compute_diffusive_flux(const array::Staggered &h_x, const array::Staggered &h_y, const array::Staggered &diffusivity, array::Staggered &result)
SIAFD(std::shared_ptr< const Grid > g)
const array::Staggered & surface_gradient_y() const
std::shared_ptr< EnthalpyConverter > m_EC
std::shared_ptr< rheology::FlowLaw > m_flow_law
array::Staggered1 m_diffusive_flux
Shallow stress balance modifier (such as the non-sliding SIA).
#define PISM_ERROR_LOCATION
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)