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"
39 namespace stressbalance {
43 m_stencil_width(m_config->get_number(
"grid.max_stencil_width")),
44 m_work_2d_0(m_grid,
"work_vector_2d_0"),
45 m_work_2d_1(m_grid,
"work_vector_2d_1"),
48 m_D(m_grid,
"diffusivity"),
49 m_delta_0(m_grid,
"delta_0", array::
WITH_GHOSTS, m_grid->z()),
50 m_delta_1(m_grid,
"delta_1", array::
WITH_GHOSTS, m_grid->z()),
51 m_work_3d_0(m_grid,
"work_3d_0", array::
WITH_GHOSTS, m_grid->z()),
52 m_work_3d_1(m_grid,
"work_3d_1", array::
WITH_GHOSTS, m_grid->z()) {
60 m_config->get_number(
"stress_balance.sia.enhancement_factor_interglacial");
67 const bool compute_grain_size_using_age =
68 m_config->get_flag(
"stress_balance.sia.grain_size_age_coupling");
69 const bool age_model_enabled =
m_config->get_flag(
"age.enabled");
70 const bool e_age_coupling =
m_config->get_flag(
"stress_balance.sia.e_age_coupling");
72 if (compute_grain_size_using_age) {
75 "flow law %s does not use grain size "
76 "but sia.grain_size_age_coupling was set",
80 if (not age_model_enabled) {
82 "SIAFD: age model is not active but\n"
83 "age is needed for grain-size-based flow law %s",
88 if (e_age_coupling and not age_model_enabled) {
90 "age is needed for age-dependent flow enhancement");
107 m_log->message(2,
"* Initializing the SIA stress balance modifier...\n");
108 m_log->message(2,
" [using the %s flow law]\n",
m_flow_law->name().c_str());
113 if (
m_config->get_flag(
"stress_balance.sia.e_age_coupling")) {
115 " using age-dependent enhancement factor:\n"
116 " e=%f for ice accumulated during interglacial periods\n"
117 " e=%f for ice accumulated during glacial periods\n",
191 const std::string method =
m_config->get_string(
"stress_balance.sia.surface_gradient_method");
193 if (method ==
"eta") {
197 }
else if (method ==
"haseloff") {
202 }
else if (method ==
"mahaffy") {
209 "value of sia.surface_gradient_method, option '-gradient %s', is not valid",
219 etapow = (2.0 *
n + 2.0) /
n,
220 invpow = 1.0 / etapow, dinvpow = (-
n - 2.0) / (2.0 *
n + 2.0);
231 for (
auto p =
m_grid->points(GHOSTS); p; p.next()) {
232 const int i = p.i(), j = p.j();
234 eta(i, j) = pow(ice_thickness(i, j), etapow);
244 for (
auto p =
m_grid->points(1); p; p.next()) {
245 const int i = p.i(), j = p.j();
247 auto b = bed_elevation.
box(i, j);
248 auto e = eta.
box(i, j);
252 double mean_eta = 0.5 * (e.e + e.c);
253 if (mean_eta > 0.0) {
254 double factor = invpow * pow(mean_eta, dinvpow);
255 h_x(i, j, 0) = factor * (e.e - e.c) / dx;
256 h_y(i, j, 0) = factor * (e.ne + e.n - e.se - e.s) / (4.0 * dy);
262 h_x(i, j, 0) += (b.e - b.c) / dx;
263 h_y(i, j, 0) += (b.ne + b.n - b.se - b.s) / (4.0 * dy);
268 double mean_eta = 0.5 * (e.n + e.c);
269 if (mean_eta > 0.0) {
270 double factor = invpow * pow(mean_eta, dinvpow);
271 h_x(i, j, 1) = factor * (e.ne + e.e - e.nw - e.w) / (4.0 * dx);
272 h_y(i, j, 1) = factor * (e.n - e.c) / dy;
278 h_x(i, j, 1) += (b.ne + b.e - b.nw - b.w) / (4.0 * dx);
279 h_y(i, j, 1) += (b.n - b.c) / dy;
301 for (
auto p =
m_grid->points(1); p; p.next()) {
302 const int i = p.i(), j = p.j();
305 h_x(i, j, 0) = (h(i + 1, j) - h(i, j)) / dx;
306 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);
308 h_y(i, j, 1) = (h(i, j + 1) - h(i, j)) / dy;
309 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);
363 const double dx =
m_grid->dx(),
369 const auto &mask = cell_type;
373 assert(mask.stencil_width() >= 2);
378 assert(w_j.stencil_width() >= 1);
380 for (
auto p =
m_grid->points(1); p; p.next()) {
381 const int i = p.i(), j = p.j();
385 if ((mask.floating_ice(i, j) && mask.ice_free_ocean(i + 1, j)) ||
386 (mask.ice_free_ocean(i, j) && mask.floating_ice(i + 1, j))) {
390 }
else if ((mask.icy(i, j) && mask.ice_free(i + 1, j) && h(i + 1, j) > h(i, j)) ||
391 (mask.ice_free(i, j) && mask.icy(i + 1, j) && h(i, j) > h(i + 1, j))) {
397 h_x(i, j, 0) = (h(i + 1, j) - h(i, j)) / dx;
404 if ((mask.floating_ice(i, j) && mask.ice_free_ocean(i, j + 1)) ||
405 (mask.ice_free_ocean(i, j) && mask.floating_ice(i, j + 1))) {
409 }
else if ((mask.icy(i, j) && mask.ice_free(i, j + 1) && h(i, j + 1) > h(i, j)) ||
410 (mask.ice_free(i, j) && mask.icy(i, j + 1) && h(i, j) > h(i, j + 1))) {
416 h_y(i, j, 1) = (h(i, j + 1) - h(i, j)) / dy;
422 for (
auto p =
m_grid->points(); p; p.next()) {
423 const int i = p.i(), j = p.j();
428 double W = w_i(i, j) + w_i(i - 1, j) + w_i(i - 1, j + 1) + w_i(i, j + 1);
431 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));
436 if (mask.icy(i, j)) {
437 double W = w_i(i, j) + w_i(i - 1, j);
439 h_x(i, j, 1) = 1.0 / W * (h_x(i, j, 0) + h_x(i - 1, j, 0));
444 double W = w_i(i, j + 1) + w_i(i - 1, j + 1);
446 h_x(i, j, 1) = 1.0 / W * (h_x(i - 1, j + 1, 0) + h_x(i, j + 1, 0));
457 double W = w_j(i, j) + w_j(i, j - 1) + w_j(i + 1, j - 1) + w_j(i + 1, j);
460 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));
465 if (mask.icy(i, j)) {
466 double W = w_j(i, j) + w_j(i, j - 1);
468 h_y(i, j, 0) = 1.0 / W * (h_y(i, j, 1) + h_y(i, j - 1, 1));
473 double W = w_j(i + 1, j - 1) + w_j(i + 1, j);
475 h_y(i, j, 0) = 1.0 / W * (h_y(i + 1, j - 1, 1) + h_y(i + 1, j, 1));
540 D_limit =
m_config->get_number(
"stress_balance.sia.max_diffusivity");
542 const bool compute_grain_size_using_age =
543 m_config->get_flag(
"stress_balance.sia.grain_size_age_coupling"),
544 e_age_coupling =
m_config->get_flag(
"stress_balance.sia.e_age_coupling"),
545 limit_diffusivity =
m_config->get_flag(
"stress_balance.sia.limit_diffusivity"),
546 use_age = compute_grain_size_using_age or e_age_coupling;
566 list.add({ delta[0], delta[1] });
571 assert(theta.stencil_width() >= 2);
578 const std::vector<double> &z =
m_grid->z();
581 std::vector<double> depth(Mz), stress(Mz), pressure(Mz), E(Mz), flow(Mz);
582 std::vector<double> delta_ij(Mz);
583 std::vector<double> A(Mz),
584 ice_grain_size(Mz,
m_config->get_number(
"constants.ice.grain_size",
"m"));
588 int high_diffusivity_counter = 0;
589 for (
int o = 0; o < 2; o++) {
592 for (
auto p =
m_grid->points(1); p; p.next()) {
593 const int i = p.i(), j = p.j();
597 const int oi = 1 - o, oj = o;
599 const double thk = 0.5 * (thk_smooth(i, j) + thk_smooth(i + oi, j + oj));
603 result(i, j, o) = 0.0;
610 const int ks =
m_grid->kBelowHeight(thk);
612 for (
int k = 0;
k <= ks; ++
k) {
613 depth[
k] = thk - z[
k];
618 m_EC->pressure(depth, ks, pressure);
622 *age_offset = age->
get_column(i + oi, j + oj);
624 for (
int k = 0;
k <= ks; ++
k) {
625 A[
k] = 0.5 * (age_ij[
k] + age_offset[
k]);
628 if (compute_grain_size_using_age) {
629 for (
int k = 0;
k <= ks; ++
k) {
635 if (e_age_coupling) {
636 for (
int k = 0;
k <= ks; ++
k) {
637 const double accumulation_time = current_time - A[
k];
648 const double *E_ij = enthalpy->
get_column(i, j),
649 *E_offset = enthalpy->
get_column(i + oi, j + oj);
650 for (
int k = 0;
k <= ks; ++
k) {
651 E[
k] = 0.5 * (E_ij[
k] + E_offset[
k]);
655 const double alpha = sqrt(PetscSqr(h_x(i, j, o)) + PetscSqr(h_y(i, j, o)));
656 for (
int k = 0;
k <= ks; ++
k) {
657 stress[
k] = alpha * pressure[
k];
660 m_flow_law->flow_n(&stress[0], &E[0], &pressure[0], &ice_grain_size[0], ks + 1, &flow[0]);
662 const double theta_local = 0.5 * (theta(i, j) + theta(i + oi, j + oj));
663 for (
int k = 0;
k <= ks; ++
k) {
664 delta_ij[
k] = e_factor[
k] * theta_local * 2.0 * pressure[
k] * flow[
k];
669 for (
int k = 1;
k <= ks; ++
k) {
671 const double dz = z[
k] - z[
k - 1];
672 D += 0.5 * dz * ((depth[
k] + dz) * delta_ij[
k - 1] + depth[
k] * delta_ij[
k]);
675 const double dz = thk - z[ks];
676 D += 0.5 * dz * dz * delta_ij[ks];
696 if (limit_diffusivity and
D >= D_limit) {
698 high_diffusivity_counter += 1;
708 for (
unsigned int k = ks + 1;
k < Mz; ++
k) {
722 high_diffusivity_counter =
GlobalSum(
m_grid->com, high_diffusivity_counter);
730 "Maximum diffusivity of SIA flow (%f m2/s) is too high.\n"
731 "This probably means that the bed elevation or the ice thickness is "
733 "Increase stress_balance.sia.max_diffusivity to suppress this message.",
737 if (high_diffusivity_counter > 0) {
742 m_log->message(2,
" SIA diffusivity was capped at %.2f m2/s at %d locations.\n", D_limit,
743 high_diffusivity_counter);
752 for (
int o = 0; o < 2; o++) {
755 for (
auto p =
m_grid->points(1); p; p.next()) {
756 const int i = p.i(), j = p.j();
758 const double slope = (o == 0) ? h_x(i, j, o) : h_y(i, j, o);
795 assert(
I[0]->stencil_width() >= 1);
796 assert(
I[1]->stencil_width() >= 1);
797 assert(delta[0]->stencil_width() >= 1);
798 assert(delta[1]->stencil_width() >= 1);
801 const unsigned int Mz =
m_grid->Mz();
803 std::vector<double> dz(Mz);
804 for (
unsigned int k = 1;
k < Mz; ++
k) {
808 for (
int o = 0; o < 2; ++o) {
811 for (
auto p =
m_grid->points(1); p; p.next()) {
812 const int i = p.i(), j = p.j();
814 const int oi = 1 - o, oj = o;
815 const double thk = 0.5 * (thk_smooth(i, j) + thk_smooth(i + oi, j + oj));
817 const double *delta_ij = delta[o]->
get_column(i, j);
818 double *I_ij =
I[o]->get_column(i, j);
820 const unsigned int ks =
m_grid->kBelowHeight(thk);
824 double I_current = 0.0;
825 for (
unsigned int k = 1;
k <= ks; ++
k) {
827 I_current += 0.5 * dz[
k] * (delta_ij[
k - 1] + delta_ij[
k]);
832 for (
unsigned int k = ks + 1;
k < Mz; ++
k) {
872 const unsigned int Mz =
m_grid->Mz();
874 for (
auto p =
m_grid->points(); p; p.next()) {
875 const int i = p.i(), j = p.j();
878 *I_e =
I[0]->get_column(i, j),
879 *I_w =
I[0]->get_column(i - 1, j),
880 *I_n =
I[1]->get_column(i, j),
881 *I_s =
I[1]->get_column(i, j - 1);
885 h_x_w = h_x(i - 1, j, 0),
886 h_x_e = h_x(i, j, 0),
887 h_x_n = h_x(i, j, 1),
888 h_x_s = h_x(i, j - 1, 1);
891 h_y_w = h_y(i - 1, j, 0),
892 h_y_e = h_y(i, j, 0),
893 h_y_n = h_y(i, j, 1),
894 h_y_s = h_y(i, j - 1, 1);
897 sliding_velocity_u = sliding_velocity(i, j).u,
898 sliding_velocity_v = sliding_velocity(i, j).v;
905 for (
unsigned int k = 0;
k < Mz; ++
k) {
906 u_ij[
k] = sliding_velocity_u - 0.25 * (I_e[
k] * h_x_e + I_w[
k] * h_x_w +
907 I_n[
k] * h_x_n + I_s[
k] * h_x_s);
909 for (
unsigned int k = 0;
k < Mz; ++
k) {
910 v_ij[
k] = sliding_velocity_v - 0.25 * (I_e[
k] * h_y_e + I_w[
k] * h_y_w +
911 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
const Config::ConstPtr m_config
configuration database used by this component
const Logger::ConstPtr m_log
logger (for easy access)
const std::shared_ptr< const Grid > m_grid
grid used by this component
const Profiling & profiling() const
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()
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
virtual void compute_3d_horizontal_velocity(const Geometry &geometry, const array::Staggered &h_x, const array::Staggered &h_y, const array::Vector &vel_input, array::Array3D &u_out, array::Array3D &v_out)
Compute horizontal components of the SIA velocity (in 3D).
const array::Staggered1 & diffusivity() const
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
EnthalpyConverter::Ptr 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 max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
bool FlowLawUsesGrainSize(const FlowLaw &flow_law)
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)