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"
42 namespace stressbalance {
62 int ierr = DMDAGetLocalInfo(
m_da, &info);
PISM_CHK(ierr,
"DMDAGetLocalInfo");
74 for (
int j = info.gys; j < info.gys + info.gym - 1; j++) {
75 for (
int i = info.gxs; i < info.gxs + info.gxm - 1; i++) {
84 if (p[
k].thickness < min_thickness) {
93 node_type(ii, jj) +=
static_cast<double>(interior);
102 for (
int j = info.ys; j < info.ys + info.ym; j++) {
103 for (
int i = info.xs; i < info.xs + info.xm; i++) {
105 switch ((
int)node_type(i, j)) {
149 for (
int n = 0;
n < N; ++
n) {
175 for (
int n = 0;
n < N; ++
n) {
202 for (
int n = 0;
n < N; ++
n) {
204 if (z[
k] > sea_level[
k]) {
211 return above and below;
226 const int *node_type,
227 const double *ice_bottom,
228 const double *sea_level) {
235 for (
int n = 0;
n < N; ++
n) {
243 for (
int n = 0;
n < N; ++
n) {
244 if (ice_bottom[nodes[
n]] < sea_level[nodes[
n]]) {
260 m_parameters(grid,
"bp_input_parameters", array::
WITH_GHOSTS),
261 m_face4(grid->dx(), grid->dy(), fem::Q1Quadrature4()),
262 m_face100(grid->dx(), grid->dy(), fem::Q1QuadratureN(10))
268 auto pism_da =
grid->get_dm(1, 0);
270 int ierr =
setup(*pism_da,
grid->periodicity(), Mz, coarsening_factor,
"bp_");
273 "Failed to allocate a Blatter solver instance");
277 std::vector<double> sigma(Mz);
278 double dz = 1.0 / (Mz - 1.0);
279 for (
int i = 0; i < Mz; ++i) {
286 .long_name(
"u velocity component on the sigma grid")
291 .long_name(
"v velocity component on the sigma grid")
294 std::map<std::string,std::string> z_attrs =
296 {
"long_name",
"scaled Z-coordinate in the ice (z_base=0, z_surface=1)"},
300 m_u_sigma->metadata(0).z().set_name(
"z_sigma");
301 m_v_sigma->metadata(0).z().set_name(
"z_sigma");
303 for (
const auto &z_attr : z_attrs) {
304 m_u_sigma->metadata(0).z().set_string(z_attr.first, z_attr.second);
305 m_v_sigma->metadata(0).z().set_string(z_attr.first, z_attr.second);
316 double g =
m_config->get_number(
"constants.standard_gravity");
325 double E =
m_config->get_number(
"stress_balance.blatter.enhancement_factor");
328 double softness =
m_config->get_number(
"flow_law.isothermal_Glen.ice_softness"),
345 Mat mrestrict, Vec rscale, Mat inject,
346 DM coarse,
void *ctx) {
353 PetscErrorCode ierr =
restrict_data(fine, coarse,
"3D_DM"); CHKERRQ(ierr);
364 ierr = DMGetOptionsPrefix(dm_fine, &prefix); CHKERRQ(ierr);
369 ierr =
setup_level(dm_coarse, mg_levels); CHKERRQ(ierr);
383 int coarsening_factor,
384 const std::string &prefix) {
386 PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pism_da, &comm); CHKERRQ(ierr);
390 auto option =
pism::printf(
"-%spc_mg_levels", prefix.c_str());
398 int c = coarsening_factor;
403 if (((mz - 1) / c) * c != mz - 1) {
404 int N = std::pow(c, (
int)mg_levels - 1);
405 auto message =
pism::printf(
"Blatter stress balance solver: settings\n"
406 "stress_balance.blatter.Mz = %d,\n"
407 "stress_balance.blatter.coarsening_factor = %d,\n"
408 "and '%s %d' are not compatible.\n"
409 "To use N = %d multigrid levels with the coarsening factor C = %d\n"
410 "stress_balance.blatter.Mz has to be equal to A * C^(N - 1) + 1\n"
411 "for some positive integer A, e.g. %d, %d, %d, ...",
412 Mz, c, option.c_str(), mg_levels, mg_levels, c,
413 N + 1, 2*N + 1, 3*N + 1);
416 mz = (mz - 1) / c + 1;
429 ierr = DMDAGetInfo(pism_da,
444 ierr = DMDAGetOwnershipRanges(pism_da, &lx, &ly, NULL); CHKERRQ(ierr);
452 bx = (periodicity &
grid::X_PERIODIC) != 0 ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE,
453 by = (periodicity &
grid::Y_PERIODIC) != 0 ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE,
454 bz = DM_BOUNDARY_NONE;
456 ierr = DMDACreate3d(comm,
466 ierr = DMSetOptionsPrefix(
m_da, prefix.c_str()); CHKERRQ(ierr);
469 ierr = DMDASetRefinementFactor(
m_da, coarsening_factor, 1, 1); CHKERRQ(ierr);
471 ierr = DMSetFromOptions(
m_da); CHKERRQ(ierr);
473 ierr = DMSetUp(
m_da); CHKERRQ(ierr);
485 ierr = DMCreateGlobalVector(
m_da,
m_x.
rawptr()); CHKERRQ(ierr);
487 ierr = VecSetOptionsPrefix(
m_x, prefix.c_str()); CHKERRQ(ierr);
489 ierr = VecSetFromOptions(
m_x); CHKERRQ(ierr);
496 ierr = SNESCreate(comm,
m_snes.
rawptr()); CHKERRQ(ierr);
498 ierr = SNESSetOptionsPrefix(
m_snes, prefix.c_str()); CHKERRQ(ierr);
500 ierr = SNESSetDM(
m_snes,
m_da); CHKERRQ(ierr);
502 ierr = DMDASNESSetFunctionLocal(
m_da, INSERT_VALUES,
503 #
if PETSC_VERSION_LT(3,21,0)
508 this); CHKERRQ(ierr);
510 ierr = DMDASNESSetJacobianLocal(
m_da,
511 #
if PETSC_VERSION_LT(3,21,0)
516 this); CHKERRQ(ierr);
518 ierr = SNESSetFromOptions(
m_snes); CHKERRQ(ierr);
521 PetscBool ksp_use_ew = PETSC_FALSE;
522 ierr = SNESKSPGetUseEW(
m_snes, &ksp_use_ew); CHKERRQ(ierr);
535 ice_density =
m_config->get_number(
"constants.ice.density"),
536 water_density =
m_config->get_number(
"constants.sea_water.density"),
537 alpha = ice_density / water_density;
548 for (
auto p =
m_grid->points(); p; p.next()) {
549 const int i = p.i(), j = p.j();
552 b_grounded = b(i, j),
553 b_floating = sea_level(i, j) - alpha * H(i, j),
554 s_grounded = b(i, j) + H(i, j),
555 s_floating = sea_level(i, j) + (1.0 - alpha) * H(i, j);
562 m_parameters(i, j).floatation = s_floating - s_grounded;
569 double H_min =
m_config->get_number(
"stress_balance.ice_free_thickness_standard");
581 const auto *enthalpy = inputs.
enthalpy;
583 const auto &zlevels = enthalpy->
levels();
584 auto Mz = zlevels.size();
590 int ierr = DMDAGetLocalInfo(da, &info);
PISM_CHK(ierr,
"DMDAGetLocalInfo");
600 for (
auto p =
m_grid->points(); p; p.next()) {
601 const int i = p.i(), j = p.j();
603 double H = ice_thickness(i, j);
605 const double *E = enthalpy->get_column(i, j);
607 for (
int k = 0;
k < Mz_sigma; ++
k) {
609 z =
grid_z(0.0, H, Mz_sigma,
k),
611 pressure =
m_EC->pressure(depth),
614 auto k0 =
m_grid->kBelowHeight(z);
617 double lambda = (z - zlevels[k0]) / (zlevels[k0 + 1] - zlevels[k0]);
619 E_local = (1.0 - lambda) * E[k0] + lambda * E[k0 + 1];
624 hardness[j][i][
k] =
m_flow_law->hardness(E_local, pressure);
640 double *bottom_elevation,
641 double *ice_thickness,
642 double *surface_elevation,
643 double *sea_level)
const {
648 auto p = P[
I.j][
I.i];
650 node_type[
n] =
static_cast<int>(p.node_type);
651 bottom_elevation[
n] = p.
bed;
652 ice_thickness[
n] = p.thickness;
654 if (surface_elevation !=
nullptr) {
655 surface_elevation[
n] = p.bed + p.thickness;
658 if (sea_level !=
nullptr) {
659 sea_level[
n] = p.sea_level;
665 m_log->message(2,
"* Initializing the Blatter stress balance...\n");
673 unsigned int start = input_file.
nrecords() - 1;
675 if (u_sigma_found and v_sigma_found) {
676 m_log->message(3,
"Reading uvel_sigma and vvel_sigma...\n");
684 "uvel_sigma and vvel_sigma not found");
687 int ierr = VecSet(
m_x, 0.0);
PISM_CHK(ierr,
"VecSet");
704 int ierr = DMDAGetLocalInfo(
m_da, &info);
PISM_CHK(ierr,
"DMDAGetLocalInfo");
711 double R_min = 1e16, R_max = 0.0, R_avg = 0.0;
713 double n_cells = 0.0;
716 for (
int j = info.ys; j < info.ys + info.ym - 1; j++) {
717 for (
int i = info.xs; i < info.xs + info.xm - 1; i++) {
724 for (
int k = 0;
k < 4; ++
k) {
735 for (
int k = 0;
k < 4; ++
k) {
736 dz_max =
std::max(P[
k].thickness / (info.mz - 1), dz_max);
738 double R = dz_max / dxy;
754 "Blatter solver: %d * (%d - 1) = %d active elements\n",
755 (
int)n_cells, (
int)info.mz, (
int)(n_cells * (info.mz - 1)));
759 " Vertical spacing (m): min = %3.3f, max = %3.3f, avg = %3.3f\n",
760 R_min * dxy, R_max * dxy, R_avg * dxy);
762 " Aspect ratios: min = %3.3f, max = %3.3f, avg = %3.3f, max/min = %3.3f\n",
763 R_min, R_max, R_avg, R_max / R_min);
778 PISM_CHK(ierr,
"SNESGetConvergedReason");
781 PISM_CHK(ierr,
"SNESGetIterationNumber");
783 ierr = SNESGetLinearSolveIterations(
m_snes, &result.
ksp_it);
784 PISM_CHK(ierr,
"SNESGetLinearSolveIterations");
787 ierr = SNESGetKSP(
m_snes, &ksp);
790 ierr = KSPGetConvergedReason(ksp, &result.
ksp_reason);
791 PISM_CHK(ierr,
"KSPGetConvergedReason");
794 ierr = KSPGetPC(ksp, &pc);
798 ierr = PCGetType(pc, &pc_type);
801 if (std::string(pc_type) == PCMG) {
803 ierr = PCMGGetCoarseSolve(pc, &coarse_ksp);
804 PISM_CHK(ierr,
"PCMGGetCoarseSolve");
807 PISM_CHK(ierr,
"KSPGetIterationNumber");
820 int snes_total_it = 0;
821 int ksp_total_it = 0;
827 schoofLen =
m_config->get_number(
"flow_law.Schoof_regularizing_length",
"m"),
828 schoofVel =
m_config->get_number(
"flow_law.Schoof_regularizing_velocity",
"m second-1"),
830 eps = pow(schoofVel / schoofLen, 2.0),
832 gamma = std::floor(std::log10(eps)),
847 PetscInt snes_max_it = 0;
848 ierr = SNESGetTolerances(
m_snes, NULL, NULL, NULL, &snes_max_it, NULL);
849 PISM_CHK(ierr,
"SNESGetTolerances");
851 double lambda = lambda_min;
852 double delta = delta0;
859 "Blatter solver: Starting parameter continuation with lambda = %f\n",
862 for (
int N = 0; N < Nc; ++N) {
866 m_log->message(2,
"Blatter solver: step %d with lambda = %f, eps = %e\n",
873 ksp_total_it += info.
ksp_it;
876 m_log->message(2,
"Blatter solver continuation step #%d: %s\n"
877 " lambda = %f, eps = %e\n"
878 " SNES: %d, KSP: %d\n",
883 " Coarse MG level KSP (last iteration): %d\n",
893 info.
ksp_it = ksp_total_it;
903 double F = (snes_max_it - info.
snes_it) / (
double)snes_max_it;
904 delta *= 1.0 + A *
F *
F;
910 if (lambda + delta > lambda_max) {
911 delta = lambda_max - lambda;
914 m_log->message(2,
" Advancing lambda from %f to %f (delta = %f)\n",
915 lambda, lambda + delta, delta);
918 }
else if (info.
snes_reason == SNES_DIVERGED_LINE_SEARCH or
928 if (lambda < lambda_min) {
929 m_log->message(2,
"Blatter solver: Parameter continuation failed at step 1\n");
933 if (std::fabs(delta - delta_min) < 1e-6) {
934 m_log->message(2,
"Blatter solver: cannot reduce the continuation step\n");
941 delta =
pism::clip(delta, delta_min, delta_max);
947 m_log->message(2,
" Back-tracking to lambda = %f using delta = %f\n",
954 m_log->message(2,
"Blatter solver failed after %d parameter continuation steps\n", Nc);
965 ierr = SNESKSPSetUseEW(snes, PETSC_FALSE);
969 ierr = SNESGetKSP(snes, &ksp);
972 ierr = KSPSetTolerances(ksp, rtol, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT);
979 ierr = SNESKSPSetUseEW(snes, PETSC_TRUE);
PISM_CHK(ierr,
"SNESKSPSetUseEW");
988 schoofLen =
m_config->get_number(
"flow_law.Schoof_regularizing_length",
"m"),
989 schoofVel =
m_config->get_number(
"flow_law.Schoof_regularizing_velocity",
"m second-1");
1004 int snes_total_it = 0;
1005 int ksp_total_it = 0;
1007 double ksp_rtol = 1e-5;
1011 ierr = VecNorm(
m_x, NORM_INFINITY, &norm);
PISM_CHK(ierr,
"VecNorm");
1018 "Blatter solver: zero initial guess\n"
1019 " Disabling the Eisenstat-Walker method of adjusting solver tolerances\n");
1027 snes_total_it += info.
snes_it;
1028 ksp_total_it += info.
ksp_it;
1033 m_log->message(2,
"Blatter solver: %s\n", SNESConvergedReasons[info.
snes_reason]);
1037 m_log->message(2,
" Trying without the Eisenstat-Walker method of adjusting solver tolerances\n");
1040 if (not (info.
snes_reason == SNES_DIVERGED_LINE_SEARCH or
1055 snes_total_it += info.
snes_it;
1056 ksp_total_it += info.
ksp_it;
1061 m_log->message(2,
"Blatter solver: %s\n", SNESConvergedReasons[info.
snes_reason]);
1067 snes_total_it += info.
snes_it;
1068 ksp_total_it += info.
ksp_it;
1072 m_log->message(2,
"Blatter solver: %s\n", SNESConvergedReasons[info.
snes_reason]);
1080 "Blatter solver: %s. Done.\n"
1081 " SNES: %d, KSP: %d\n",
1083 snes_total_it, ksp_total_it);
1086 " Level 0 KSP (last iteration): %d\n",
1105 int ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1111 for (
auto p =
m_grid->points(); p; p.next()) {
1112 const int i = p.i(), j = p.j();
1117 for (
int k = 0;
k < Mz; ++
k) {
1118 u[
k] = x[j][i][
k].
u;
1119 v[
k] = x[j][i][
k].
v;
1123 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
1128 int ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1132 for (
auto p =
m_grid->points(); p; p.next()) {
1133 const int i = p.i(), j = p.j();
1135 result(i, j) = x[j][i][0];
1138 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
1145 int ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1151 for (
auto p =
m_grid->points(); p; p.next()) {
1152 const int i = p.i(), j = p.j();
1157 for (
int k = 0;
k < Mz; ++
k) {
1158 x[j][i][
k].
u = u[
k];
1159 x[j][i][
k].
v = v[
k];
1163 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
1167 PetscErrorCode ierr;
1170 ierr = DMDAVecGetArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecGetArray");
1176 for (
auto p =
m_grid->points(); p; p.next()) {
1177 const int i = p.i(), j = p.j();
1185 double dz = H / (Mz - 1);
1186 for (
int k = 0;
k < Mz - 1; ++
k) {
1187 V += x[j][i][
k] + x[j][i][
k + 1];
1189 V *= (0.5 * dz) / H;
1195 ierr = DMDAVecRestoreArray(
m_da,
m_x, &x);
PISM_CHK(ierr,
"DMDAVecRestoreArray");
#define ICE_GOLDSBY_KOHLSTEDT
std::shared_ptr< const Grid > grid() 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
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
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.
int n_pts() const
Number of quadrature points.
The 1-point Gaussian quadrature on the square [-1,1]*[-1,1].
std::shared_ptr< FlowLaw > create()
void remove(const std::string &name)
void define_model_state_impl(const File &output) const
The default (empty implementation).
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)
void write_model_state_impl(const File &output) const
The default (empty implementation).
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)
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
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
EnthalpyConverter::Ptr m_EC
array::Vector2 m_velocity
Shallow stress balance (such as the SSA).
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
double max(const array::Scalar &input)
Finds maximum over all the values in an array::Scalar object. Ignores ghosts.
double min(const array::Scalar &input)
Finds minimum over all the values in an array::Scalar object. Ignores ghosts.
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
bool grounded(int M)
Grounded cell (grounded ice or ice-free).
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)
InputOptions process_input_options(MPI_Comm com, Config::ConstPtr config)
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)
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