<commit_before>"""
This module contains slope limiters.
Slope limiters are used in transport schemes to enforce monotonicity. They are
generally passed as an argument to time discretisations, and should be selected
to be compatible with with :class:`FunctionSpace` of the transported field.
"""

from firedrake import (BrokenElement, Function, FunctionSpace, interval,
                       FiniteElement, TensorProductElement)
from firedrake.slope_limiter.vertex_based_limiter import VertexBasedLimiter
from gusto.kernels import LimitMidpoints

import numpy as np

__all__ = ["DG1Limiter", "ThetaLimiter", "NoLimiter"]


class DG1Limiter(object):
    """
    A vertex-based limiter for the degree 1 discontinuous Galerkin space.

    A vertex based limiter for fields in the DG1 space. This wraps around the
    vertex-based limiter implemented in Firedrake, but ensures that this is done
    in the space using the appropriate "equispaced" elements.
    """

    def __init__(self, space):
        """
        Args:
            space (:class:`FunctionSpace`): the space in which the transported
                variables lies. It should be the DG1 space.

        Raises:
            ValueError: If the space is not appropriate for the limiter.
        """

        self.space = space
        mesh = space.mesh()

        # check that space is DG1
        degree = space.ufl_element().degree()
        if (space.ufl_element().sobolev_space().name != 'L2'
            or ((type(degree) is tuple and np.any([deg != 1 for deg in degree]))
                and degree != 1)):
            raise ValueError('DG1 limiter can only be applied to DG1 space')

        # Create equispaced DG1 space needed for limiting
        if space.extruded:
            cell = mesh._base_mesh.ufl_cell().cellname()
            DG1_hori_elt = FiniteElement("DG", cell, 1, variant="equispaced")
            DG1_vert_elt = FiniteElement("DG", interval, 1, variant="equispaced")
            DG1_element = TensorProductElement(DG1_hori_elt, DG1_vert_elt)
        else:
            cell = mesh.ufl_cell().cellname()
            DG1_element = FiniteElement("DG", cell, 1, variant="equispaced")

        DG1_equispaced = FunctionSpace(mesh, DG1_element)

        self.vertex_limiter = VertexBasedLimiter(DG1_equispaced)
        self.field_equispaced = Function(DG1_equispaced)

    def apply(self, field):
        """
        The application of the limiter to the field.

        Args:
            field (:class:`Function`): the field to apply the limiter to.


class ThetaLimiter(object):
    """
    A vertex-based limiter for the degree 1 temperature space.
    A vertex based limiter for fields in the DG1xCG2 space, i.e. temperature
    variables in the next-to-lowest order set of spaces. This acts like the
    vertex-based limiter implemented in Firedrake, but in addition corrects
    the central nodes to prevent new maxima or minima forming.
    """

    def __init__(self, space):
        """
        Args:
            space (:class:`FunctionSpace`): the space in which the transported
                variables lies. It should be a form of the DG1xCG2 space.<commit_msg>reinstate apply method lost in merge<commit_after>"""
This module contains slope limiters.
Slope limiters are used in transport schemes to enforce monotonicity. They are
generally passed as an argument to time discretisations, and should be selected
to be compatible with with :class:`FunctionSpace` of the transported field.
"""

from firedrake import (BrokenElement, Function, FunctionSpace, interval,
                       FiniteElement, TensorProductElement)
from firedrake.slope_limiter.vertex_based_limiter import VertexBasedLimiter
from gusto.kernels import LimitMidpoints

import numpy as np

__all__ = ["DG1Limiter", "ThetaLimiter", "NoLimiter"]


class DG1Limiter(object):
    """
    A vertex-based limiter for the degree 1 discontinuous Galerkin space.

    A vertex based limiter for fields in the DG1 space. This wraps around the
    vertex-based limiter implemented in Firedrake, but ensures that this is done
    in the space using the appropriate "equispaced" elements.
    """

    def __init__(self, space):
        """
        Args:
            space (:class:`FunctionSpace`): the space in which the transported
                variables lies. It should be the DG1 space.

        Raises:
            ValueError: If the space is not appropriate for the limiter.
        """

        self.space = space
        mesh = space.mesh()

        # check that space is DG1
        degree = space.ufl_element().degree()
        if (space.ufl_element().sobolev_space().name != 'L2'
            or ((type(degree) is tuple and np.any([deg != 1 for deg in degree]))
                and degree != 1)):
            raise ValueError('DG1 limiter can only be applied to DG1 space')

        # Create equispaced DG1 space needed for limiting
        if space.extruded:
            cell = mesh._base_mesh.ufl_cell().cellname()
            DG1_hori_elt = FiniteElement("DG", cell, 1, variant="equispaced")
            DG1_vert_elt = FiniteElement("DG", interval, 1, variant="equispaced")
            DG1_element = TensorProductElement(DG1_hori_elt, DG1_vert_elt)
        else:
            cell = mesh.ufl_cell().cellname()
            DG1_element = FiniteElement("DG", cell, 1, variant="equispaced")

        DG1_equispaced = FunctionSpace(mesh, DG1_element)

        self.vertex_limiter = VertexBasedLimiter(DG1_equispaced)
        self.field_equispaced = Function(DG1_equispaced)

    def apply(self, field):
        """
        The application of the limiter to the field.

        Args:
            field (:class:`Function`): the field to apply the limiter to.

        Raises:
             AssertionError: If the field is not in the correct space.
         """
         assert field.function_space() == self.space, \
             "Given field does not belong to this object's function space"

         # Obtain field in equispaced DG space
         self.field_equispaced.interpolate(field)
         # Use vertex based limiter on DG1 field
         self.vertex_limiter.apply(self.field_equispaced)
         # Return to original space
         field.interpolate(self.field_equispaced)


class ThetaLimiter(object):
    """
    A vertex-based limiter for the degree 1 temperature space.
    A vertex based limiter for fields in the DG1xCG2 space, i.e. temperature
    variables in the next-to-lowest order set of spaces. This acts like the
    vertex-based limiter implemented in Firedrake, but in addition corrects
    the central nodes to prevent new maxima or minima forming.
    """

    def __init__(self, space):
        """
        Args:
            space (:class:`FunctionSpace`): the space in which the transported
                variables lies. It should be a form of the DG1xCG2 space.
{'fraction_non_alphanumeric': 0.0768894076451707, 'fraction_numerical': 0.008899912459877445, 'mean_word_length': 2.687466379774072, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3258747', 'n_tokens_mistral': 1965, 'n_tokens_neox': 1834, 'n_words': 680}