ISMIP-HOM

We begin with an example that does not require any external data; the “Ice Sheet Model Intercomparison Project for Higher-Order Models”.

Set up the model

First, import all the packages we will need:

import issm  as im
import numpy as np

First, create an empty model instance and name the simulation:

md = im.model()
md.miscellaneous.name = 'ISMIP_HOM_A'

Next, we make a simple two-dimensional box mesh with 49 cells in the \(x\) and \(y\) directions over a width of 8 km using squaremesh:

L  = 80000.0
n  = 15
md = im.squaremesh(md, L, L, n, n)

Let the entire domain be defined over grounded ice with setmask:

md = im.setmask(md, 'all', '')

The ISMIP-HOM experiment “A” geometry is created by directly editing the coordinates of the mesh2d instance created above:

# surface :
md.geometry.surface = - md.mesh.x * np.tan(0.5*np.pi/180.0)

# base of ice sheet with 'L' the size of the side of the square :
md.geometry.base = + md.geometry.surface - 1000.0 \
                   + 500.0 * np.sin(md.mesh.x*2*np.pi/L) \
                           * np.sin(md.mesh.y*2*np.pi/L)

# thickness is the difference between surface and base :
md.geometry.thickness = md.geometry.surface - md.geometry.base

We will also need to define the element-wise multiplicative identities:

v_ones = np.ones(md.mesh.numberofvertices)  # rank-zero tensor vertex
e_ones = np.ones(md.mesh.numberofelements)  # rank-zero tensor element

The material parameters may be changed to match those of the ISMIP HOM experiment by changing either the model’s constants or material properties matice:

md.materials.rho_ice    = 910.0              # ice density
md.constants.g          = 9.80665            # gravitational acc.
md.constants.yts        = 31556926.0         # seconds per year
n                       = 3.0                # Glen's flow exponent
spy                     = md.constants.yts   # s a^{-1}
A                       = 1e-16              # Pa^{-n} s^{-1}
B                       = (A / spy)**(-1/n)
md.materials.rheology_B = B * v_ones
md.materials.rheology_n = n * e_ones

While no-slip basal velocity boundary conditions are imposed, the friction coefficient must be defined:

md.friction.coefficient = 1.0 * v_ones
md.friction.p           = 1.0 * e_ones
md.friction.q           = 0.0 * e_ones

Next, configure the model for “ice-sheet” boundary conditions via SetIceSheetBC, extrude vertically 5 cells in the \(z\) direction with extrude(), and set the appropriate “flow equation” with setflowequation:

md = im.SetIceSheetBC(md)  # create placeholder arrays for indicies
md.extrude(6, 1.0)
md = im.setflowequation(md, mdl_odr, 'all')

Now that the 2D mesh has been converted to 3D, we have to redefine the element-wise multiplicitave identies:

v_ones = np.ones(md.mesh.numberofvertices)  # rank-zero tensor vertex
e_ones = np.ones(md.mesh.numberofelements)  # rank-zero tensor element

The no-slip basal-velocity boundary conditions are then set within the model property stressbalance:

md.stressbalance.spcvx = np.nan * v_ones
md.stressbalance.spcvy = np.nan * v_ones
md.stressbalance.spcvz = np.nan * v_ones

basal_v                         = md.mesh.vertexonbase
md.stressbalance.spcvx[basal_v] = 0.0
md.stressbalance.spcvy[basal_v] = 0.0
md.stressbalance.spcvz[basal_v] = 0.0

The periodic-velocity-lateral-boundary conditions specified by the ISMIP HOM experiment are defined by pairing lateral nodes as follows:

minX = np.where(md.mesh.x == 0)[0] + 1
maxX = np.where(md.mesh.x == L)[0] + 1

# for y, maxX and minX should be excluded :
minY = np.where(np.logical_and(md.mesh.y == 0,
                               md.mesh.x != L,
                               md.mesh.x != 0))[0] + 1
maxY = np.where(np.logical_and(md.mesh.y == L,
                               md.mesh.x != L,
                               md.mesh.x != 0))[0] + 1

# set the nodes that should be paired together :
md.stressbalance.vertex_pairing = np.array([np.append(minX, minY),
                                            np.append(maxX, maxY)]).T

Solve the momentum balance

Now, set up the computing environment variables using the generic class, enable verbose solver output with verbose, and finally solve the system with the solve class:

md.cluster = im.generic('name', im.gethostname(), 'np', 1)
md.verbose = im.verbose('convergence', True)
md         = im.solve(md, 'Stressbalance')

Plot the results

You can plot the resulting variables on the surface or the be easily like so:

p   = md.results.StressbalanceSolution.Pressure[md.mesh.vertexonbase]
u_x = md.results.StressbalanceSolution.Vx[md.mesh.vertexonsurface]
u_y = md.results.StressbalanceSolution.Vy[md.mesh.vertexonsurface]
u_z = md.results.StressbalanceSolution.Vz[md.mesh.vertexonsurface]
u   = np.array([u_x.flatten(), u_y.flatten(), u_z.flatten()])

You can then save the data if you like using NumPy:

np.savetxt(out_dir + 'x.txt',   md.mesh.x2d)
np.savetxt(out_dir + 'y.txt',   md.mesh.y2d)
np.savetxt(out_dir + 'u_x.txt', u[0])
np.savetxt(out_dir + 'u_y.txt', u[1])
np.savetxt(out_dir + 'u_z.txt', u[2])
np.savetxt(out_dir + 'p.txt',   p)

You can utilize the plotting capabilities of the fenics_viz package:

from fenics_viz import *


U_mag  = np.sqrt(u[0]**2 + u[1]**2 + u[2]**2 + 1e-16)
U_lvls = np.array([U_mag.min(), 10, 20, 30, 40, 50, 60, 70, 80, U_mag.max()])

tp_kwargs     = {'linestyle'      : '-',
                 'lw'             : 1.0,
                 'color'          : 'k',
                 'alpha'          : 0.2}

quiver_kwargs = {'pivot'          : 'middle',
                 'color'          : '0.5',
                 'scale'          : None,
                 'alpha'          : 1.0,
                 'width'          : 0.005,
                 'headwidth'      : 3.0,
                 'headlength'     : 3.0,
                 'headaxislength' : 3.0}

plot_variable(u                   = u,
              name                = 'U',
              direc               = plt_dir,
              coords              = (md.mesh.x2d, md.mesh.y2d),
              cells               = md.mesh.elements2d - 1,
              figsize             = (7,7),
              cmap                = 'viridis',
              scale               = 'lin',
              numLvls             = 10,
              levels              = U_lvls,
              levels_2            = None,
              umin                = None,
              umax                = None,
              plot_tp             = True,
              tp_kwargs           = tp_kwargs,
              show                = False,
              hide_x_tick_labels  = False,
              hide_y_tick_labels  = False,
              xlabel              = r'$x$',
              ylabel              = r'$y$',
              equal_axes          = True,
              title               = r'$\underline{u} |_S^{\mathrm{ISSM}}$',
              hide_axis           = False,
              colorbar_loc        = 'right',
              contour_type        = 'filled',
              extend              = 'neither',
              ext                 = '.png',
              normalize_vec       = True,
              plot_quiver         = True,
              quiver_kwargs       = quiver_kwargs,
              res                 = 150,
              cb                  = True,
              cb_format           = '%g')

This will produce a plot of the velocity at the upper surface like this :

_images/U.png