25#include "pism/stressbalance/blatter/Blatter.hh"
26#include "pism/util/error_handling.hh"
27#include "pism/util/Vector2d.hh"
29#include "pism/stressbalance/blatter/util/DataAccess.hh"
30#include "pism/stressbalance/blatter/util/grid_hierarchy.hh"
31#include "pism/util/node_types.hh"
33#include "pism/rheology/FlowLawFactory.hh"
35#include "pism/geometry/Geometry.hh"
36#include "pism/stressbalance/StressBalance.hh"
37#include "pism/util/array/Array3D.hh"
38#include "pism/util/pism_options.hh"
39#include "pism/util/pism_utilities.hh"
40#include "pism/util/fem/Quadrature.hh"
41#include "pism/util/Logger.hh"
42#include "pism/util/io/IO_Flags.hh"
45namespace stressbalance {
65 int ierr = DMDAGetLocalInfo(
m_da, &info);
PISM_CHK(ierr,
"DMDAGetLocalInfo");
77 for (
int j = info.gys; j < info.gys + info.gym - 1; j++) {
78 for (
int i = info.gxs; i < info.gxs + info.gxm - 1; i++) {
87 if (p[
k].thickness < min_thickness) {
96 node_type(ii, jj) +=
static_cast<double>(interior);
105 for (
int j = info.ys; j < info.ys + info.ym; j++) {
106 for (
int i = info.xs; i < info.xs + info.xm; i++) {
108 switch ((
int)node_type(i, j)) {
152 for (
int n = 0;
n < N; ++
n) {
178 for (
int n = 0;
n < N; ++
n) {
186 return grounded and floating;
205 for (
int n = 0;
n < N; ++
n) {
207 if (z[
k] > sea_level[
k]) {
214 return above and below;
229 const int *node_type,
230 const double *ice_bottom,
231 const double *sea_level) {
238 for (
int n = 0;
n < N; ++
n) {
246 for (
int n = 0;
n < N; ++
n) {
247 if (ice_bottom[nodes[
n]] < sea_level[nodes[
n]]) {
263 m_parameters(grid,
"bp_input_parameters", array::WITH_GHOSTS),
264 m_face4(grid->dx(), grid->dy(), fem::Q1Quadrature4()),
265 m_face100(grid->dx(), grid->dy(), fem::Q1QuadratureN(10))
271 auto pism_da =
grid->get_dm(1, 0);
273 int ierr =
setup(*pism_da,
grid->periodicity(), Mz, coarsening_factor,
"bp_");
276 "Failed to allocate a Blatter solver instance");
280 std::vector<double> sigma(Mz);
281 double dz = 1.0 / (Mz - 1.0);
282 for (
int i = 0; i < Mz; ++i) {
289 .long_name(
"u velocity component on the sigma grid")
294 .long_name(
"v velocity component on the sigma grid")
297 std::map<std::string,std::string> z_attrs =
299 {
"long_name",
"scaled Z-coordinate in the ice (z_base=0, z_surface=1)"},
303 auto &z_u =
m_u_sigma->metadata(0).dimension(
"z");
304 auto &z_v =
m_v_sigma->metadata(0).dimension(
"z");
306 z_u.set_name(
"z_sigma").clear();
307 z_v.set_name(
"z_sigma").clear();
309 for (
const auto &z_attr : z_attrs) {
310 z_u.set_string(z_attr.first, z_attr.second);
311 z_v.set_string(z_attr.first, z_attr.second);
320 m_config->get_number(
"stress_balance.blatter.Glen_exponent"));
323 double g =
m_config->get_number(
"constants.standard_gravity");
332 double E =
m_config->get_number(
"stress_balance.blatter.enhancement_factor");
335 double softness =
m_config->get_number(
"flow_law.isothermal_Glen.ice_softness"),
352 Mat mrestrict, Vec rscale, Mat inject,
353 DM coarse,
void *ctx) {
360 PetscErrorCode ierr =
restrict_data(fine, coarse,
"3D_DM"); CHKERRQ(ierr);
371 ierr = DMGetOptionsPrefix(dm_fine, &prefix); CHKERRQ(ierr);
376 ierr =
setup_level(dm_coarse, mg_levels); CHKERRQ(ierr);
390 int coarsening_factor,
391 const std::string &prefix) {
393 PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pism_da, &comm); CHKERRQ(ierr);
397 auto option =
pism::printf(
"-%spc_mg_levels", prefix.c_str());
405 int c = coarsening_factor;
410 if (((mz - 1) / c) * c != mz - 1) {
411 int N = std::pow(c, (
int)mg_levels - 1);
412 auto message =
pism::printf(
"Blatter stress balance solver: settings\n"
413 "stress_balance.blatter.Mz = %d,\n"
414 "stress_balance.blatter.coarsening_factor = %d,\n"
415 "and '%s %d' are not compatible.\n"
416 "To use N = %d multigrid levels with the coarsening factor C = %d\n"
417 "stress_balance.blatter.Mz has to be equal to A * C^(N - 1) + 1\n"
418 "for some positive integer A, e.g. %d, %d, %d, ...",
419 Mz, c, option.c_str(), mg_levels, mg_levels, c,
420 N + 1, 2*N + 1, 3*N + 1);
423 mz = (mz - 1) / c + 1;
436 ierr = DMDAGetInfo(pism_da,
451 ierr = DMDAGetOwnershipRanges(pism_da, &lx, &ly, NULL); CHKERRQ(ierr);
459 bx = (periodicity &
grid::X_PERIODIC) != 0 ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE,
460 by = (periodicity &
grid::Y_PERIODIC) != 0 ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE,
461 bz = DM_BOUNDARY_NONE;
463 ierr = DMDACreate3d(comm,
473 ierr = DMSetOptionsPrefix(
m_da, prefix.c_str()); CHKERRQ(ierr);
476 ierr = DMDASetRefinementFactor(
m_da, coarsening_factor, 1, 1); CHKERRQ(ierr);
478 ierr = DMSetFromOptions(
m_da); CHKERRQ(ierr);
480 ierr = DMSetUp(
m_da); CHKERRQ(ierr);
492 ierr = DMCreateGlobalVector(
m_da,
m_x.
rawptr()); CHKERRQ(ierr);
494 ierr = VecSetOptionsPrefix(
m_x, prefix.c_str()); CHKERRQ(ierr);
496 ierr = VecSetFromOptions(
m_x); CHKERRQ(ierr);
503 ierr = SNESCreate(comm,
m_snes.
rawptr()); CHKERRQ(ierr);
505 ierr = SNESSetOptionsPrefix(
m_snes, prefix.c_str()); CHKERRQ(ierr);
507 ierr = SNESSetDM(
m_snes,
m_da); CHKERRQ(ierr);
509 ierr = DMDASNESSetFunctionLocal(
m_da, INSERT_VALUES,
510#
if PETSC_VERSION_LT(3,21,0)
515 this); CHKERRQ(ierr);
517 ierr = DMDASNESSetJacobianLocal(
m_da,
518#
if PETSC_VERSION_LT(3,21,0)
523 this); CHKERRQ(ierr);
525 ierr = SNESSetFromOptions(
m_snes); CHKERRQ(ierr);
528 PetscBool ksp_use_ew = PETSC_FALSE;
529 ierr = SNESKSPGetUseEW(
m_snes, &ksp_use_ew); CHKERRQ(ierr);
542 ice_density =
m_config->get_number(
"constants.ice.density"),
543 water_density =
m_config->get_number(
"constants.sea_water.density"),
544 alpha = ice_density / water_density;
555 for (
auto p :
m_grid->points()) {
556 const int i = p.i(), j = p.j();
559 b_grounded = b(i, j),
560 b_floating = sea_level(i, j) - alpha * H(i, j),
561 s_grounded = b(i, j) + H(i, j),
562 s_floating = sea_level(i, j) + (1.0 - alpha) * H(i, j);
567 m_parameters(i, j).bed = std::max(b_grounded, b_floating);
569 m_parameters(i, j).floatation = s_floating - s_grounded;
576 double H_min =
m_config->get_number(
"stress_balance.ice_free_thickness_standard");
588 const auto *enthalpy = inputs.
enthalpy;
590 const auto &zlevels = enthalpy->
levels();
591 auto Mz = zlevels.size();
597 int ierr = DMDAGetLocalInfo(da, &info);
PISM_CHK(ierr,
"DMDAGetLocalInfo");
607 for (
auto p :
m_grid->points()) {
608 const int i = p.i(), j = p.j();
610 double H = ice_thickness(i, j);
612 const double *E = enthalpy->get_column(i, j);
614 for (
int k = 0;
k < Mz_sigma; ++
k) {
616 z =
grid_z(0.0, H, Mz_sigma,
k),
618 pressure =
m_EC->pressure(depth),
621 auto k0 =
m_grid->kBelowHeight(z);
624 double lambda = (z - zlevels[k0]) / (zlevels[k0 + 1] - zlevels[k0]);
626 E_local = (1.0 - lambda) * E[k0] + lambda * E[k0 + 1];
631 hardness[j][i][
k] =
m_flow_law->hardness(E_local, pressure);
647 double *bottom_elevation,
648 double *ice_thickness,
649 double *surface_elevation,
650 double *sea_level)
const {
655 auto p = P[I.j][I.i];
657 node_type[
n] =
static_cast<int>(p.node_type);
658 bottom_elevation[
n] = p.bed;
659 ice_thickness[
n] = p.thickness;
661 if (surface_elevation !=
nullptr) {
662 surface_elevation[
n] = p.bed + p.thickness;
665 if (sea_level !=
nullptr) {
666 sea_level[
n] = p.sea_level;
672 m_log->message(2,
"* Initializing the Blatter stress balance...\n");
680 unsigned int start = input_file.
nrecords() - 1;
682 if (u_sigma_found and v_sigma_found) {
683 m_log->message(3,
"Reading uvel_sigma and vvel_sigma...\n");
691 "uvel_sigma and vvel_sigma not found");
694 int ierr = VecSet(
m_x, 0.0);
PISM_CHK(ierr,
"VecSet");
710 int ierr = DMDAGetLocalInfo(
m_da, &info);
PISM_CHK(ierr,
"DMDAGetLocalInfo");
717 double R_min = 1e16, R_max = 0.0, R_avg = 0.0;
719 double n_cells = 0.0;
722 for (
int j = info.ys; j < info.ys + info.ym - 1; j++) {
723 for (
int i = info.xs; i < info.xs + info.xm - 1; i++) {
730 for (
int k = 0;
k < 4; ++
k) {
741 for (
int k = 0;
k < 4; ++
k) {
742 dz_max = std::max(P[
k].thickness / (info.mz - 1), dz_max);
744 double R = dz_max / dxy;
746 R_min = std::min(R, R_min);
747 R_max = std::max(R, R_max);
754 R_avg /= std::max(n_cells, 1.0);
760 "Blatter solver: %d * (%d - 1) = %d active elements\n",
761 (
int)n_cells, (
int)info.mz, (
int)(n_cells * (info.mz - 1)));
765 " Vertical spacing (m): min = %3.3f, max = %3.3f, avg = %3.3f\n",
766 R_min * dxy, R_max * dxy, R_avg * dxy);
768 " Aspect ratios: min = %3.3f, max = %3.3f, avg = %3.3f, max/min = %3.3f\n",
769 R_min, R_max, R_avg, R_max / R_min);
784 PISM_CHK(ierr,
"SNESGetConvergedReason");
787 PISM_CHK(ierr,
"SNESGetIterationNumber");
789 ierr = SNESGetLinearSolveIterations(
m_snes, &result.
ksp_it);
790 PISM_CHK(ierr,
"SNESGetLinearSolveIterations");
793 ierr = SNESGetKSP(
m_snes, &ksp);
796 ierr = KSPGetConvergedReason(ksp, &result.
ksp_reason);
797 PISM_CHK(ierr,
"KSPGetConvergedReason");
800 ierr = KSPGetPC(ksp, &pc);
804 ierr = PCGetType(pc, &pc_type);
807 if (pc_type !=
nullptr and std::string(pc_type) == PCMG) {
809 ierr = PCMGGetCoarseSolve(pc, &coarse_ksp);
810 PISM_CHK(ierr,
"PCMGGetCoarseSolve");
813 PISM_CHK(ierr,
"KSPGetIterationNumber");
826 int snes_total_it = 0;
827 int ksp_total_it = 0;
833 schoofLen =
m_config->get_number(
"flow_law.Schoof_regularizing_length",
"m"),
834 schoofVel =
m_config->get_number(
"flow_law.Schoof_regularizing_velocity",
"m second-1"),
836 eps = pow(schoofVel / schoofLen, 2.0),
838 gamma = std::floor(std::log10(eps)),
853 PetscInt snes_max_it = 0;
854 ierr = SNESGetTolerances(
m_snes, NULL, NULL, NULL, &snes_max_it, NULL);
855 PISM_CHK(ierr,
"SNESGetTolerances");
857 double lambda = lambda_min;
858 double delta = delta0;
865 "Blatter solver: Starting parameter continuation with lambda = %f\n",
868 for (
int N = 0; N < Nc; ++N) {
872 m_log->message(2,
"Blatter solver: step %d with lambda = %f, eps = %e\n",
879 ksp_total_it += info.
ksp_it;
882 m_log->message(2,
"Blatter solver continuation step #%d: %s\n"
883 " lambda = %f, eps = %e\n"
884 " SNES: %d, KSP: %d\n",
889 " Coarse MG level KSP (last iteration): %d\n",
899 info.
ksp_it = ksp_total_it;
909 double F = (snes_max_it - info.
snes_it) / (
double)snes_max_it;
910 delta *= 1.0 + A *
F *
F;
913 delta = std::min(delta, delta_max);
916 if (lambda + delta > lambda_max) {
917 delta = lambda_max - lambda;
920 m_log->message(2,
" Advancing lambda from %f to %f (delta = %f)\n",
921 lambda, lambda + delta, delta);
924 }
else if (info.
snes_reason == SNES_DIVERGED_LINE_SEARCH or
934 if (lambda < lambda_min) {
935 m_log->message(2,
"Blatter solver: Parameter continuation failed at step 1\n");
939 if (std::fabs(delta - delta_min) < 1e-6) {
940 m_log->message(2,
"Blatter solver: cannot reduce the continuation step\n");
947 delta =
pism::clip(delta, delta_min, delta_max);
953 m_log->message(2,
" Back-tracking to lambda = %f using delta = %f\n",
960 m_log->message(2,
"Blatter solver failed after %d parameter continuation steps\n", Nc);
971 ierr = SNESKSPSetUseEW(snes, PETSC_FALSE);
975 ierr = SNESGetKSP(snes, &ksp);
978 ierr = KSPSetTolerances(ksp, rtol, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
985 ierr = SNESKSPSetUseEW(snes, PETSC_TRUE);
PISM_CHK(ierr,
"SNESKSPSetUseEW");
994 schoofLen =
m_config->get_number(
"flow_law.Schoof_regularizing_length",
"m"),
995 schoofVel =
m_config->get_number(
"flow_law.Schoof_regularizing_velocity",
"m second-1");
1010 int snes_total_it = 0;
1011 int ksp_total_it = 0;
1014 m_config->get_number(
"stress_balance.blatter.relative_convergence");
1018 ierr = VecNorm(
m_x, NORM_INFINITY, &norm);
PISM_CHK(ierr,
"VecNorm");
1025 "Blatter solver: zero initial guess\n"
1026 " Disabling the Eisenstat-Walker method of adjusting solver tolerances\n");
1034 snes_total_it += info.
snes_it;
1035 ksp_total_it += info.
ksp_it;
1040 m_log->message(2,
"Blatter solver: %s\n", SNESConvergedReasons[info.
snes_reason]);
1044 m_log->message(2,
" Trying without the Eisenstat-Walker method of adjusting solver tolerances\n");
1047 if (not (info.
snes_reason == SNES_DIVERGED_LINE_SEARCH or
1062 snes_total_it += info.
snes_it;
1063 ksp_total_it += info.
ksp_it;
1068 m_log->message(2,
"Blatter solver: %s\n", SNESConvergedReasons[info.
snes_reason]);
1074 snes_total_it += info.
snes_it;
1075 ksp_total_it += info.
ksp_it;
1079 m_log->message(2,
"Blatter solver: %s\n", SNESConvergedReasons[info.
snes_reason]);
1087 "Blatter solver: %s. Done.\n"
1088 " SNES: %d, KSP: %d\n",
1090 snes_total_it, ksp_total_it);
1093 " Level 0 KSP (last iteration): %d\n",
1112 int ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1118 for (
auto p :
m_grid->points()) {
1119 const int i = p.i(), j = p.j();
1124 for (
int k = 0;
k < Mz; ++
k) {
1125 u[
k] = x[j][i][
k].
u;
1126 v[
k] = x[j][i][
k].
v;
1130 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
1135 int ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1139 for (
auto p :
m_grid->points()) {
1140 const int i = p.i(), j = p.j();
1142 result(i, j) = x[j][i][0];
1145 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
1152 int ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1158 for (
auto p :
m_grid->points()) {
1159 const int i = p.i(), j = p.j();
1164 for (
int k = 0;
k < Mz; ++
k) {
1165 x[j][i][
k].
u = u[
k];
1166 x[j][i][
k].
v = v[
k];
1170 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
1174 PetscErrorCode ierr;
1177 ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1183 for (
auto p :
m_grid->points()) {
1184 const int i = p.i(), j = p.j();
1192 double dz = H / (Mz - 1);
1193 for (
int k = 0;
k < Mz - 1; ++
k) {
1194 V += x[j][i][
k] + x[j][i][
k + 1];
1196 V *= (0.5 * dz) / H;
1202 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
#define ICE_GOLDSBY_KOHLSTEDT
std::shared_ptr< const Grid > grid() 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
std::shared_ptr< const Logger > m_log
logger (for easy access)
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
High-level PISM I/O class.
array::Scalar1 sea_level_elevation
array::CellType2 cell_type
array::Scalar2 ice_thickness
array::Scalar2 bed_elevation
This class represents a 2D vector field (such as ice velocity) at a certain grid point.
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
double * get_column(int i, int j)
A virtual class collecting methods common to ice and bedrock 3D fields.
const std::vector< double > & levels() const
void set(double c)
Result: v[j] <- c for all j.
void update_ghosts()
Updates ghost points.
void local_to_global(unsigned int k, int &i, int &j) const
Convert a local degree of freedom index k to a global degree of freedom index (i,j).
void reset(int i, int j)
Initialize the Element to element (i, j) for the purposes of inserting into global residual and Jacob...
void nodal_values(const array::Scalar &x_global, int *result) const
Get nodal values of an integer mask.
GlobalIndex local_to_global(int i, int j, int k, unsigned int n) const
Q1 element with sides parallel to X and Y axes.
unsigned int n_pts() const
Number of quadrature points.
The 1-point Gaussian quadrature on the square [-1,1]*[-1,1].
void remove(const std::string &name)
std::shared_ptr< FlowLaw > create(const std::string &type_name, double exponent)
void get_basal_velocity(array::Vector &result)
virtual Vector2d u_bc(double x, double y, double z) const
void compute_node_type(double min_thickness)
virtual bool marine_boundary(int face, const int *node_type, const double *ice_bottom, const double *sea_level)
fem::Q1Element3Face m_face4
static bool partially_submerged_face(int face, const double *z, const double *sea_level)
virtual void init_2d_parameters(const Inputs &inputs)
std::shared_ptr< array::Array3D > m_u_sigma
std::shared_ptr< array::Array3D > velocity_v_sigma() const
SolutionInfo parameter_continuation()
void update(const Inputs &inputs, bool)
PetscErrorCode setup(DM pism_da, grid::Periodicity p, int Mz, int coarsening_factor, const std::string &prefix)
void init_ice_hardness(const Inputs &inputs, const petsc::DM &da)
static PetscErrorCode jacobian_callback(DMDALocalInfo *info, const Vector2d ***x, Mat A, Mat J, Blatter *solver)
static PetscErrorCode function_callback(DMDALocalInfo *info, const Vector2d ***x, Vector2d ***f, Blatter *solver)
fem::Q1Element3Face m_face100
void compute_averaged_velocity(array::Vector &result)
void write_state_impl(const OutputFile &output) const
The default (empty implementation).
std::shared_ptr< array::Array3D > velocity_u_sigma() const
Blatter(std::shared_ptr< const Grid > grid, int Mz, int coarsening_factor)
static bool exterior_element(const int *node_type)
static bool grounding_line(const double *F)
array::Array2D< Parameters > m_parameters
virtual bool dirichlet_node(const DMDALocalInfo &info, const fem::Element3::GlobalIndex &I)
std::shared_ptr< array::Array3D > m_v_sigma
virtual void nodal_parameter_values(const fem::Q1Element3 &element, Parameters **P, int i, int j, int *node_type, double *bottom, double *thickness, double *surface, double *sea_level) const
virtual std::set< VariableMetadata > state_impl() const
void set_initial_guess(const array::Array3D &u_sigma, const array::Array3D &v_sigma)
void compute_basal_frictional_heating(const array::Vector &velocity, const array::Scalar &tauc, const array::CellType &mask, array::Scalar &result) const
Compute the basal frictional heating.
array::Scalar m_basal_frictional_heating
std::shared_ptr< rheology::FlowLaw > m_flow_law
std::shared_ptr< EnthalpyConverter > m_EC
array::Vector2 m_velocity
Shallow stress balance (such as the SSA).
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
std::set< VariableMetadata > metadata(std::initializer_list< const Array * > vecs)
const int n_chi
Number of shape functions on a Q1 element.
const unsigned int incident_nodes[n_faces][4]
@ PISM_READONLY
open an existing file for reading only
static void disable_ew(::SNES snes, double rtol)
static void enable_ew(::SNES snes)
static PetscErrorCode blatter_restriction_hook(DM fine, Mat mrestrict, Vec rscale, Mat inject, DM coarse, void *ctx)
static PetscErrorCode blatter_coarsening_hook(DM dm_fine, DM dm_coarse, void *ctx)
double grid_z(double b, double H, int Mz, int k)
PetscErrorCode setup_level(DM dm, int mg_levels)
static double F(double SL, double B, double H, double alpha)
DMDALocalInfo grid_transpose(const DMDALocalInfo &input)
void GlobalMax(MPI_Comm comm, double *local, double *result, int count)
std::string printf(const char *format,...)
PetscErrorCode restrict_data(DM fine, DM coarse, const char *dm_name)
Restrict model parameters from the fine grid onto the coarse grid.
void GlobalMin(MPI_Comm comm, double *local, double *result, int count)
InputOptions process_input_options(MPI_Comm com, std::shared_ptr< const Config > config)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
PetscErrorCode create_restriction(DM fine, DM coarse, const char *dm_name)
Create the restriction matrix.
PetscInt mg_coarse_ksp_it
SNESConvergedReason snes_reason
KSPConvergedReason ksp_reason