Skip to content

Commit 96f635e

Browse files
committed
Revert changes to mass balance, use discontinuous velocity
The changes to the mass balance function were making the tests fail because we don't actually want the factor of `h` inside the `max_value` that defines the upwind flux. We can get some of the effect using a discontinuous velocity field but all this does is change the thickness downstream to its upstream value * (u / (u + u_c)) which is not zero.
1 parent 26c221e commit 96f635e

File tree

3 files changed

+119
-31
lines changed

3 files changed

+119
-31
lines changed

notebooks/frontal-ablation.ipynb

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
"metadata": {},
2727
"outputs": [],
2828
"source": [
29-
"nx, ny = 32, 32\n",
30-
"mesh = firedrake.UnitSquareMesh(nx, ny, diagonal=\"crossed\")\n",
29+
"nx, ny = 64, 64\n",
30+
"mesh = firedrake.UnitSquareMesh(nx, ny, quadrilateral=True)\n",
3131
"δ = 1.0 / nx"
3232
]
3333
},
@@ -39,7 +39,7 @@
3939
"outputs": [],
4040
"source": [
4141
"degree = 0\n",
42-
"element = firedrake.FiniteElement(\"DG\", \"triangle\", degree)\n",
42+
"element = firedrake.FiniteElement(\"DQ\", \"quadrilateral\", degree)\n",
4343
"Q = firedrake.FunctionSpace(mesh, element)"
4444
]
4545
},
@@ -90,12 +90,11 @@
9090
"metadata": {},
9191
"outputs": [],
9292
"source": [
93-
"u_γ = Constant(-1.0)\n",
94-
"x_γ = Constant(0.5)\n",
95-
"h_γ = Constant(0.5)\n",
93+
"U_c = Constant(-1.0)\n",
94+
"x_c = Constant(0.5)\n",
9695
"\n",
97-
"f_γ = h_γ * firedrake.as_vector(\n",
98-
" (firedrake.conditional(x[0] >= x_γ, u_γ, 0), 0)\n",
96+
"u_c = firedrake.as_vector(\n",
97+
" (firedrake.conditional(x[0] >= x_c, U_c, 0), 0)\n",
9998
")"
10099
]
101100
},
@@ -109,10 +108,9 @@
109108
"ϕ = firedrake.TestFunction(Q)\n",
110109
"F = model.mass_balance(\n",
111110
" thickness=h,\n",
112-
" velocity=u,\n",
111+
" velocity=u - u_c,\n",
113112
" accumulation=a + m,\n",
114113
" thickness_inflow=h_in,\n",
115-
" frontal_ablation=f_γ,\n",
116114
" test_function=ϕ,\n",
117115
")"
118116
]
@@ -135,10 +133,9 @@
135133
"\n",
136134
"params = {\n",
137135
" \"solver_parameters\": {\n",
136+
" \"snes_monitor\": \":frontal-ablation.log\",\n",
137+
" \"snes_atol\": 1e-12,\n",
138138
" \"snes_type\": \"vinewtonrsls\",\n",
139-
" \"ksp_type\": \"gmres\",\n",
140-
" \"pc_type\": \"lu\",\n",
141-
" \"pc_factor_mat_solver_type\": \"mumps\",\n",
142139
" },\n",
143140
" \"stage_type\": \"value\",\n",
144141
" \"basis_type\": \"Bernstein\",\n",
@@ -157,7 +154,7 @@
157154
"source": [
158155
"hs = [h.copy(deepcopy=True)]\n",
159156
"\n",
160-
"final_time = 1.0\n",
157+
"final_time = 2.0\n",
161158
"num_steps = int(final_time / float(dt))\n",
162159
"for step in trange(num_steps):\n",
163160
" solver.advance()\n",
@@ -203,6 +200,28 @@
203200
"source": [
204201
"HTML(animation.to_html5_video())"
205202
]
203+
},
204+
{
205+
"cell_type": "code",
206+
"execution_count": null,
207+
"id": "e05e0c73-52bc-4c80-9cfc-0411dc53b568",
208+
"metadata": {},
209+
"outputs": [],
210+
"source": [
211+
"hs[-1].at((0.75, 0.5))"
212+
]
213+
},
214+
{
215+
"cell_type": "code",
216+
"execution_count": null,
217+
"id": "602b42eb-5285-4838-87af-ee1ed9f4f030",
218+
"metadata": {},
219+
"outputs": [],
220+
"source": [
221+
"from firedrake import dx\n",
222+
"expr = firedrake.conditional(x[0] <= x_c, h_in, h_in / 2)\n",
223+
"firedrake.assemble(abs(hs[-1] - expr) * dx) / firedrake.assemble(abs(hs[-1]) * dx)"
224+
]
206225
}
207226
],
208227
"metadata": {

src/icepack2/model/__init__.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,16 @@ def mass_balance(**kwargs):
1111
field_names = ("thickness", "velocity", "accumulation", "test_function")
1212
h, u, a, φ = map(kwargs.get, field_names)
1313
h_in = kwargs.get("thickness_inflow", Constant(0.0))
14-
f_c = kwargs.get("frontal_ablation", Constant((0.0, 0.0)))
1514

16-
cell_balance = (Dt(h) * φ - inner(h * u - f_c, grad(φ)) - a * φ) * dx
15+
cell_balance = (Dt(h) * φ - inner(h * u, grad(φ)) - a * φ) * dx
1716

1817
mesh = ufl.domain.extract_unique_domain(h)
1918
ν = FacetNormal(mesh)
20-
F = max_value(0, inner(h * u - f_c, ν))
19+
f = h * max_value(0, inner(u, ν))
2120

22-
outflow = F * φ * ds
21+
outflow = f * φ * ds
2322
inflow = h_in * min_value(0, inner(u, ν)) * φ * ds
24-
boundary_balance = inflow + outflow
2523

26-
facet_balance = jump(F) * jump(φ) * dS
24+
facet_balance = jump(f) * jump(φ) * dS
2725

28-
return cell_balance + facet_balance + boundary_balance
26+
return cell_balance + facet_balance + inflow + outflow

test/mass_balance_test.py

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from numpy import pi as π
33
import pytest
44
import firedrake
5-
from firedrake import dx, inner, max_value, Constant
5+
from firedrake import dx, inner, max_value, Constant, conditional
66
import irksome
77
from icepack2 import model
88

@@ -49,19 +49,12 @@ def test_convergence_rate(degree):
4949
dt = firedrake.Constant(δt)
5050
final_time = 2 * π
5151

52-
lower, upper = firedrake.Function(Q), firedrake.Function(Q)
53-
upper.assign(+np.inf)
54-
bounds = ("stage", lower, upper)
5552
params = {
5653
"solver_parameters": {
57-
"snes_type": "vinewtonrsls",
54+
"snes_type": "ksponly",
5855
"ksp_type": "gmres",
59-
"pc_type": "lu",
60-
"pc_factor_mat_solver_type": "mumps",
56+
"pc_type": "bjacobi",
6157
},
62-
"stage_type": "value",
63-
"basis_type": "Bernstein",
64-
"bounds": bounds,
6558
}
6659
solver = irksome.TimeStepper(problem, tableau, t, dt, h, **params)
6760

@@ -80,3 +73,81 @@ def test_convergence_rate(degree):
8073
slope, intercept = np.polyfit(log_mesh_sizes, log_errors, 1)
8174
print(f"degree {degree}: log(error) ~= {slope:g} * log(dx) {intercept:+g}")
8275
assert slope > degree - 0.55
76+
77+
78+
@pytest.mark.parametrize("degree", [0, 1])
79+
def test_frontal_ablation(degree):
80+
errors, mesh_sizes = [], []
81+
k_min, k_max, num_steps = 5 - degree, 8 - degree, 9
82+
for nx in np.logspace(k_min, k_max, num_steps, base=2, dtype=int):
83+
mesh = firedrake.UnitSquareMesh(nx, nx, quadrilateral=True)
84+
x = firedrake.SpatialCoordinate(mesh)
85+
86+
element = firedrake.FiniteElement("DQ", "quadrilateral", degree)
87+
# Try Bernstein?
88+
89+
Q = firedrake.FunctionSpace(mesh, element)
90+
91+
u_max = Constant(1.0)
92+
u = Constant((u_max, 0.0))
93+
94+
h_in = Constant(1.0)
95+
x = firedrake.SpatialCoordinate(mesh)
96+
L = Constant(0.25)
97+
expr = conditional(x[0] < L, h_in, 0.0)
98+
h_0 = firedrake.Function(Q).project(expr)
99+
h = h_0.copy(deepcopy=True)
100+
101+
a = Constant(0.0)
102+
103+
U_c = Constant(0.5)
104+
x_c = Constant(0.5)
105+
u_c = firedrake.as_vector((conditional(x[0] >= x_c, U_c, 0), 0))
106+
107+
h_exact = conditional(x[0] <= x_c, h_in, h_in * u_max / (u_max + U_c))
108+
109+
ϕ = firedrake.TestFunction(Q)
110+
problem = model.mass_balance(
111+
thickness=h,
112+
velocity=u + u_c,
113+
accumulation=a,
114+
thickness_inflow=h_in,
115+
test_function=ϕ,
116+
)
117+
118+
δx = mesh.cell_sizes.dat.data_ro.max()
119+
δt = 0.5 * δx / u_max
120+
121+
t = Constant(0.0)
122+
dt = Constant(δt)
123+
final_time = 2.0
124+
125+
lower, upper = firedrake.Function(Q), firedrake.Function(Q)
126+
upper.assign(np.inf)
127+
params = {
128+
"solver_parameters": {
129+
"snes_type": "vinewtonrsls",
130+
"snes_atol": 1e-12,
131+
},
132+
"stage_type": "value",
133+
"basis_type": "Bernstein",
134+
"bounds": ("stage", lower, upper),
135+
}
136+
137+
tableau = irksome.BackwardEuler()
138+
solver = irksome.TimeStepper(problem, tableau, t, dt, h, **params)
139+
140+
while float(t) < final_time:
141+
if float(t) + float(dt) > final_time:
142+
dt.assign(final_time - float(t))
143+
solver.advance()
144+
t.assign(t + dt)
145+
146+
mesh_sizes.append(δx)
147+
errors.append(firedrake.assemble(abs(h - h_exact) * dx))
148+
print(".", end="", flush=True)
149+
150+
log_mesh_sizes = np.log2(np.array(mesh_sizes))
151+
log_errors = np.log2(np.array(errors))
152+
slope, intercept = np.polyfit(log_mesh_sizes, log_errors, 1)
153+
print(f"degree {degree}: log(error) ~= {slope:g} * log(dx) {intercept:+g}")

0 commit comments

Comments
 (0)