Skip to content

Commit dd411df

Browse files
committed
Use Rainier demo as unit test
1 parent 6ca00b6 commit dd411df

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed

test/dome_test.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import numpy as np
2+
from numpy import pi as π
3+
import firedrake
4+
from firedrake import inner, grad, dx, ds, exp, min_value, max_value, Constant
5+
import irksome
6+
from icepack2.model import mass_balance
7+
from icepack2.model.variational import momentum_balance, flow_law, friction_law
8+
from icepack2.constants import gravity, ice_density, glen_flow_law
9+
10+
11+
def run_simulation(refinement_level: int):
12+
radius = Constant(12e3)
13+
mesh = firedrake.UnitDiskMesh(refinement_level)
14+
mesh.coordinates.dat.data[:] *= float(radius)
15+
16+
# Make a bunch of finite elements and function spaces
17+
cg1 = firedrake.FiniteElement("CG", "triangle", 1)
18+
dg1 = firedrake.FiniteElement("DG", "triangle", 1)
19+
dg0 = firedrake.FiniteElement("DG", "triangle", 0)
20+
S = firedrake.FunctionSpace(mesh, cg1)
21+
Q = firedrake.FunctionSpace(mesh, dg1)
22+
V = firedrake.VectorFunctionSpace(mesh, cg1)
23+
Σ = firedrake.TensorFunctionSpace(mesh, dg0, symmetry=True)
24+
T = firedrake.VectorFunctionSpace(mesh, cg1)
25+
Z = V * Σ * T
26+
W = V * Σ * T * Q
27+
28+
x = firedrake.SpatialCoordinate(mesh)
29+
30+
# Make the bed topography
31+
B = Constant(4e3)
32+
r_b = Constant(150e3 / (2 * π))
33+
expr = B * exp(-inner(x, x) / r_b**2)
34+
b = firedrake.Function(S).interpolate(expr)
35+
36+
# Make the mass balance field
37+
z_measured = Constant(1600.0)
38+
a_measured = Constant(-0.917 * 8.7)
39+
a_top = Constant(0.7)
40+
z_top = Constant(4e3)
41+
δa_δz = (a_top - a_measured) / (z_top - z_measured)
42+
a_max = Constant(0.7)
43+
44+
def smb(z):
45+
return min_value(a_max, a_measured + δa_δz * (z - z_measured))
46+
47+
# Make the initial thickness
48+
r_h = Constant(5e3)
49+
H = Constant(100.0)
50+
expr = H * firedrake.max_value(0, 1 - inner(x, x) / r_h**2)
51+
h = firedrake.Function(Q).interpolate(expr)
52+
h_0 = h.copy(deepcopy=True)
53+
54+
s = firedrake.Function(Q).interpolate(b + h)
55+
a = firedrake.Function(Q).interpolate(smb(s))
56+
57+
# Fluidity of ice in yr⁻¹ MPa⁻³ at 0C
58+
A = Constant(158.0)
59+
60+
# Make an initial guess for the velocity using SIA
61+
u = firedrake.Function(V)
62+
v = firedrake.TestFunction(V)
63+
64+
ρ_I = Constant(ice_density)
65+
g = Constant(gravity)
66+
67+
n = Constant(glen_flow_law)
68+
69+
P = ρ_I * g * h
70+
S_n = inner(grad(s), grad(s))**((n - 1) / 2)
71+
u_shear = -2 * A * P ** n / (n + 2) * h * S_n * grad(s)
72+
F = inner(u - u_shear, v) * dx
73+
74+
degree = 1
75+
qdegree = max(8, degree ** glen_flow_law)
76+
pparams = {"form_compiler_parameters": {"quadrature_degree": qdegree}}
77+
firedrake.solve(F == 0, u, **pparams)
78+
79+
# Compute the initial velocity using the dual form of SSA
80+
z = firedrake.Function(Z)
81+
z.sub(0).assign(u);
82+
83+
τ_c = Constant(0.1)
84+
ε_c = Constant(A * τ_c ** n)
85+
86+
K = h * A / (n + 2)
87+
U_c = Constant(100.0)
88+
u_c = K * τ_c ** n + U_c
89+
90+
glen_rheology = {
91+
"flow_law_exponent": n,
92+
"flow_law_coefficient": ε_c / τ_c ** n,
93+
"sliding_exponent": n,
94+
"sliding_coefficient": u_c / τ_c ** n,
95+
}
96+
97+
α = firedrake.Constant(1e-4)
98+
linear_rheology = {
99+
"flow_law_exponent": 1,
100+
"flow_law_coefficient": ε_c / τ_c,
101+
"sliding_exponent": 1,
102+
"sliding_coefficient": u_c / τ_c,
103+
}
104+
105+
u, M, τ = firedrake.split(z)
106+
fields = {
107+
"velocity": u,
108+
"membrane_stress": M,
109+
"basal_stress": τ,
110+
"thickness": h,
111+
"surface": s,
112+
}
113+
114+
sparams = {
115+
"solver_parameters": {
116+
"snes_monitor": ":rainier-output-initial.log",
117+
"snes_type": "newtonls",
118+
"snes_max_it": 200,
119+
"snes_linesearch_type": "nleqerr",
120+
"ksp_type": "gmres",
121+
"pc_type": "lu",
122+
"pc_factor_mat_solver_type": "mumps",
123+
},
124+
}
125+
126+
v, N, σ = firedrake.TestFunctions(Z)
127+
F = (
128+
momentum_balance(**fields, test_function=v)
129+
+ firedrake.replace(flow_law(**fields, **glen_rheology, test_function=N), {h: H})
130+
+ α * firedrake.replace(flow_law(**fields, **linear_rheology, test_function=N), {h: H})
131+
+ friction_law(**fields, **glen_rheology, test_function=σ)
132+
+ α * friction_law(**fields, **linear_rheology, test_function=σ)
133+
)
134+
135+
momentum_problem = firedrake.NonlinearVariationalProblem(F, z, **pparams)
136+
momentum_solver = firedrake.NonlinearVariationalSolver(momentum_problem, **sparams)
137+
138+
num_continuation_steps = 5
139+
for exponent in np.linspace(1.0, 3.0, num_continuation_steps):
140+
n.assign(exponent)
141+
momentum_solver.solve()
142+
143+
# Time-dependent solve
144+
w = firedrake.Function(W)
145+
w.sub(0).assign(z.sub(0))
146+
w.sub(1).assign(z.sub(1))
147+
w.sub(2).assign(z.sub(2))
148+
w.sub(3).assign(h);
149+
150+
u, M, τ, h = firedrake.split(w)
151+
v, N, σ, η = firedrake.TestFunctions(W)
152+
153+
fields = {
154+
"velocity": u,
155+
"membrane_stress": M,
156+
"basal_stress": τ,
157+
"thickness": h,
158+
"surface": b + h,
159+
}
160+
161+
F_momentum = (
162+
momentum_balance(**fields, test_function=v)
163+
+ firedrake.replace(flow_law(**fields, **glen_rheology, test_function=N), {h: H})
164+
+ α * firedrake.replace(flow_law(**fields, **linear_rheology, test_function=N), {h: H})
165+
+ friction_law(**fields, **glen_rheology, test_function=σ)
166+
+ α * friction_law(**fields, **linear_rheology, test_function=σ)
167+
)
168+
F_mass = mass_balance(thickness=h, velocity=u, accumulation=a, test_function=η)
169+
F = F_momentum + F_mass
170+
171+
tableau = irksome.BackwardEuler()
172+
t = Constant(0.0)
173+
dt = Constant(1.0 / 6)
174+
175+
lower = firedrake.Function(W)
176+
upper = firedrake.Function(W)
177+
lower.assign(-np.inf)
178+
upper.assign(+np.inf)
179+
lower.subfunctions[3].assign(0.0)
180+
bounds = ("stage", lower, upper)
181+
182+
bparams = {
183+
"solver_parameters": {
184+
"snes_monitor": ":rainier-output-vi.log",
185+
"snes_type": "vinewtonrsls",
186+
"snes_max_it": 200,
187+
"ksp_type": "gmres",
188+
"pc_type": "lu",
189+
"pc_factor_mat_solver_type": "mumps",
190+
},
191+
"stage_type": "value",
192+
"basis_type": "Bernstein",
193+
"bounds": bounds,
194+
}
195+
196+
solver = irksome.TimeStepper(F, tableau, t, dt, w, **bparams, **pparams)
197+
198+
us = [w.subfunctions[0].copy(deepcopy=True)]
199+
hs = [w.subfunctions[3].copy(deepcopy=True)]
200+
201+
final_time = 300.0
202+
num_steps = int(final_time / float(dt))
203+
for step in num_steps:
204+
solver.advance()
205+
h = w.subfunctions[3]
206+
a.interpolate(smb(b + h))
207+
208+
us.append(w.subfunctions[0].copy(deepcopy=True))
209+
hs.append(w.subfunctions[3].copy(deepcopy=True))
210+
211+
return hs, us
212+
213+
214+
def test_dome_problem():
215+
hs, us = run_simulation(4)
216+
volumes = [firedrake.assemble(h * dx) for h in hs]
217+
assert volumes[0] <= volumes[-1]

0 commit comments

Comments
 (0)