19 #include "pism/util/Grid.hh"
20 #include "pism/stressbalance/ssa/SSAFEM.hh"
21 #include "pism/util/fem/FEM.hh"
22 #include "pism/util/Mask.hh"
23 #include "pism/basalstrength/basal_resistance.hh"
24 #include "pism/rheology/FlowLaw.hh"
25 #include "pism/util/pism_options.hh"
26 #include "pism/util/error_handling.hh"
27 #include "pism/util/Vars.hh"
28 #include "pism/stressbalance/StressBalance.hh"
29 #include "pism/geometry/Geometry.hh"
31 #include "pism/util/node_types.hh"
32 #include "pism/util/pism_utilities.hh"
33 #include "pism/util/interpolation.hh"
34 #include "pism/util/petscwrappers/DM.hh"
35 #include "pism/util/petscwrappers/Vec.hh"
36 #include "pism/util/petscwrappers/Viewer.hh"
39 namespace stressbalance {
48 m_bc_mask(grid,
"bc_mask"),
49 m_bc_values(grid,
"_bc"),
51 m_coefficients(grid,
"ssa_coefficients", array::
WITH_GHOSTS, 1),
52 m_node_type(m_grid,
"node_type"),
53 m_boundary_integral(m_grid,
"boundary_integral"),
54 m_element_index(*grid),
55 m_q1_element(*grid, fem::Q1Quadrature4()) {
59 const double ice_density =
m_config->get_number(
"constants.ice.density");
60 m_alpha = 1 - ice_density /
m_config->get_number(
"constants.sea_water.density");
61 m_rho_g = ice_density *
m_config->get_number(
"constants.standard_gravity");
78 ierr = DMDASNESSetFunctionLocal(*
m_da, INSERT_VALUES,
79 #
if PETSC_VERSION_LT(3,21,0)
85 PISM_CHK(ierr,
"DMDASNESSetFunctionLocal");
87 ierr = DMDASNESSetJacobianLocal(*
m_da,
88 #
if PETSC_VERSION_LT(3,21,0)
94 PISM_CHK(ierr,
"DMDASNESSetJacobianLocal");
96 ierr = DMSetMatType(*
m_da,
"baij");
100 PISM_CHK(ierr,
"DMSetApplicationContext");
106 int snes_max_it = 200;
107 ierr = SNESSetTolerances(
m_snes, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, snes_max_it,
109 PISM_CHK(ierr,
"SNESSetTolerances");
111 ierr = SNESSetFromOptions(
m_snes);
112 PISM_CHK(ierr,
"SNESSetFromOptions");
115 "node types: interior, boundary, exterior");
120 "residual contribution from lateral boundaries");
133 if (
m_grid->variables().is_available(
"ssa_driving_stress_x") and
134 m_grid->variables().is_available(
"ssa_driving_stress_y")) {
139 m_log->message(2,
" [using the SNES-based finite element method implementation]\n");
145 "Enforce Dirichlet conditions with this additional scaling",
174 if (reason->failed()) {
176 "SSAFEM solve failed to converge (SNES reason %s)",
177 reason->description().c_str());
180 if (
m_log->get_threshold() > 2) {
181 m_stdout_ssa +=
"SSAFEM converged (SNES reason " + reason->description() +
")";
203 ierr = PetscViewerASCIIOpen(
m_grid->com, filename->c_str(), viewer.
rawptr());
204 PISM_CHK(ierr,
"PetscViewerASCIIOpen");
206 ierr = PetscViewerASCIIPrintf(viewer,
"SNES before SSASolve_FE\n");
207 PISM_CHK(ierr,
"PetscViewerASCIIPrintf");
209 ierr = SNESView(
m_snes, viewer);
212 ierr = PetscViewerASCIIPrintf(viewer,
"solution vector before SSASolve_FE\n");
213 PISM_CHK(ierr,
"PetscViewerASCIIPrintf");
220 if (
m_log->get_threshold() >= 2) {
229 SNESConvergedReason snes_reason;
230 ierr = SNESGetConvergedReason(
m_snes, &snes_reason);
PISM_CHK(ierr,
"SNESGetConvergedReason");
233 if (not reason->failed()) {
239 bool view_solution =
options::Bool(
"-ssa_view_solution",
"view solution of the SSA system");
242 ierr = PetscViewerASCIIOpen(
m_grid->com, filename->c_str(), viewer.
rawptr());
243 PISM_CHK(ierr,
"PetscViewerASCIIOpen");
245 ierr = PetscViewerASCIIPrintf(viewer,
"solution vector after SSASolve\n");
246 PISM_CHK(ierr,
"PetscViewerASCIIPrintf");
280 const std::vector<double> &z =
m_grid->z();
290 if (use_explicit_driving_stress) {
296 for (
auto p =
m_grid->points(); p; p.next()) {
297 const int i = p.i(), j = p.j();
302 if (use_explicit_driving_stress) {
303 tau_d.
u = (*m_driving_stress_x)(i, j);
304 tau_d.
v = (*m_driving_stress_y)(i, j);
312 m_grid->kBelowHeight(thickness),
332 const bool use_cfbc =
m_config->get_flag(
"stress_balance.calving_front_stress_bc");
336 m_config->get_number(
"stress_balance.ice_free_thickness_standard"),
352 double *hardness)
const {
354 unsigned int n = E.
n_pts();
356 for (
unsigned int q = 0; q <
n; q++) {
365 for (
int k = 0;
k < E.
n_chi();
k++) {
375 mask[q] =
m_gc.
mask(sea_level, bed, thickness[q]);
384 const unsigned int n = E.
n_pts();
386 for (
unsigned int q = 0; q <
n; q++) {
389 for (
int k = 0;
k < E.
n_chi();
k++) {
444 const unsigned int n = E.
n_pts();
446 for (
unsigned int q = 0; q <
n; q++) {
458 for (
int k = 0;
k < E.
n_chi();
k++) {
472 const int M =
m_gc.
mask(sea_level, b, H);
480 result[q].
u = - pressure * h_x;
481 result[q].
v = - pressure * h_y;
512 double *nuH,
double *dnuH,
513 double *beta,
double *dbeta) {
515 if (thickness < strength_extension->get_min_thickness()) {
560 const unsigned int Nq = 2;
561 double chi_b[Nq][Nq];
566 for (
int i : {0, 1}) {
567 for (
int j : {0, 1}) {
577 P1Element2 p1_element[Nk] = {P1Element2(*
m_grid, Q_p1, 0),
578 P1Element2(*
m_grid, Q_p1, 1),
579 P1Element2(*
m_grid, Q_p1, 2),
580 P1Element2(*
m_grid, Q_p1, 3)};
585 use_cfbc =
m_config->get_flag(
"stress_balance.calving_front_stress_bc");
588 ice_density =
m_config->get_number(
"constants.ice.density"),
589 ocean_density =
m_config->get_number(
"constants.sea_water.density"),
590 standard_gravity =
m_config->get_number(
"constants.standard_gravity");
615 for (
int j = ys; j < ys + ym; j++) {
616 for (
int i = xs; i < xs + xm; i++) {
634 E = &p1_element[type];
644 std::vector<Vector2d>
I(Nk);
656 double psi[2] = {0.0, 0.0};
660 for (
unsigned int s = 0; s <
n_sides; ++s) {
676 for (
unsigned int q = 0; q < Nq; ++q) {
682 psi[0] = chi_b[0][q];
683 psi[1] = chi_b[1][q];
688 H = H_nodal[n0] * psi[0] + H_nodal[n1] * psi[1],
689 bed = b_nodal[n0] * psi[0] + b_nodal[n1] * psi[1],
690 sea_level = sl_nodal[n0] * psi[0] + sl_nodal[n1] * psi[1];
694 P_ice = 0.5 * ice_density * standard_gravity * H,
696 ice_density, ocean_density,
700 double dP = H * (P_ice - P_water);
712 I[n0] += W * (- psi[0] * dP) * E->
normal(s);
713 I[n1] += W * (- psi[1] * dP) * E->
normal(s);
742 const bool use_cfbc =
m_config->get_flag(
"stress_balance.calving_front_stress_bc");
749 P1Element2 p1_element[Nk] = {P1Element2(*
m_grid, Q_p1, 0),
750 P1Element2(*
m_grid, Q_p1, 1),
751 P1Element2(*
m_grid, Q_p1, 2),
752 P1Element2(*
m_grid, Q_p1, 3)};
759 for (
auto p =
m_grid->points(); p; p.next()) {
760 const int i = p.i(), j = p.j();
769 Vector2d U[Nq_max], U_x[Nq_max], U_y[Nq_max];
780 for (
int j = ys; j < ys + ym; j++) {
781 for (
int i = xs; i < xs + xm; i++) {
801 E = &p1_element[type];
812 const unsigned int Nq = E->
n_pts();
818 double thickness[Nq_max];
820 double hardness[Nq_max];
829 if (use_explicit_driving_stress) {
842 if (dirichlet_data) {
845 dirichlet_data.
enforce(*E, velocity_nodal);
857 for (
unsigned int k = 0;
k < Nk;
k++) {
862 for (
unsigned int q = 0; q < Nq; q++) {
866 double eta = 0.0, beta = 0.0;
868 U[q], U_x[q], U_y[q],
869 &eta, NULL, &beta, NULL);
872 const Vector2d tau_b = U[q] * (- beta);
877 u_y_plus_v_x = U_y[q].
u + U_x[q].
v;
880 for (
int k = 0;
k < E->
n_chi();
k++) {
883 residual[
k].
u += W * (eta * (psi.
dx * (4.0 * u_x + 2.0 * v_y) + psi.
dy * u_y_plus_v_x)
884 - psi.
val * (tau_b.
u + tau_d[q].
u));
885 residual[
k].
v += W * (eta * (psi.
dx * u_y_plus_v_x + psi.
dy * (2.0 * u_x + 4.0 * v_y))
886 - psi.
val * (tau_b.
v + tau_d[q].
v));
900 if (dirichlet_data) {
901 dirichlet_data.
fix_residual(velocity_global, residual_global);
916 Vector2d const *
const *
const residual_global) {
918 bool monitorFunction =
options::Bool(
"-ssa_monitor_function",
"monitor the SSA residual");
919 if (not monitorFunction) {
923 ierr = PetscPrintf(
m_grid->com,
924 "SSA Solution and Function values (pointwise residuals)\n");
929 for (
auto p =
m_grid->points(); p; p.next()) {
930 const int i = p.i(), j = p.j();
932 ierr = PetscSynchronizedPrintf(
m_grid->com,
933 "[%2d, %2d] u=(%12.10e, %12.10e) f=(%12.4e, %12.4e)\n",
935 velocity_global[j][i].
u, velocity_global[j][i].
v,
936 residual_global[j][i].
u, residual_global[j][i].
v);
937 PISM_CHK(ierr,
"PetscSynchronizedPrintf");
944 ierr = PetscSynchronizedFlush(
m_grid->com, NULL);
945 PISM_CHK(ierr,
"PetscSynchronizedFlush");
961 const bool use_cfbc =
m_config->get_flag(
"stress_balance.calving_front_stress_bc");
968 P1Element2 p1_element[Nk] = {P1Element2(*
m_grid, Q_p1, 0),
969 P1Element2(*
m_grid, Q_p1, 1),
970 P1Element2(*
m_grid, Q_p1, 2),
971 P1Element2(*
m_grid, Q_p1, 3)};
974 PetscErrorCode ierr = MatZeroEntries(Jac);
983 Vector2d U[Nq_max], U_x[Nq_max], U_y[Nq_max];
994 for (
int j = ys; j < ys + ym; j++) {
995 for (
int i = xs; i < xs + xm; i++) {
1015 E = &p1_element[type];
1031 double thickness[Nq_max];
1032 double tauc[Nq_max];
1033 double hardness[Nq_max];
1040 mask, thickness, tauc, hardness);
1050 if (dirichlet_data) {
1051 dirichlet_data.
enforce(*E, velocity_nodal);
1055 E->
evaluate(velocity_nodal, U, U_x, U_y);
1061 double K[2*Nk][2*Nk];
1063 ierr = PetscMemzero(
K,
sizeof(
K));
1066 for (
unsigned int q = 0; q < Nq; q++) {
1074 u_y_plus_v_x = U_y[q].
u + U_x[q].
v;
1076 double eta = 0.0, deta = 0.0, beta = 0.0, dbeta = 0.0;
1078 U[q], U_x[q], U_y[q],
1079 &eta, &deta, &beta, &dbeta);
1081 for (
unsigned int l = 0; l <
n_chi; l++) {
1088 gamma_u = (2.0 * u_x + v_y) *
phi.dx + 0.5 * u_y_plus_v_x *
phi.dy,
1089 gamma_v = 0.5 * u_y_plus_v_x *
phi.dx + (u_x + 2.0 * v_y) *
phi.dy;
1093 eta_u = deta * gamma_u,
1094 eta_v = deta * gamma_v;
1098 taub_xu = -dbeta * u * u *
phi.val - beta *
phi.val,
1099 taub_xv = -dbeta * u * v *
phi.val,
1100 taub_yu = -dbeta * v * u *
phi.val,
1101 taub_yv = -dbeta * v * v *
phi.val - beta *
phi.val;
1103 for (
unsigned int k = 0;
k <
n_chi;
k++) {
1110 ierr = PetscPrintf(PETSC_COMM_SELF,
"eta=0 i %d j %d q %d k %d\n", i, j, q,
k);
1115 K[
k*2 + 0][l*2 + 0] += W * (eta_u * (psi.
dx * (4 * u_x + 2 * v_y) + psi.
dy * u_y_plus_v_x)
1116 + eta * (4 * psi.
dx *
phi.dx + psi.
dy *
phi.dy) - psi.
val * taub_xu);
1118 K[
k*2 + 0][l*2 + 1] += W * (eta_v * (psi.
dx * (4 * u_x + 2 * v_y) + psi.
dy * u_y_plus_v_x)
1119 + eta * (2 * psi.
dx *
phi.dy + psi.
dy *
phi.dx) - psi.
val * taub_xv);
1121 K[
k*2 + 1][l*2 + 0] += W * (eta_u * (psi.
dx * u_y_plus_v_x + psi.
dy * (2 * u_x + 4 * v_y))
1122 + eta * (psi.
dx *
phi.dy + 2 * psi.
dy *
phi.dx) - psi.
val * taub_yu);
1124 K[
k*2 + 1][l*2 + 1] += W * (eta_v * (psi.
dx * u_y_plus_v_x + psi.
dy * (2 * u_x + 4 * v_y))
1125 + eta * (psi.
dx *
phi.dx + 4 * psi.
dy *
phi.dy) - psi.
val * taub_yv);
1144 if (dirichlet_data) {
1156 ierr = MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY);
1157 PISM_CHK(ierr,
"MatAssemblyBegin");
1159 ierr = MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY);
1162 ierr = MatSetOption(Jac, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE);
1165 ierr = MatSetOption(Jac, MAT_SYMMETRIC, PETSC_TRUE);
1172 PetscErrorCode ierr;
1173 bool mon_jac =
options::Bool(
"-ssa_monitor_jacobian",
"monitor the SSA Jacobian");
1182 ierr = SNESGetIterationNumber(
m_snes, &iter);
1183 PISM_CHK(ierr,
"SNESGetIterationNumber");
1185 auto file_name =
pism::printf(
"PISM_SSAFEM_J%d.m", (
int)iter);
1188 "writing Matlab-readable file for SSAFEM system A xsoln = rhs to file `%s' ...\n",
1193 ierr = PetscViewerSetType(viewer, PETSCVIEWERASCII);
1194 PISM_CHK(ierr,
"PetscViewerSetType");
1196 ierr = PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
1197 PISM_CHK(ierr,
"PetscViewerPushFormat");
1199 ierr = PetscViewerFileSetName(viewer, file_name.c_str());
1200 PISM_CHK(ierr,
"PetscViewerFileSetName");
1202 ierr = PetscObjectSetName((PetscObject) Jac,
"A");
1203 PISM_CHK(ierr,
"PetscObjectSetName");
1205 ierr = MatView(Jac, viewer);
1208 ierr = PetscViewerPopFormat(viewer);
1209 PISM_CHK(ierr,
"PetscViewerPopFormat");
1214 Vector2d const *
const *
const velocity,
1221 MPI_Comm com = MPI_COMM_SELF;
1222 PetscErrorCode ierr = PetscObjectGetComm((PetscObject)fe->
da, &com); CHKERRQ(ierr);
1224 SETERRQ(com, 1,
"A PISM callback failed");
1230 Vector2d const *
const *
const velocity,
1237 MPI_Comm com = MPI_COMM_SELF;
1238 PetscErrorCode ierr = PetscObjectGetComm((PetscObject)fe->
da, &com); CHKERRQ(ierr);
1240 SETERRQ(com, 1,
"A PISM callback failed");
const units::System::Ptr m_sys
unit system used by this component
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
int mask(double sea_level, double bed, double thickness) const
array::Scalar1 sea_level_elevation
array::Scalar2 ice_thickness
array::Scalar2 bed_elevation
virtual void drag_with_derivative(double tauc, double vx, double vy, double *drag, double *ddrag) const
Compute the drag coefficient and its derivative with respect to .
void failed()
Indicates a failure of a parallel section.
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
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.
void copy_from(const Array2D< T > &source)
void add(double alpha, const Array2D< T > &x)
double * get_column(int i, int j)
void set_interpolation_type(InterpolationType type)
void set(double c)
Result: v[j] <- c for all j.
void update_ghosts()
Updates ghost points.
SpatialVariableMetadata & metadata(unsigned int N=0)
Returns a reference to the SpatialVariableMetadata object containing metadata for the compoment N.
void fix_residual(Vector2d const *const *const x_global, Vector2d **r_global)
void enforce(const Element2 &element, Vector2d *x_e)
void fix_residual_homogeneous(Vector2d **r)
void constrain(Element2 &element)
Constrain element, i.e. ensure that quadratures do not contribute to Dirichlet nodes by marking corre...
void evaluate(const T *x, T *vals, T *dx, T *dy)
Given nodal values, compute the values and partial derivatives at the quadrature points.
void reset(int i, int j)
Initialize the Element to element (i, j) for the purposes of inserting into global residual and Jacob...
double side_length(unsigned int side) const
void add_contribution(const T *local, T **y_global) const
Add the values of element-local contributions y to the global vector y_global.
void nodal_values(const array::Scalar &x_global, int *result) const
Get nodal values of an integer mask.
unsigned int n_sides() const
Vector2d normal(unsigned int side) const
int xm
total number of elements to loop over in the x-direction.
int ym
total number of elements to loop over in the y-direction.
int ys
y-coordinate of the first element to loop over.
int xs
x-coordinate of the first element to loop over.
const Germ & chi(unsigned int q, unsigned int k) const
int n_pts() const
Number of quadrature points.
double weight(unsigned int q) const
Weight of the quadrature point q
The mapping from global to local degrees of freedom.
P1 element embedded in Q1Element2.
double weight(int k) const
QuadPoint point(int k) const
void compute_local_function(Vector2d const *const *const velocity, Vector2d **residual)
Implements the callback for computing the residual.
std::shared_ptr< TerminationReason > solve_with_reason(const Inputs &inputs)
void PointwiseNuHAndBeta(double thickness, double hardness, int mask, double tauc, const Vector2d &U, const Vector2d &U_x, const Vector2d &U_y, double *nuH, double *dnuH, double *beta, double *dbeta)
Compute the "(regularized effective viscosity) x (ice thickness)" and effective viscous bed strength ...
void quad_point_values(const fem::Element &Q, const Coefficients *x, int *mask, double *thickness, double *tauc, double *hardness) const
Compute quadrature point values of various coefficients given a quadrature Q and nodal values.
const array::Scalar * m_driving_stress_x
static PetscErrorCode function_callback(DMDALocalInfo *info, Vector2d const *const *const velocity, Vector2d **residual, CallbackData *fe)
SNES callbacks.
void cache_inputs(const Inputs &inputs)
Initialize stored data from the coefficients in the SSA. Called by SSAFEM::solve.
void compute_local_jacobian(Vector2d const *const *const velocity, Mat J)
Implements the callback for computing the Jacobian.
double m_beta_ice_free_bedrock
SSAFEM(std::shared_ptr< const Grid > g)
CallbackData m_callback_data
array::Vector1 m_boundary_integral
Boundary integral (CFBC contribution to the residual).
void monitor_jacobian(Mat Jac)
void explicit_driving_stress(const fem::Element &E, const Coefficients *x, Vector2d *driving_stress) const
static PetscErrorCode jacobian_callback(DMDALocalInfo *info, Vector2d const *const *const xg, Mat A, Mat J, CallbackData *fe)
virtual void solve(const Inputs &inputs)
fem::Q1Element2 m_q1_element
virtual void init_impl()
Initialize a generic regular-grid SSA solver.
std::shared_ptr< TerminationReason > solve_nocache()
void cache_residual_cfbc(const Inputs &inputs)
Compute and cache residual contributions from the integral over the lateral boundary.
fem::ElementIterator m_element_index
const array::Scalar * m_driving_stress_y
void monitor_function(Vector2d const *const *const velocity_global, Vector2d const *const *const residual_global)
array::Scalar1 m_node_type
Storage for node types (interior, boundary, exterior).
array::Vector1 m_bc_values
array::Array2D< Coefficients > m_coefficients
PISM's SSA solver: the finite element method implementation written by Jed and David.
double get_notional_strength() const
Returns strength = (viscosity times thickness).
array::Vector m_velocity_global
SSAStrengthExtension * strength_extension
std::shared_ptr< petsc::DM > m_da
const array::Vector & driving_stress() const
virtual void init_impl()
Initialize a generic regular-grid SSA solver.
const array::Vector1 & velocity() const
Get the thickness-advective 2D velocity.
std::shared_ptr< rheology::FlowLaw > m_flow_law
IceBasalResistancePlasticLaw * m_basal_sliding_law
array::Vector2 m_velocity
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
Germ chi(unsigned int k, const QuadPoint &pt)
Linear basis functions on the interval [-1, -1].
const unsigned int MAX_QUADRATURE_SIZE
ElementType element_type(int node_type[q1::n_chi])
static double K(double psi_x, double psi_y, double speed, double epsilon)
bool ice_free_land(int M)
bool grounded(int M)
Grounded cell (grounded ice or ice-free).
bool ocean(int M)
An ocean cell (floating ice or ice-free).
bool Bool(const std::string &option, const std::string &description)
double averaged_hardness(const FlowLaw &ice, double thickness, unsigned int kbelowH, const double *zlevels, const double *enthalpy)
Computes vertical average of B(E, p) ice hardness, namely .
SSA * SSAFEMFactory(std::shared_ptr< const Grid > g)
Factory function for constructing a new SSAFEM.
static double secondInvariant_2D(const Vector2d &U_x, const Vector2d &U_y)
void compute_node_types(const array::Scalar1 &ice_thickness, double thickness_threshold, array::Scalar &result)
double average_water_column_pressure(double ice_thickness, double bed, double floatation_level, double rho_ice, double rho_water, double g)
std::string printf(const char *format,...)
void handle_fatal_errors(MPI_Comm com)
double dy
Function derivative with respect to y.
double val
Function value.
double dx
Function deriviative with respect to x.
Struct for gathering the value and derivative of a function at a point.
Adaptor for gluing SNESDAFormFunction callbacks to an SSAFEM.
double thickness
ice thickness
double tauc
basal yield stress
double sea_level
sea level
Vector2d driving_stress
prescribed gravitational driving stress
double hardness
ice hardness