Skip to content

Commit

Permalink
Improvmeents to gibbous demo
Browse files Browse the repository at this point in the history
Benchmark script, plot for number of Newton iterations
  • Loading branch information
danshapero committed Jun 25, 2024
1 parent 5213e28 commit f5f46b8
Show file tree
Hide file tree
Showing 8 changed files with 443 additions and 117 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dual-problems.pdf: dual-problems.tex dual-problems.bib
cd demos/singularity && make primal.pdf dual.pdf && cd ../../
cd demos/convergence-tests && make results.pdf && cd ../../
cd demos/slab && make tables/table_alpha0.50.txt && cd ../../
cd demos/gibbous-ice-shelf && make steady-state.pdf calved-thickness.pdf volumes.pdf && cd ../../
cd demos/gibbous-ice-shelf && make gibbous.pdf volumes.pdf && cd ../../
pdflatex $<
bibtex $(basename $<)
pdflatex $<
Expand Down
5 changes: 4 additions & 1 deletion demos/gibbous-ice-shelf/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
steady-state.pdf calved-thickness.pdf volumes.pdf &: calving.h5 make_plots.py
gibbous.pdf volumes.pdf residuals.pdf &: calving.h5 residuals.json make_plots.py
python make_plots.py --calving-freq 24.0

calving.h5: steady-state-fine.h5 gibbous.py
Expand All @@ -7,5 +7,8 @@ calving.h5: steady-state-fine.h5 gibbous.py
steady-state-fine.h5: steady-state-coarse.h5 gibbous.py
python gibbous.py --resolution 2e3 --final-time 400.0 --num-steps 400 --input $< --output $@

residuals.json: steady-state-coarse.h5 primal_dual_calving.py
python primal_dual_calving.py

steady-state-coarse.h5: gibbous.py
python gibbous.py --resolution 5e3 --final-time 400.0 --num-steps 200 --output $@
157 changes: 157 additions & 0 deletions demos/gibbous-ice-shelf/benchmark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import argparse
import numpy as np
import tqdm
import firedrake
from firedrake import (
derivative,
NonlinearVariationalProblem,
NonlinearVariationalSolver,
)
import icepack
import irksome
from icepack2 import model
from icepack2.constants import glen_flow_law as n
import gibbous_inputs

parser = argparse.ArgumentParser()
parser.add_argument("--resolution", type=float, default=5e3)
parser.add_argument("--final-time", type=float, default=400.0)
parser.add_argument("--num-steps", type=int, default=200)
parser.add_argument("--form", choices=["primal", "dual"])
args = parser.parse_args()

# Generate and load the mesh
R = 200e3
δx = args.resolution

mesh = gibbous_inputs.make_mesh(R, δx)

print(mesh.num_vertices(), mesh.num_cells())

h_expr, u_expr = gibbous_inputs.make_initial_data(mesh, R)

# Create some function spaces and some fields
cg = firedrake.FiniteElement("CG", "triangle", 1)
dg0 = firedrake.FiniteElement("DG", "triangle", 0)
dg1 = firedrake.FiniteElement("DG", "triangle", 1)
Q = firedrake.FunctionSpace(mesh, dg1)
V = firedrake.VectorFunctionSpace(mesh, cg)

h0 = firedrake.Function(Q).interpolate(h_expr)
u0 = firedrake.Function(V).interpolate(u_expr)

h = h0.copy(deepcopy=True)

# Create some physical constants. Rather than specify the fluidity factor `A`
# in Glen's flow law, we instead define it in terms of a stress scale `τ_c` and
# a strain rate scale `ε_c` as
#
# A = ε_c / τ_c ** n
#
# where `n` is the Glen flow law exponent. This way, we can do a continuation-
# type method in `n` while preserving dimensional correctness.
ε_c = firedrake.Constant(0.01)
τ_c = firedrake.Constant(0.1)


if args.form == "primal":
u = u0.copy(deepcopy=True)
solver = icepack.solvers.FlowSolver(icepack.models.IceShelf(), dirichlet_ids=[1])
A = firedrake.Constant(ε_c / τ_c ** n)
solver.diagnostic_solve(velocity=u, thickness=h, fluidity=A)
diagnostic_solver = solver._diagnostic_solver
elif args.form == "dual":
Σ = firedrake.TensorFunctionSpace(mesh, dg0, symmetry=True)
Z = V * Σ
z = firedrake.Function(Z)

# Set up the diagnostic problem
u, M = firedrake.split(z)
fields = {
"velocity": u,
"membrane_stress": M,
"thickness": h,
}

