|
| 1 | +import ufl |
| 2 | +from firedrake import ( |
| 3 | + Constant, inner, tr, sym, grad, div, dx, ds, dS, avg, jump, FacetNormal, min_value |
| 4 | +) |
| 5 | +from ..constants import ice_density as ρ_I, water_density as ρ_W, gravity as g |
| 6 | + |
| 7 | + |
| 8 | +def viscous_power(**kwargs): |
| 9 | + r"""Return the viscous power dissipation""" |
| 10 | + # Get all the dynamical fields |
| 11 | + field_names = ("membrane_stress", "thickness") |
| 12 | + M, h = map(kwargs.get, field_names) |
| 13 | + |
| 14 | + # Get the parameters for the constitutive relation |
| 15 | + parameter_names = ("flow_law_coefficient", "flow_law_exponent") |
| 16 | + A, n = map(kwargs.get, parameter_names) |
| 17 | + |
| 18 | + mesh = ufl.domain.extract_unique_domain(M) |
| 19 | + d = mesh.geometric_dimension() |
| 20 | + |
| 21 | + M_2 = (inner(M, M) - tr(M) ** 2 / (d + 1)) / 2 |
| 22 | + M_n = M_2 if float(n) == 1 else M_2 ** ((n + 1) / 2) |
| 23 | + return 2 * h * A / (n + 1) * M_n * dx |
| 24 | + |
| 25 | + |
| 26 | +def friction_power(**kwargs): |
| 27 | + r"""Return the frictional power dissipation""" |
| 28 | + τ = kwargs["basal_stress"] |
| 29 | + parameter_names = ("sliding_coefficient", "sliding_exponent") |
| 30 | + K, m = map(kwargs.get, parameter_names) |
| 31 | + τ_2 = inner(τ, τ) |
| 32 | + τ_m = τ_2 if float(m) == 1 else τ_2 ** ((m + 1) / 2) |
| 33 | + return K / (m + 1) * τ_m * dx |
| 34 | + |
| 35 | + |
| 36 | +def calving_terminus(**kwargs): |
| 37 | + r"""Return the power dissipation from the terminus boundary condition""" |
| 38 | + # Get all the dynamical fields and boundary conditions |
| 39 | + u, h, s = map(kwargs.get, ("velocity", "thickness", "surface")) |
| 40 | + outflow_ids = kwargs["outflow_ids"] |
| 41 | + |
| 42 | + # Get the unit outward normal vector to the terminus |
| 43 | + mesh = ufl.domain.extract_unique_domain(u) |
| 44 | + ν = FacetNormal(mesh) |
| 45 | + |
| 46 | + # Compute the forces per unit length at the terminus from the glacier |
| 47 | + # and from the ocean (assuming that sea level is at z = 0) |
| 48 | + f_I = 0.5 * ρ_I * g * h**2 |
| 49 | + d = min_value(0, s - h) |
| 50 | + f_W = 0.5 * ρ_W * g * d**2 |
| 51 | + |
| 52 | + return (f_I - f_W) * inner(u, ν) * ds(outflow_ids) |
| 53 | + |
| 54 | + |
| 55 | +def momentum_balance(**kwargs): |
| 56 | + r"""Return the momentum balance constraint""" |
| 57 | + field_names = ( |
| 58 | + "velocity", "membrane_stress", "basal_stress", "thickness", "surface" |
| 59 | + ) |
| 60 | + u, M, τ, h, s = map(kwargs.get, field_names) |
| 61 | + f = kwargs.get("floating", Constant(1.0)) |
| 62 | + ε = sym(grad(u)) |
| 63 | + cell_balance = (-h * inner(M, ε) + inner(f * τ - ρ_I * g * h * grad(s), u)) * dx |
| 64 | + |
| 65 | + mesh = ufl.domain.extract_unique_domain(u) |
| 66 | + ν = FacetNormal(mesh) |
| 67 | + facet_balance = ρ_I * g * avg(h) * inner(jump(s, ν), avg(u)) * dS |
| 68 | + |
| 69 | + return cell_balance + facet_balance |
| 70 | + |
| 71 | + |
| 72 | +def ice_shelf_momentum_balance(**kwargs): |
| 73 | + r"""Return the momentum balance constraint for floating ice shelves |
| 74 | +
|
| 75 | + Floating ice shelves are simpler because there is no basal shear stress |
| 76 | + and we assume the ice is hydrostatic, in which case the surface |
| 77 | + elevation is proportional to the thickness. |
| 78 | + """ |
| 79 | + field_names = ("velocity", "membrane_stress", "thickness") |
| 80 | + u, M, h = map(kwargs.get, field_names) |
| 81 | + ε = sym(grad(u)) |
| 82 | + |
| 83 | + ρ = ρ_I * (1 - ρ_I / ρ_W) |
| 84 | + return (-h * inner(M, ε) + 0.5 * ρ * g * h**2 * div(u)) * dx |
0 commit comments