def advective_flux(self, **kwargs): keys = ('energy', 'velocity', 'vertical_velocity', 'thickness', 'energy_inflow', 'energy_surface') keys_alt = ('E', 'u', 'w', 'h', 'E_inflow', 'E_surface') E, u, w, h, E_inflow, E_surface = get_kwargs_alt( kwargs, keys, keys_alt) Q = E.function_space() ψ = firedrake.TestFunction(Q) U = firedrake.as_vector((u[0], u[1], w)) flux_cells = -E * inner(U, grad(ψ)) * h * dx mesh = Q.mesh() ν = facet_normal_2(mesh) outflow = firedrake.max_value(inner(u, ν), 0) inflow = firedrake.min_value(inner(u, ν), 0) flux_outflow = (E * outflow * ψ * h * ds_v + E * firedrake.max_value(-w, 0) * ψ * h * ds_b + E * firedrake.max_value(+w, 0) * ψ * h * ds_t) flux_inflow = (E_inflow * inflow * ψ * h * ds_v + E_surface * firedrake.min_value(-w, 0) * ψ * h * ds_b + E_surface * firedrake.min_value(+w, 0) * ψ * h * ds_t) return flux_cells + flux_outflow + flux_inflow
def side_friction(**kwargs): r"""Return the side wall friction part of the action functional The component of the action functional due to friction along the side walls of the domain is .. math:: E(u) = -\frac{m}{m + 1}\int_\Gamma h\tau(u, C_s)\cdot u\; ds where :math:`\tau(u, C_s)` is the side wall shear stress, :math:`ds` is the element of surface area and :math:`\Gamma` are the side walls. Side wall friction is relevant for glaciers that flow through a fjord with rock walls on either side. """ u, h = get_kwargs_alt(kwargs, ('velocity', 'thickness'), ('u', 'h')) Cs = kwargs.get('side_friction', kwargs.get('Cs', firedrake.Constant(0.))) mesh = u.ufl_domain() if mesh.geometric_dimension() == 2: ν = firedrake.FacetNormal(mesh) else: ν = facet_normal_2(mesh) u_t = u - inner(u, ν) * ν τ = friction_stress(u_t, Cs) return -m / (m + 1) * h * inner(τ, u_t)
def normal_flow_penalty(**kwargs): r"""Return the penalty for flow normal to the domain boundary For problems where a glacier flows along some boundary, e.g. a fjord wall, the velocity has to be parallel to this boundary. Rather than enforce this boundary condition directly, we add a penalty for normal flow to the action functional. """ u, = get_kwargs_alt(kwargs, ('velocity', ), ('u', )) scale = kwargs.get('scale', firedrake.Constant(1.)) mesh = u.ufl_domain() if mesh.geometric_dimension() == 2: ν = firedrake.FacetNormal(mesh) elif mesh.geometric_dimension() == 3: ν = facet_normal_2(mesh) L = diameter(mesh) δx = firedrake.FacetArea(mesh) # Get the polynomial degree in the horizontal direction of the velocity # field -- if extruded, the element degree is a tuple of the horizontal # and vertical degrees. degree = u.ufl_function_space().ufl_element().degree() if isinstance(degree, tuple): d = degree[0] else: d = degree exponent = kwargs.get('exponent', d + 1) penalty = scale * (L / δx)**exponent return 0.5 * penalty * inner(u, ν)**2
def terminus(**kwargs): r"""Return the terminal stress part of the ice stream action functional The power exerted due to stress at the ice calving terminus :math:`\Gamma` is .. math:: E(u) = \frac{1}{2}\int_\Gamma\left(\rho_Igh^2 - \rho_Wgd^2\right) u\cdot \nu\, ds where :math:`d` is the water depth at the terminus. We assume that sea level is at :math:`z = 0` for purposes of calculating the water depth. Parameters ---------- velocity : firedrake.Function thickness : firedrake.Function surface : firedrake.Function """ keys = ('velocity', 'thickness', 'surface') keys_alt = ('u', 'h', 's') u, h, s = get_kwargs_alt(kwargs, keys, keys_alt) d = firedrake.min_value(s - h, 0) τ_I = ρ_I * g * h**2 / 2 τ_W = ρ_W * g * d**2 / 2 ν = firedrake.FacetNormal(u.ufl_domain()) return (τ_I - τ_W) * inner(u, ν)
def sources(self, **kwargs): keys = ('energy', 'thickness', 'heat', 'heat_bed') keys_alt = ('E', 'h', 'q', 'q_bed') E, h, q, q_bed = get_kwargs_alt(kwargs, keys, keys_alt) Q = E.function_space() ψ = firedrake.TestFunction(Q) internal_sources = q * ψ * h * dx boundary_sources = q_bed * ψ * ds_b return internal_sources + boundary_sources
def diffusive_flux(self, **kwargs): keys = ('energy', 'thickness', 'energy_surface') keys_alt = ('E', 'h', 'E_surface') E, h, E_surface = get_kwargs_alt(kwargs, keys, keys_alt) Q = E.function_space() ψ = firedrake.TestFunction(Q) κ = self.surface_exchange_coefficient cell_flux = α * E.dx(2) * ψ.dx(2) / h * dx surface_flux = κ * α * (E - E_surface) * ψ / h * ds_t return cell_flux + surface_flux
def __call__(self, dt, **kwargs): keys = ('thickness', 'velocity', 'accumulation') keys_alt = ('h', 'u', 'a') h, u, a = utilities.get_kwargs_alt(kwargs, keys, keys_alt) h_inflow = kwargs.get('thickness_inflow', kwargs.get('h_inflow', h)) Q = h.function_space() q = firedrake.TestFunction(Q) grad, ds, n = self.grad, self.ds, self.facet_normal(Q.mesh()) u_n = inner(u, n) flux_cells = -inner(h * u, grad(q)) * dx flux_out = h * firedrake.max_value(u_n, 0) * q * ds flux_in = h_inflow * firedrake.min_value(u_n, 0) * q * ds accumulation = a * q * dx return accumulation - (flux_in + flux_out + flux_cells)
def terminus(**kwargs): r"""Return the terminus stress part of the ice shelf action functional The power exerted due to stress at the calving terminus :math:`\Gamma` is .. math:: E(u) = \int_\Gamma\varrho gh^2u\cdot\nu\; ds We assume that sea level is at :math:`z = 0` for calculating the water depth. """ u, h = get_kwargs_alt(kwargs, ('velocity', 'thickness'), ('u', 'h')) mesh = u.ufl_domain() ν = firedrake.FacetNormal(mesh) ρ = ρ_I * (1 - ρ_I / ρ_W) return 0.5 * ρ * g * h**2 * inner(u, ν)
def sources(self, **kwargs): keys = ('damage', 'velocity', 'strain_rate', 'membrane_stress') keys_alt = ('D', 'u', 'ε', 'M') D, u, ε, M = get_kwargs_alt(kwargs, keys, keys_alt) # Increase/decrease damage depending on stress and strain rates ε_1 = eigenvalues(ε)[0] σ_e = sqrt(inner(M, M) - det(M)) ε_h = firedrake.Constant(self.healing_strain_rate) σ_d = firedrake.Constant(self.damage_stress) γ_h = firedrake.Constant(self.healing_rate) γ_d = firedrake.Constant(self.damage_rate) healing = γ_h * min_value(ε_1 - ε_h, 0) fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.) * (1 - D) return healing + fracture
def flux(self, **kwargs): keys = ('damage', 'velocity', 'damage_inflow') keys_alt = ('D', 'u', 'D_inflow') D, u, D_inflow = get_kwargs_alt(kwargs, keys, keys_alt) Q = D.function_space() φ = firedrake.TestFunction(Q) mesh = Q.mesh() n = firedrake.FacetNormal(mesh) u_n = max_value(0, inner(u, n)) f = D * u_n flux_faces = (f('+') - f('-')) * (φ('+') - φ('-')) * dS flux_cells = -D * div(u * φ) * dx flux_out = D * max_value(0, inner(u, n)) * φ * ds flux_in = D_inflow * min_value(0, inner(u, n)) * φ * ds return flux_faces + flux_cells + flux_out + flux_in
def penalty(**kwargs): r"""Return the penalty of the shallow ice action functional The penalty for the shallow ice action functional is .. math:: E(u) = \frac{1}{2}\int_\Omega l^2\nabla u\cdot \nabla u\; dx Parameters ---------- velocity : firedrake.Function thickness : firedrake.Function Returns ------- firedrake.Form """ u, h = get_kwargs_alt(kwargs, ('velocity', 'thickness'), ('u', 'h')) l = 2 * firedrake.max_value(firedrake.CellDiameter(u.ufl_domain()), 5 * h) return .5 * l**2 * inner(grad(u), grad(u))
def sources(self, **kwargs): keys = ('damage', 'velocity', 'fluidity') keys_alt = ('D', 'u', 'A') D, u, A = get_kwargs_alt(kwargs, keys, keys_alt) # Increase/decrease damage depending on stress and strain rates ε = sym(grad(u)) ε_1 = eigenvalues(ε)[0] σ = M(ε, A) σ_e = sqrt(inner(σ, σ) - det(σ)) ε_h = firedrake.Constant(self.healing_strain_rate) σ_d = firedrake.Constant(self.damage_stress) γ_h = firedrake.Constant(self.healing_rate) γ_d = firedrake.Constant(self.damage_rate) healing = γ_h * min_value(ε_1 - ε_h, 0) fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.) * (1 - D) return healing + fracture
def terminus(**kwargs): r"""Return the terminal stress part of the hybrid model action functional The power exerted due to stress at the calving terminus :math:`\Gamma` is .. math:: E(u) = \int_\Gamma\int_0^1\left(\rho_Ig(1 - \zeta) - \rho_Wg(\zeta_{\text{sl}} - \zeta)_+\right)u\cdot\nu\; h\, d\zeta\; ds where :math:`\zeta_\text{sl}` is the relative depth to sea level and the :math:`(\zeta_\text{sl} - \zeta)_+` denotes only the positive part. Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness s : firedrake.Function ice surface elevation ice_front_ids : list of int numeric IDs of the parts of the boundary corresponding to the calving front """ keys = ('velocity', 'thickness', 'surface') keys_alt = ('u', 'h', 's') u, h, s = get_kwargs_alt(kwargs, keys, keys_alt) mesh = u.ufl_domain() zdegree = u.ufl_element().degree()[1] ζ = firedrake.SpatialCoordinate(mesh)[2] b = s - h ζ_sl = firedrake.max_value(-b, 0) / h p_W = ρ_W * g * h * _pressure_approx(zdegree + 1)(ζ, ζ_sl) p_I = ρ_I * g * h * (1 - ζ) ν = facet_normal_2(mesh) return (p_I - p_W) * inner(u, ν) * h
def gravity(**kwargs): r"""Return the gravitational part of the ice stream action functional The gravitational part of the hybrid model action functional is .. math:: E(u) = -\int_\Omega\int_0^1\rho_Ig\nabla s\cdot u\; h\, d\zeta\; dx Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness s : firedrake.Function ice surface elevation """ keys = ('velocity', 'thickness', 'surface') keys_alt = ('u', 'h', 's') u, h, s = get_kwargs_alt(kwargs, keys, keys_alt) return -ρ_I * g * inner(grad_2(s), u) * h
def gravity(**kwargs): r"""Return the gravitational part of the ice shelf action functional The gravitational part of the ice shelf action functional is .. math:: E(u) = -\frac{1}{2}\int_\Omega\varrho g\nabla h^2\cdot u\; dx Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness Returns ------- firedrake.Form """ u, h = get_kwargs_alt(kwargs, ('velocity', 'thickness'), ('u', 'h')) ρ = ρ_I * (1 - ρ_I / ρ_W) return -0.5 * ρ * g * inner(grad(h**2), u)
def viscosity(**kwargs): r"""Return the viscous part of the hybrid model action functional The viscous component of the action for the hybrid model is .. math:: E(u) = \frac{n}{n + 1}\int_\Omega\int_0^1\left( M : \dot\varepsilon_x + \tau_z\cdot\varepsilon_z\right)h\, d\zeta\; dx where :math:`M(\dot\varepsilon, A)` is the membrane stress tensor and :math:`\tau_z` is the vertical shear stress vector. This form assumes that we're using the fluidity parameter instead the rheology parameter, the temperature, etc. To use a different variable, you can implement your own viscosity functional and pass it as an argument when initializing model objects to use your functional instead. Keyword arguments ----------------- velocity : firedrake.Function surface : firedrake.Function thickness : firedrake.Function fluidity : firedrake.Function `A` in Glen's flow law Returns ------- firedrake.Form """ keys = ('velocity', 'thickness', 'surface', 'fluidity') keys_alt = ('u', 'h', 's', 'A') u, h, s, A = get_kwargs_alt(kwargs, keys, keys_alt) ε_x, ε_z = horizontal_strain(u, s, h), vertical_strain(u, h) M, τ_z = stresses(ε_x, ε_z, A) return n / (n + 1) * (inner(M, ε_x) + inner(τ_z, ε_z)) * h
def gravity(**kwargs): r"""Return the gravity term for the shallow ice action functional The gravity function for the shallow ice action functional is .. math:: E(u) = \int_\Omega\frac{2A(\varrho_I g)**n}{n+2} (\nabla h^2\cdot u) h^{n+1} \nabla s^{n-1}\; dx Parameters ---------- velocity : firedrake.Function thickness : firedrake.Function surface : firedrake.Function fluidity : firedrake.Function or firedrake.Constant Returns ------- firedrake.Form """ keys = ('velocity', 'thickness', 'surface', 'fluidity') keys_alt = ('u', 'h', 's', 'A') u, h, s, A = get_kwargs_alt(kwargs, keys, keys_alt) return (2 * A * (ρ_I * g)**n / (n + 2)) * h**(n + 1) * grad(s)**(n - 1) * inner(grad(s), u)