Skip to content

Commit 661f5a8

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

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

test/dome_test.py

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

0 commit comments

Comments
 (0)