fns = [model.viscous_power, model.ice_shelf_momentum_balance]

rheology = {
"flow_law_exponent": n,
"flow_law_coefficient": ε_c / τ_c ** n,
}

L = sum(fn(**fields, **rheology) for fn in fns)
F = derivative(L, z)

# Make an initial guess by solving a Picard linearization
linear_rheology = {
"flow_law_exponent": 1,
"flow_law_coefficient": ε_c / τ_c,
}

L_1 = sum(fn(**fields, **linear_rheology) for fn in fns)
F_1 = derivative(L_1, z)

qdegree = n + 2
bc = firedrake.DirichletBC(Z.sub(0), u0, (1,))
problem_params = {
#"form_compiler_parameters": {"quadrature_degree": qdegree},
"bcs": bc,
}
solver_params = {
"solver_parameters": {
"snes_type": "newtonls",
"ksp_type": "gmres",
"pc_type": "lu",
"pc_factor_mat_solver_type": "mumps",
"snes_rtol": 1e-2,
},
}
firedrake.solve(F_1 == 0, z, **problem_params, **solver_params)

# Create an approximate linearization
h_min = firedrake.Constant(1.0)
rfields = {
"velocity": u,
"membrane_stress": M,
"thickness": firedrake.max_value(h_min, h),
}

L_r = sum(fn(**rfields, **rheology) for fn in fns)
F_r = derivative(L_r, z)
J_r = derivative(F_r, z)

# Set up the diagnostic problem and solver
diagnostic_problem = NonlinearVariationalProblem(F, z, J=J_r, **problem_params)
diagnostic_solver = NonlinearVariationalSolver(diagnostic_problem, **solver_params)
diagnostic_solver.solve()
else:
raise ValueError(f"--form must be either `primal` or `dual`, got {args.form}")

# Set up the prognostic problem and solver
prognostic_problem = model.mass_balance(
thickness=h,
velocity=u,
accumulation=firedrake.Constant(0.0),
thickness_inflow=h0,
test_function=firedrake.TestFunction(Q),
)
dt = firedrake.Constant(args.final_time / args.num_steps)
t = firedrake.Constant(0.0)
method = irksome.BackwardEuler()
prognostic_params = {
"solver_parameters": {
"snes_type": "ksponly",
"ksp_type": "gmres",
"pc_type": "bjacobi",
},
}
prognostic_solver = irksome.TimeStepper(
prognostic_problem, method, t, dt, h, **prognostic_params
)

