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 norm(u, norm_type="L2"): r"""Compute the norm of a field Computes one of any number of norms of a scalar or vector field. The available options are: - ``L2``: :math:`\|u\|^2 = \int_\Omega|u|^2dx` - ``H01``: :math:`\|u\|^2 = \int_\Omega|\nabla u|^2dx` - ``H1``: :math:`\|u\|^2 = \int_\Omega\left(|u|^2 + L^2|\nabla u|^2\right)dx` - ``L1``: :math:`\|u\| = \int_\Omega|u|dx` - ``TV``: :math:`\|u\| = \int_\Omega|\nabla u|dx` - ``Linfty``: :math:`\|u\| = \max_{x\in\Omega}|u(x)|` The extra factor :math:`L` in the :math:`H^1` norm is the diameter of the domain in the infinity metric. This extra factor is included to make the norm scale appropriately with the size of the domain. """ if norm_type == "L2": form, p = inner(u, u) * dx, 2 if norm_type == "H01": form, p = inner(grad(u), grad(u)) * dx, 2 if norm_type == "H1": L = utilities.diameter(u.ufl_domain()) form, p = inner(u, u) * dx + L**2 * inner(grad(u), grad(u)) * dx, 2 if norm_type == "L1": form, p = sqrt(inner(u, u)) * dx, 1 if norm_type == "TV": form, p = sqrt(inner(grad(u), grad(u))) * dx, 1 if norm_type == "Linfty": data = u.dat.data_ro if len(data.shape) == 1: local_max = np.max(np.abs(data)) elif len(data.shape) == 2: local_max = np.max(np.sqrt(np.sum(data**2, 1))) return u.comm.allreduce(local_max, op=max) return assemble(form)**(1 / p)
def normal_flow_penalty(u, scale=1.0, exponent=None, side_wall_ids=()): 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. """ mesh = u.ufl_domain() ν = firedrake.FacetNormal(mesh) L = utilities.diameter(mesh) δx = firedrake.CellSize(mesh) d = u.ufl_function_space().ufl_element().degree() exponent = d + 1 if exponent is None else exponent penalty = scale * (L / δx)**exponent return 0.5 * penalty * inner(u, ν)**2 * ds(tuple(side_wall_ids))
def normal_flow_penalty(u, h, scale=1.0, exponent=None, side_wall_ids=()): 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. """ mesh = u.ufl_domain() ν = facet_normal_2(mesh) # Note that this quantity has units of [length] x [dimensionless] because # the mesh has a "thickness" of 1! If it had dimensions of physical # thickness, we would instead use the square root of the facet area. δx = firedrake.FacetArea(mesh) L = diameter(mesh) d = u.ufl_function_space().ufl_element().degree()[0] exponent = d + 1 if (exponent is None) else exponent penalty = scale * (L / δx)**exponent return 0.5 * penalty * inner(u, ν)**2 * h * ds_v(tuple(side_wall_ids))