# Run the simulation
for step in tqdm.trange(args.num_steps):
prognostic_solver.advance()
h.interpolate(firedrake.max_value(0, h))
diagnostic_solver.solve()
65 changes: 6 additions & 59 deletions demos/gibbous-ice-shelf/gibbous.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import argparse
import subprocess
import numpy as np
from numpy import pi as π
import tqdm
import pygmsh
import firedrake
from firedrake import (
inner,
Expand All @@ -17,6 +14,7 @@
import irksome
from icepack2 import model
from icepack2.constants import glen_flow_law as n
import gibbous_inputs

parser = argparse.ArgumentParser()
parser.add_argument("--input")
Expand All @@ -31,60 +29,8 @@
R = 200e3
δx = args.resolution

geometry = pygmsh.built_in.Geometry()

x1 = geometry.add_point([-R, 0, 0], lcar=δx)
x2 = geometry.add_point([+R, 0, 0], lcar=δx)

center1 = geometry.add_point([0, 0, 0], lcar=δx)
center2 = geometry.add_point([0, -4 * R, 0], lcar=δx)

arcs = [
geometry.add_circle_arc(x1, center1, x2),
geometry.add_circle_arc(x2, center2, x1),
]

line_loop = geometry.add_line_loop(arcs)
plane_surface = geometry.add_plane_surface(line_loop)

physical_lines = [geometry.add_physical(arc) for arc in arcs]
physical_surface = geometry.add_physical(plane_surface)

with open("ice-shelf.geo", "w") as geo_file:
geo_file.write(geometry.get_code())

command = "gmsh -2 -format msh2 -v 0 -o ice-shelf.msh ice-shelf.geo"
subprocess.run(command.split())

mesh = firedrake.Mesh("ice-shelf.msh")

# Generate the initial data
inlet_angles = π * np.array([-3 / 4, -1 / 2, -1 / 3, -1 / 6])
inlet_widths = π * np.array([1 / 8, 1 / 12, 1 / 24, 1 / 12])

x = firedrake.SpatialCoordinate(mesh)

u_in = 300
h_in = 350
hb = 100
dh, du = 400, 250

hs, us = [], []
for θ, ϕ in zip(inlet_angles, inlet_widths):
x0 = R * firedrake.as_vector((np.cos(θ), np.sin(θ)))
v = -firedrake.as_vector((np.cos(θ), np.sin(θ)))
L = inner(x - x0, v)
W = x - x0 - L * v
Rn = 2 * ϕ / π * R
q = firedrake.max_value(1 - (W / Rn) ** 2, 0)
hs.append(hb + q * ((h_in - hb) - dh * L / R))
us.append(firedrake.exp(-4 * (W / R) ** 2) * (u_in + du * L / R) * v)

h_expr = firedrake.Constant(hb)
for h in hs:
h_expr = firedrake.max_value(h, h_expr)

u_expr = sum(us)
mesh = gibbous_inputs.make_mesh(R, δx)
h_expr, u_expr = gibbous_inputs.make_initial_data(mesh, R)

# Create some function spaces and some fields
cg = firedrake.FiniteElement("CG", "triangle", 1)
Expand All @@ -95,8 +41,8 @@
Σ = firedrake.TensorFunctionSpace(mesh, dg0, symmetry=True)
Z = V * Σ

h0 = firedrake.interpolate(h_expr, Q)
u0 = firedrake.interpolate(u_expr, V)
h0 = firedrake.Function(Q).interpolate(h_expr)
u0 = firedrake.Function(V).interpolate(u_expr)

h = h0.copy(deepcopy=True)
z = firedrake.Function(Z)
Expand Down Expand Up @@ -218,6 +164,7 @@

# Set up a calving mask
R = firedrake.Constant(60e3)
x = firedrake.SpatialCoordinate(mesh)
y = firedrake.Constant((0.0, R))
mask = firedrake.conditional(inner(x - y, x - y) < R**2, 0.0, 1.0)

Expand Down
64 changes: 64 additions & 0 deletions demos/gibbous-ice-shelf/gibbous_inputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import subprocess
import numpy as np
from numpy import pi as π
import pygmsh
import firedrake
from firedrake import inner


def make_mesh(R, δx):
geometry = pygmsh.built_in.Geometry()
x1 = geometry.add_point([-R, 0, 0], lcar=δx)
x2 = geometry.add_point([+R, 0, 0], lcar=δx)

center1 = geometry.add_point([0, 0, 0], lcar=δx)
center2 = geometry.add_point([0, -4 * R, 0], lcar=δx)

arcs = [
geometry.add_circle_arc(x1, center1, x2),
geometry.add_circle_arc(x2, center2, x1),
]

line_loop = geometry.add_line_loop(arcs)
plane_surface = geometry.add_plane_surface(line_loop)

physical_lines = [geometry.add_physical(arc) for arc in arcs]
physical_surface = geometry.add_physical(plane_surface)

with open("ice-shelf.geo", "w") as geo_file:
geo_file.write(geometry.get_code())

command = "gmsh -2 -format msh2 -v 0 -o ice-shelf.msh ice-shelf.geo"
subprocess.run(command.split())

return firedrake.Mesh("ice-shelf.msh")


def make_initial_data(mesh, R):
inlet_angles = π * np.array([-3 / 4, -1 / 2, -1 / 3, -1 / 6])
inlet_widths = π * np.array([1 / 8, 1 / 12, 1 / 24, 1 / 12])

x = firedrake.SpatialCoordinate(mesh)

u_in = 300
h_in = 350
hb = 100
dh, du = 400, 250

hs, us = [], []
for θ, ϕ in zip(inlet_angles, inlet_widths):
x0 = R * firedrake.as_vector((np.cos(θ), np.sin(θ)))
v = -firedrake.as_vector((np.cos(θ), np.sin(θ)))
L = inner(x - x0, v)
W = x - x0 - L * v
Rn = 2 * ϕ / π * R
q = firedrake.max_value(1 - (W / Rn) ** 2, 0)
hs.append(hb + q * ((h_in - hb) - dh * L / R))
us.append(firedrake.exp(-4 * (W / R) ** 2) * (u_in + du * L / R) * v)

h_expr = firedrake.Constant(hb)
for h in hs:
h_expr = firedrake.max_value(h, h_expr)

u_expr = sum(us)
return h_expr, u_expr
Loading

0 comments on commit f5f46b8

Please sign in to comment.