Example #1
0
 def build_hdiv_space(self, family, degree):
     if self.extruded_mesh:
         if not self._initialised_base_spaces:
             self.build_base_spaces(family, degree)
         Vh_elt = HDiv(TensorProductElement(self.S1, self.T1))
         Vt_elt = TensorProductElement(self.S2, self.T0)
         Vv_elt = HDiv(Vt_elt)
         V_elt = Vh_elt + Vv_elt
     else:
         cell = self.mesh.ufl_cell().cellname()
         V_elt = FiniteElement(family, cell, degree + 1)
     return FunctionSpace(self.mesh, V_elt, name='HDiv')
Example #2
0
def test_physics_recovery_kernels(boundary):

    m = IntervalMesh(3, 3)
    mesh = ExtrudedMesh(m, layers=3, layer_height=1.0)

    cell = m.ufl_cell().cellname()
    hori_elt = FiniteElement("DG", cell, 0)
    vert_elt = FiniteElement("CG", interval, 1)
    theta_elt = TensorProductElement(hori_elt, vert_elt)
    Vt = FunctionSpace(mesh, theta_elt)
    Vt_brok = FunctionSpace(mesh, BrokenElement(theta_elt))

    initial_field = Function(Vt)
    true_field = Function(Vt_brok)
    new_field = Function(Vt_brok)

    initial_field, true_field = setup_values(boundary, initial_field,
                                             true_field)

    kernel = kernels.PhysicsRecoveryTop(
    ) if boundary == "top" else kernels.PhysicsRecoveryBottom()
    kernel.apply(new_field, initial_field)

    tolerance = 1e-12
    index = 11 if boundary == "top" else 6
    assert abs(true_field.dat.data[index] - new_field.dat.data[index]) < tolerance, \
        "Value at %s from physics recovery is not correct" % boundary
Example #3
0
 def build_theta_space(self, degree):
     assert self.extruded_mesh
     if not self._initialised_base_spaces:
         cell = self.mesh._base_mesh.ufl_cell().cellname()
         self.S2 = FiniteElement("DG", cell, degree)
         self.T0 = FiniteElement("CG", interval, degree + 1)
     V_elt = TensorProductElement(self.S2, self.T0)
     return FunctionSpace(self.mesh, V_elt, name='Vtheta')
Example #4
0
    def _build_spaces(self, mesh, vertical_degree, horizontal_degree, family):
        """
        Build:
        velocity space self.V2,
        pressure space self.V3,
        temperature space self.Vt,
        mixed function space self.W = (V2,V3,Vt)
        """

        self.spaces = SpaceCreator()
        if vertical_degree is not None:
            # horizontal base spaces
            cell = mesh._base_mesh.ufl_cell().cellname()
            S1 = FiniteElement(family, cell, horizontal_degree+1)
            S2 = FiniteElement("DG", cell, horizontal_degree)

            # vertical base spaces
            T0 = FiniteElement("CG", interval, vertical_degree+1)
            T1 = FiniteElement("DG", interval, vertical_degree)

            # build spaces V2, V3, Vt
            V2h_elt = HDiv(TensorProductElement(S1, T1))
            V2t_elt = TensorProductElement(S2, T0)
            V3_elt = TensorProductElement(S2, T1)
            V2v_elt = HDiv(V2t_elt)
            V2_elt = V2h_elt + V2v_elt

            V0 = self.spaces("HDiv", mesh, V2_elt)
            V1 = self.spaces("DG", mesh, V3_elt)
            V2 = self.spaces("HDiv_v", mesh, V2t_elt)

            self.Vv = self.spaces("Vv", mesh, V2v_elt)

            self.W = MixedFunctionSpace((V0, V1, V2))

        else:
            cell = mesh.ufl_cell().cellname()
            V1_elt = FiniteElement(family, cell, horizontal_degree+1)

            V0 = self.spaces("HDiv", mesh, V1_elt)
            V1 = self.spaces("DG", mesh, "DG", horizontal_degree)

            self.W = MixedFunctionSpace((V0, V1))
Example #5
0
    def __init__(self, space):
        """
        Initialise limiter
        :arg space: the space in which the transported variables lies.
                    It should be a form of the DG1xCG2 space.
        """

        if not space.extruded:
            raise ValueError(
                'The Theta Limiter can only be used on an extruded mesh')

        # check that horizontal degree is 1 and vertical degree is 2
        sub_elements = space.ufl_element().sub_elements()
        if (sub_elements[0].family() not in ['Discontinuous Lagrange', 'DQ']
                or sub_elements[1].family() != 'Lagrange'
                or space.ufl_element().degree() != (1, 2)):
            raise ValueError(
                'Theta Limiter should only be used with the DG1xCG2 space')

        # Transport will happen in broken form of Vtheta
        mesh = space.mesh()
        self.Vt_brok = FunctionSpace(mesh, BrokenElement(space.ufl_element()))

        # Create equispaced DG1 space needed for limiting
        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")
        CG2_vert_elt = FiniteElement("CG", interval, 2)
        DG1_element = TensorProductElement(DG1_hori_elt, DG1_vert_elt)
        Vt_element = TensorProductElement(DG1_hori_elt, CG2_vert_elt)
        DG1_equispaced = FunctionSpace(mesh, DG1_element)
        Vt_equispaced = FunctionSpace(mesh, Vt_element)
        Vt_brok_equispaced = FunctionSpace(
            mesh, BrokenElement(Vt_equispaced.ufl_element()))

        self.vertex_limiter = VertexBasedLimiter(DG1_equispaced)
        self.field_hat = Function(Vt_brok_equispaced)
        self.field_old = Function(Vt_brok_equispaced)
        self.field_DG1 = Function(DG1_equispaced)

        self._limit_midpoints_kernel = LimitMidpoints(Vt_brok_equispaced)
Example #6
0
def get_functionspace(mesh,
                      h_family,
                      h_degree,
                      v_family=None,
                      v_degree=None,
                      vector=False,
                      hdiv=False,
                      variant=None,
                      v_variant=None,
                      **kwargs):
    cell_dim = mesh.cell_dimension()
    print(cell_dim)
    assert cell_dim in [2, (2, 1), (1, 1)], 'Unsupported cell dimension'
    hdiv_families = [
        'RT',
        'RTF',
        'RTCF',
        'RAVIART-THOMAS',
        'BDM',
        'BDMF',
        'BDMCF',
        'BREZZI-DOUGLAS-MARINI',
    ]
    if variant is None:
        if h_family.upper() in hdiv_families:
            if h_family in ['RTCF', 'BDMCF']:
                variant = 'equispaced'
            else:
                variant = 'integral'
        else:
            print("var = equi")
            variant = 'equispaced'
    if v_variant is None:
        v_variant = 'equispaced'
    if cell_dim == (2, 1) or (1, 1):
        if v_family is None:
            v_family = h_family
        if v_degree is None:
            v_degree = h_degree
        h_cell, v_cell = mesh.ufl_cell().sub_cells()
        h_elt = FiniteElement(h_family, h_cell, h_degree, variant=variant)
        v_elt = FiniteElement(v_family, v_cell, v_degree, variant=v_variant)
        elt = TensorProductElement(h_elt, v_elt)
        if hdiv:
            elt = ufl.HDiv(elt)
    else:
        elt = FiniteElement(h_family,
                            mesh.ufl_cell(),
                            h_degree,
                            variant=variant)

    constructor = VectorFunctionSpace if vector else FunctionSpace
    return constructor(mesh, elt, **kwargs)
Example #7
0
 def build_dg_space(self, degree, variant=None):
     if self.extruded_mesh:
         if not self._initialised_base_spaces or self.T1.degree(
         ) != degree or self.T1.variant() != variant:
             cell = self.mesh._base_mesh.ufl_cell().cellname()
             S2 = FiniteElement("DG", cell, degree, variant=variant)
             T1 = FiniteElement("DG", interval, degree, variant=variant)
         else:
             S2 = self.S2
             T1 = self.T1
         V_elt = TensorProductElement(S2, T1)
     else:
         cell = self.mesh.ufl_cell().cellname()
         V_elt = FiniteElement("DG", cell, degree, variant=variant)
     name = f'DG{degree}_equispaced' if variant == 'equispaced' else f'DG{degree}'
     return FunctionSpace(self.mesh, V_elt, name=name)
Example #8
0
def correct_eff_coords(eff_coords):
    """
    Correct the effective coordinates calculated by simply averaging
    which will not be correct at periodic boundaries.
    :arg eff_coords: the effective coordinates in vec_DG1 space.
    """

    mesh = eff_coords.function_space().mesh()
    vec_CG1 = VectorFunctionSpace(mesh, "CG", 1)

    if vec_CG1.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")

    vec_DG1 = VectorFunctionSpace(mesh, DG1_element)

    x = SpatialCoordinate(mesh)

    if eff_coords.function_space() != vec_DG1:
        raise ValueError('eff_coords needs to be in the vector DG1 space')

    # obtain different coords in DG1
    DG1_coords = Function(vec_DG1).interpolate(x)
    CG1_coords_from_DG1 = Function(vec_CG1)
    averager = Averager(DG1_coords, CG1_coords_from_DG1)
    averager.project()
    DG1_coords_from_averaged_CG1 = Function(vec_DG1).interpolate(
        CG1_coords_from_DG1)
    DG1_coords_diff = Function(vec_DG1).interpolate(
        DG1_coords - DG1_coords_from_averaged_CG1)

    # interpolate coordinates, adjusting those different coordinates
    adjusted_coords = Function(vec_DG1)
    adjusted_coords.interpolate(eff_coords + DG1_coords_diff)

    return adjusted_coords
Example #9
0
    def __init__(self,
                 v_CG1,
                 v_DG1,
                 method=Boundary_Method.physics,
                 coords_to_adjust=None):

        self.v_DG1 = v_DG1
        self.v_CG1 = v_CG1
        self.v_DG1_old = Function(v_DG1.function_space())
        self.coords_to_adjust = coords_to_adjust

        self.method = method
        mesh = v_CG1.function_space().mesh()
        VDG0 = FunctionSpace(mesh, "DG", 0)
        VCG1 = FunctionSpace(mesh, "CG", 1)

        if VDG0.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")
        VDG1 = FunctionSpace(mesh, DG1_element)

        self.num_ext = Function(VDG0)

        # check function spaces of functions
        if self.method == Boundary_Method.dynamics:
            if v_CG1.function_space() != VCG1:
                raise NotImplementedError(
                    "This boundary recovery method requires v1 to be in CG1.")
            if v_DG1.function_space() != VDG1:
                raise NotImplementedError(
                    "This boundary recovery method requires v_out to be in DG1."
                )
            # check whether mesh is valid
            if mesh.topological_dimension() == 2:
                # if mesh is extruded then we're fine, but if not needs to be quads
                if not VDG0.extruded and mesh.ufl_cell().cellname(
                ) != 'quadrilateral':
                    raise NotImplementedError(
                        'For 2D meshes this recovery method requires that elements are quadrilaterals'
                    )
            elif mesh.topological_dimension() == 3:
                # assume that 3D mesh is extruded
                if mesh._base_mesh.ufl_cell().cellname() != 'quadrilateral':
                    raise NotImplementedError(
                        'For 3D extruded meshes this recovery method requires a base mesh with quadrilateral elements'
                    )
            elif mesh.topological_dimension() != 1:
                raise NotImplementedError(
                    'This boundary recovery is implemented only on certain classes of mesh.'
                )
            if coords_to_adjust is None:
                raise ValueError(
                    'Need coords_to_adjust field for dynamics boundary methods'
                )

        elif self.method == Boundary_Method.physics:
            # check that mesh is valid -- must be an extruded mesh
            if not VDG0.extruded:
                raise NotImplementedError(
                    'The physics boundary method only works on extruded meshes'
                )
            # base spaces
            cell = mesh._base_mesh.ufl_cell().cellname()
            w_hori = FiniteElement("DG", cell, 0, variant="equispaced")
            w_vert = FiniteElement("CG", interval, 1, variant="equispaced")
            # build element
            theta_element = TensorProductElement(w_hori, w_vert)
            # spaces
            Vtheta = FunctionSpace(mesh, theta_element)
            Vtheta_broken = FunctionSpace(mesh, BrokenElement(theta_element))
            if v_CG1.function_space() != Vtheta:
                raise ValueError(
                    "This boundary recovery method requires v_CG1 to be in DG0xCG1 TensorProductSpace."
                )
            if v_DG1.function_space() != Vtheta_broken:
                raise ValueError(
                    "This boundary recovery method requires v_DG1 to be in the broken DG0xCG1 TensorProductSpace."
                )
        else:
            raise ValueError(
                "Boundary method should be a Boundary Method Enum object.")

        VuDG1 = VectorFunctionSpace(VDG0.mesh(), DG1_element)
        x = SpatialCoordinate(VDG0.mesh())
        self.interpolator = Interpolator(self.v_CG1, self.v_DG1)

        if self.method == Boundary_Method.dynamics:

            # STRATEGY
            # obtain a coordinate field for all the nodes
            self.act_coords = Function(VuDG1).project(x)  # actual coordinates
            self.eff_coords = Function(VuDG1).project(
                x)  # effective coordinates
            self.output = Function(VDG1)

            shapes = {
                "nDOFs":
                self.v_DG1.function_space().finat_element.space_dimension(),
                "dim":
                np.prod(VuDG1.shape, dtype=int)
            }

            num_ext_domain = ("{{[i]: 0 <= i < {nDOFs}}}").format(**shapes)
            num_ext_instructions = ("""
            <float64> SUM_EXT = 0
            for i
                SUM_EXT = SUM_EXT + EXT_V1[i]
            end

            NUM_EXT[0] = SUM_EXT
            """)

            coords_domain = ("{{[i, j, k, ii, jj, kk, ll, mm, iii, kkk]: "
                             "0 <= i < {nDOFs} and "
                             "0 <= j < {nDOFs} and 0 <= k < {dim} and "
                             "0 <= ii < {nDOFs} and 0 <= jj < {nDOFs} and "
                             "0 <= kk < {dim} and 0 <= ll < {dim} and "
                             "0 <= mm < {dim} and 0 <= iii < {nDOFs} and "
                             "0 <= kkk < {dim}}}").format(**shapes)
            coords_insts = (
                """
                            <float64> sum_V1_ext = 0
                            <int> index = 100
                            <float64> dist = 0.0
                            <float64> max_dist = 0.0
                            <float64> min_dist = 0.0
                            """

                # only do adjustment in cells with at least one DOF to adjust
                """
                            if NUM_EXT[0] > 0
                            """

                # find the maximum distance between DOFs in this cell, to serve as starting point for finding min distances
                """
                                for i
                                    for j
                                        dist = 0.0
                                        for k
                                            dist = dist + pow(ACT_COORDS[i,k] - ACT_COORDS[j,k], 2.0)
                                        end
                                        dist = pow(dist, 0.5) {{id=sqrt_max_dist, dep=*}}
                                        max_dist = fmax(dist, max_dist) {{id=max_dist, dep=sqrt_max_dist}}
                                    end
                                end
                            """

                # loop through cells and find which ones to adjust
                """
                                for ii
                                    if EXT_V1[ii] > 0.5
                            """

                # find closest interior node
                """
                                        min_dist = max_dist
                                        index = 100
                                        for jj
                                            if EXT_V1[jj] < 0.5
                                                dist = 0.0
                                                for kk
                                                    dist = dist + pow(ACT_COORDS[ii,kk] - ACT_COORDS[jj,kk], 2)
                                                end
                                                dist = pow(dist, 0.5)
                                                if dist <= min_dist
                                                    index = jj
                                                end
                                                min_dist = fmin(min_dist, dist)
                                                for ll
                                                    EFF_COORDS[ii,ll] = 0.5 * (ACT_COORDS[ii,ll] + ACT_COORDS[index,ll])
                                                end
                                            end
                                        end
                                    else
                            """

                # for DOFs that aren't exterior, use the original coordinates
                """
                                        for mm
                                            EFF_COORDS[ii, mm] = ACT_COORDS[ii, mm]
                                        end
                                    end
                                end
                            else
                            """

                # for interior elements, just use the original coordinates
                """
                                for iii
                                    for kkk
                                        EFF_COORDS[iii, kkk] = ACT_COORDS[iii, kkk]
                                    end
                                end
                            end
                            """).format(**shapes)

            _num_ext_kernel = (num_ext_domain, num_ext_instructions)
            _eff_coords_kernel = (coords_domain, coords_insts)
            self.gaussian_elimination_kernel = kernels.GaussianElimination(
                VDG1)

            # find number of external DOFs per cell
            par_loop(_num_ext_kernel,
                     dx, {
                         "NUM_EXT": (self.num_ext, WRITE),
                         "EXT_V1": (self.coords_to_adjust, READ)
                     },
                     is_loopy_kernel=True)

            # find effective coordinates
            logger.warning(
                'Finding effective coordinates for boundary recovery. This could give unexpected results for deformed meshes over very steep topography.'
            )
            par_loop(_eff_coords_kernel,
                     dx, {
                         "EFF_COORDS": (self.eff_coords, WRITE),
                         "ACT_COORDS": (self.act_coords, READ),
                         "NUM_EXT": (self.num_ext, READ),
                         "EXT_V1": (self.coords_to_adjust, READ)
                     },
                     is_loopy_kernel=True)

        elif self.method == Boundary_Method.physics:

            self.bottom_kernel = kernels.PhysicsRecoveryBottom()
            self.top_kernel = kernels.PhysicsRecoveryTop()
Example #10
0
    def __init__(self,
                 v_CG1,
                 v_DG1,
                 method=Boundary_Method.physics,
                 eff_coords=None):

        self.v_DG1 = v_DG1
        self.v_CG1 = v_CG1
        self.v_DG1_old = Function(v_DG1.function_space())
        self.eff_coords = eff_coords

        self.method = method
        mesh = v_CG1.function_space().mesh()
        DG0 = FunctionSpace(mesh, "DG", 0)
        CG1 = FunctionSpace(mesh, "CG", 1)

        if DG0.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 = FunctionSpace(mesh, DG1_element)

        self.num_ext = find_domain_boundaries(mesh)

        # check function spaces of functions
        if self.method == Boundary_Method.dynamics:
            if v_CG1.function_space() != CG1:
                raise NotImplementedError(
                    "This boundary recovery method requires v1 to be in CG1.")
            if v_DG1.function_space() != DG1:
                raise NotImplementedError(
                    "This boundary recovery method requires v_out to be in DG1."
                )
            if eff_coords is None:
                raise ValueError(
                    'Need eff_coords field for dynamics boundary methods')

        elif self.method == Boundary_Method.physics:
            # check that mesh is valid -- must be an extruded mesh
            if not DG0.extruded:
                raise NotImplementedError(
                    'The physics boundary method only works on extruded meshes'
                )
            # base spaces
            cell = mesh._base_mesh.ufl_cell().cellname()
            w_hori = FiniteElement("DG", cell, 0, variant="equispaced")
            w_vert = FiniteElement("CG", interval, 1, variant="equispaced")
            # build element
            theta_element = TensorProductElement(w_hori, w_vert)
            # spaces
            Vtheta = FunctionSpace(mesh, theta_element)
            Vtheta_broken = FunctionSpace(mesh, BrokenElement(theta_element))
            if v_CG1.function_space() != Vtheta:
                raise ValueError(
                    "This boundary recovery method requires v_CG1 to be in DG0xCG1 TensorProductSpace."
                )
            if v_DG1.function_space() != Vtheta_broken:
                raise ValueError(
                    "This boundary recovery method requires v_DG1 to be in the broken DG0xCG1 TensorProductSpace."
                )
        else:
            raise ValueError(
                "Boundary method should be a Boundary Method Enum object.")

        vec_DG1 = VectorFunctionSpace(DG0.mesh(), DG1_element)
        x = SpatialCoordinate(DG0.mesh())
        self.interpolator = Interpolator(self.v_CG1, self.v_DG1)

        if self.method == Boundary_Method.dynamics:

            # STRATEGY
            # obtain a coordinate field for all the nodes
            self.act_coords = Function(vec_DG1).project(
                x)  # actual coordinates
            self.eff_coords = eff_coords  # effective coordinates
            self.output = Function(DG1)
            self.on_exterior = find_domain_boundaries(mesh)

            self.gaussian_elimination_kernel = kernels.GaussianElimination(DG1)

        elif self.method == Boundary_Method.physics:

            self.bottom_kernel = kernels.PhysicsRecoveryBottom()
            self.top_kernel = kernels.PhysicsRecoveryTop()
Example #11
0
def get_latlon_mesh(mesh):
    coords_orig = mesh.coordinates
    coords_fs = coords_orig.function_space()

    if coords_fs.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_elt = TensorProductElement(DG1_hori_elt, DG1_vert_elt)
    else:
        cell = mesh.ufl_cell().cellname()
        DG1_elt = FiniteElement("DG", cell, 1, variant="equispaced")
    vec_DG1 = VectorFunctionSpace(mesh, DG1_elt)
    coords_dg = Function(vec_DG1).interpolate(coords_orig)
    coords_latlon = Function(vec_DG1)
    shapes = {"nDOFs": vec_DG1.finat_element.space_dimension(), 'dim': 3}

    radius = np.min(
        np.sqrt(coords_dg.dat.data[:, 0]**2 + coords_dg.dat.data[:, 1]**2 +
                coords_dg.dat.data[:, 2]**2))
    # lat-lon 'x' = atan2(y, x)
    coords_latlon.dat.data[:, 0] = np.arctan2(coords_dg.dat.data[:, 1],
                                              coords_dg.dat.data[:, 0])
    # lat-lon 'y' = asin(z/sqrt(x^2 + y^2 + z^2))
    coords_latlon.dat.data[:, 1] = np.arcsin(
        coords_dg.dat.data[:, 2] /
        np.sqrt(coords_dg.dat.data[:, 0]**2 + coords_dg.dat.data[:, 1]**2 +
                coords_dg.dat.data[:, 2]**2))
    # our vertical coordinate is radius - the minimum radius
    coords_latlon.dat.data[:,
                           2] = np.sqrt(coords_dg.dat.data[:, 0]**2 +
                                        coords_dg.dat.data[:, 1]**2 +
                                        coords_dg.dat.data[:, 2]**2) - radius

    # We need to ensure that all points in a cell are on the same side of the branch cut in longitude coords
    # This kernel amends the longitude coords so that all longitudes in one cell are close together
    kernel = op2.Kernel(
        """
#define PI 3.141592653589793
#define TWO_PI 6.283185307179586
void splat_coords(double *coords) {{
    double max_diff = 0.0;
    double diff = 0.0;

    for (int i=0; i<{nDOFs}; i++) {{
        for (int j=0; j<{nDOFs}; j++) {{
            diff = coords[i*{dim}] - coords[j*{dim}];
            if (fabs(diff) > max_diff) {{
                max_diff = diff;
            }}
        }}
    }}

    if (max_diff > PI) {{
        for (int i=0; i<{nDOFs}; i++) {{
            if (coords[i*{dim}] < 0) {{
                coords[i*{dim}] += TWO_PI;
            }}
        }}
    }}
}}
""".format(**shapes), "splat_coords")

    op2.par_loop(kernel, coords_latlon.cell_set,
                 coords_latlon.dat(op2.RW, coords_latlon.cell_node_map()))
    return Mesh(coords_latlon)
Example #12
0
def find_eff_coords(V0):
    """
    Takes a function in a field V0 and returns the effective coordinates,
    in a vector DG1 space, of a recovery into a CG1 field. This is for use with the
    Boundary_Recoverer, as it facilitates the Gaussian elimination used to get
    second-order recovery at boundaries.
    If V0 is a vector function space, this returns an array of coordinates for
    each component.
    :arg V0: the original function space.
    """

    mesh = V0.mesh()
    if V0.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")

    vec_CG1 = VectorFunctionSpace(mesh, "CG", 1)
    vec_DG1 = VectorFunctionSpace(mesh, DG1_element)
    x = SpatialCoordinate(mesh)

    if V0.ufl_element().value_size() > 1:
        eff_coords_list = []
        V0_coords_list = []

        # treat this separately for each component
        for i in range(V0.ufl_element().value_size()):
            # fill an d-dimensional list with i-th coordinate
            x_list = [x[i] for j in range(V0.ufl_element().value_size())]

            # the i-th element in V0_coords_list is a vector with all components the i-th coord
            ith_V0_coords = Function(V0).project(as_vector(x_list))
            V0_coords_list.append(ith_V0_coords)

        for i in range(V0.ufl_element().value_size()):
            # slice through V0_coords_list to obtain the coords of the DOFs for that component
            x_list = [V0_coords[i] for V0_coords in V0_coords_list]

            # average these to find effective coords in CG1
            V0_coords_in_DG1 = Function(vec_DG1).interpolate(as_vector(x_list))
            eff_coords_in_CG1 = Function(vec_CG1)
            eff_coords_averager = Averager(V0_coords_in_DG1, eff_coords_in_CG1)
            eff_coords_averager.project()

            # obtain these in DG1
            eff_coords_in_DG1 = Function(vec_DG1).interpolate(
                eff_coords_in_CG1)
            eff_coords_list.append(correct_eff_coords(eff_coords_in_DG1))

        return eff_coords_list

    else:
        # find the coordinates at DOFs in V0
        vec_V0 = VectorFunctionSpace(mesh, V0.ufl_element())
        V0_coords = Function(vec_V0).project(x)

        # average these to find effective coords in CG1
        V0_coords_in_DG1 = Function(vec_DG1).interpolate(V0_coords)
        eff_coords_in_CG1 = Function(vec_CG1)
        eff_coords_averager = Averager(V0_coords_in_DG1, eff_coords_in_CG1)
        eff_coords_averager.project()

        # obtain these in DG1
        eff_coords_in_DG1 = Function(vec_DG1).interpolate(eff_coords_in_CG1)

        return correct_eff_coords(eff_coords_in_DG1)
def test_3D_cartesian_recovery(geometry, element, mesh, expr):

    family = "RTCF" if element == "quadrilateral" else "BDM"

    # horizontal base spaces
    cell = mesh._base_mesh.ufl_cell().cellname()
    u_hori = FiniteElement(family, cell, 1)
    w_hori = FiniteElement("DG", cell, 0)

    # vertical base spaces
    u_vert = FiniteElement("DG", interval, 0)
    w_vert = FiniteElement("CG", interval, 1)

    # build elements
    u_element = HDiv(TensorProductElement(u_hori, u_vert))
    w_element = HDiv(TensorProductElement(w_hori, w_vert))
    theta_element = TensorProductElement(w_hori, w_vert)
    v_element = u_element + w_element

    # DG1
    DG1_hori = FiniteElement("DG", cell, 1, variant="equispaced")
    DG1_vert = FiniteElement("DG", interval, 1, variant="equispaced")
    DG1_elt = TensorProductElement(DG1_hori, DG1_vert)
    DG1 = FunctionSpace(mesh, DG1_elt)
    vec_DG1 = VectorFunctionSpace(mesh, DG1_elt)

    # spaces
    DG0 = FunctionSpace(mesh, "DG", 0)
    CG1 = FunctionSpace(mesh, "CG", 1)
    Vt = FunctionSpace(mesh, theta_element)
    Vt_brok = FunctionSpace(mesh, BrokenElement(theta_element))
    Vu = FunctionSpace(mesh, v_element)
    vec_CG1 = VectorFunctionSpace(mesh, "CG", 1)

    # our actual theta and rho and v
    rho_CG1_true = Function(CG1).interpolate(expr)
    theta_CG1_true = Function(CG1).interpolate(expr)
    v_CG1_true = Function(vec_CG1).interpolate(as_vector([expr, expr, expr]))
    rho_Vt_true = Function(Vt).interpolate(expr)

    # make the initial fields by projecting expressions into the lowest order spaces
    rho_DG0 = Function(DG0).interpolate(expr)
    rho_CG1 = Function(CG1)
    theta_Vt = Function(Vt).interpolate(expr)
    theta_CG1 = Function(CG1)
    v_Vu = Function(Vu).project(as_vector([expr, expr, expr]))
    v_CG1 = Function(vec_CG1)
    rho_Vt = Function(Vt)

    # make the recoverers and do the recovery
    rho_recoverer = Recoverer(rho_DG0, rho_CG1, VDG=DG1, boundary_method=Boundary_Method.dynamics)
    theta_recoverer = Recoverer(theta_Vt, theta_CG1, VDG=DG1, boundary_method=Boundary_Method.dynamics)
    v_recoverer = Recoverer(v_Vu, v_CG1, VDG=vec_DG1, boundary_method=Boundary_Method.dynamics)
    rho_Vt_recoverer = Recoverer(rho_DG0, rho_Vt, VDG=Vt_brok, boundary_method=Boundary_Method.physics)

    rho_recoverer.project()
    theta_recoverer.project()
    v_recoverer.project()
    rho_Vt_recoverer.project()

    rho_diff = errornorm(rho_CG1, rho_CG1_true) / norm(rho_CG1_true)
    theta_diff = errornorm(theta_CG1, theta_CG1_true) / norm(theta_CG1_true)
    v_diff = errornorm(v_CG1, v_CG1_true) / norm(v_CG1_true)
    rho_Vt_diff = errornorm(rho_Vt, rho_Vt_true) / norm(rho_Vt_true)

    tolerance = 1e-7
    error_message = ("""
                     Incorrect recovery for {variable} with {boundary} boundary method
                     on {geometry} 3D Cartesian domain with {element} elements
                     """)
    assert rho_diff < tolerance, error_message.format(variable='rho', boundary='dynamics',
                                                      geometry=geometry, element=element)
    assert v_diff < tolerance, error_message.format(variable='v', boundary='dynamics',
                                                    geometry=geometry, element=element)
    assert theta_diff < tolerance, error_message.format(variable='rho', boundary='dynamics',
                                                        geometry=geometry, element=element)
    assert rho_Vt_diff < tolerance, error_message.format(variable='rho', boundary='physics',
                                                         geometry=geometry, element=element)
def setup_3d_recovery(dirname):

    L = 100.
    H = 10.
    W = 1.

    deltax = L / 5.
    deltay = W / 5.
    deltaz = H / 5.
    nlayers = int(H / deltaz)
    ncolumnsx = int(L / deltax)
    ncolumnsy = int(W / deltay)

    m = RectangleMesh(ncolumnsx, ncolumnsy, L, W, quadrilateral=True)
    mesh = ExtrudedMesh(m, layers=nlayers, layer_height=H / nlayers)
    x, y, z = SpatialCoordinate(mesh)

    # horizontal base spaces
    cell = mesh._base_mesh.ufl_cell().cellname()
    u_hori = FiniteElement("RTCF", cell, 1)
    w_hori = FiniteElement("DG", cell, 0)

    # vertical base spaces
    u_vert = FiniteElement("DG", interval, 0)
    w_vert = FiniteElement("CG", interval, 1)

    # build elements
    u_element = HDiv(TensorProductElement(u_hori, u_vert))
    w_element = HDiv(TensorProductElement(w_hori, w_vert))
    theta_element = TensorProductElement(w_hori, w_vert)
    v_element = u_element + w_element

    # spaces
    VDG0 = FunctionSpace(mesh, "DG", 0)
    VCG1 = FunctionSpace(mesh, "CG", 1)
    VDG1 = FunctionSpace(mesh, "DG", 1)
    Vt = FunctionSpace(mesh, theta_element)
    Vt_brok = FunctionSpace(mesh, BrokenElement(theta_element))
    Vu = FunctionSpace(mesh, v_element)
    VuCG1 = VectorFunctionSpace(mesh, "CG", 1)
    VuDG1 = VectorFunctionSpace(mesh, "DG", 1)

    # set up initial conditions
    np.random.seed(0)
    expr = np.random.randn(
    ) + np.random.randn() * x + np.random.randn() * y + np.random.randn(
    ) * z + np.random.randn() * x * y + np.random.randn(
    ) * x * z + np.random.randn() * y * z + np.random.randn() * x * y * z

    # our actual theta and rho and v
    rho_CG1_true = Function(VCG1).interpolate(expr)
    theta_CG1_true = Function(VCG1).interpolate(expr)
    v_CG1_true = Function(VuCG1).interpolate(as_vector([expr, expr, expr]))
    rho_Vt_true = Function(Vt).interpolate(expr)

    # make the initial fields by projecting expressions into the lowest order spaces
    rho_DG0 = Function(VDG0).interpolate(expr)
    rho_CG1 = Function(VCG1)
    theta_Vt = Function(Vt).interpolate(expr)
    theta_CG1 = Function(VCG1)
    v_Vu = Function(Vu).project(as_vector([expr, expr, expr]))
    v_CG1 = Function(VuCG1)
    rho_Vt = Function(Vt)

    # make the recoverers and do the recovery
    rho_recoverer = Recoverer(rho_DG0,
                              rho_CG1,
                              VDG=VDG1,
                              boundary_method=Boundary_Method.dynamics)
    theta_recoverer = Recoverer(theta_Vt,
                                theta_CG1,
                                VDG=VDG1,
                                boundary_method=Boundary_Method.dynamics)
    v_recoverer = Recoverer(v_Vu,
                            v_CG1,
                            VDG=VuDG1,
                            boundary_method=Boundary_Method.dynamics)
    rho_Vt_recoverer = Recoverer(rho_DG0,
                                 rho_Vt,
                                 VDG=Vt_brok,
                                 boundary_method=Boundary_Method.physics)

    rho_recoverer.project()
    theta_recoverer.project()
    v_recoverer.project()
    rho_Vt_recoverer.project()

    rho_diff = errornorm(rho_CG1, rho_CG1_true) / norm(rho_CG1_true)
    theta_diff = errornorm(theta_CG1, theta_CG1_true) / norm(theta_CG1_true)
    v_diff = errornorm(v_CG1, v_CG1_true) / norm(v_CG1_true)
    rho_Vt_diff = errornorm(rho_Vt, rho_Vt_true) / norm(rho_Vt_true)

    return (rho_diff, theta_diff, v_diff, rho_Vt_diff)
Example #15
0
def find_coords_to_adjust(V0, DG1):
    """
    This function finds the coordinates that need to be adjusted
    for the recovery at the boundary. These are assigned by a 1,
    while all coordinates to be left unchanged are assigned a 0.
    This field is returned as a DG1 field.
    Fields can be scalar or vector.

    :arg V0: the space of the original field (before recovery).
    :arg DG1: a DG1 space, in which the boundary recovery will happen.
    """

    # check that spaces are correct
    mesh = DG1.mesh()
    if DG1.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")
    scalar_DG1 = FunctionSpace(mesh, DG1_element)
    vector_DG1 = VectorFunctionSpace(mesh, DG1_element)

    # check DG1 field is correct
    if type(DG1.ufl_element()) == VectorElement:
        if DG1 != vector_DG1:
            raise ValueError(
                'The function space entered as vector DG1 is not vector DG1.')
    elif DG1 != scalar_DG1:
        raise ValueError('The function space entered as DG1 is not DG1.')

    # STRATEGY
    # We need to pass the boundary recoverer a field denoting the location
    # of nodes on the boundary, which denotes the coordinates to adjust to be new effective
    # coords. This field will be 1 for these coords and 0 otherwise.
    # How do we do this?
    # 1. Obtain a DG1 field which is 1 at all exterior DOFs by applying Dirichlet
    #    boundary conditions. i.e. for cells in the bottom right corner of a domain:
    #    ------- 0 ------- 0 ------- 1
    #            |         |         ||
    #            |         |         ||
    #            |         |         ||
    #    ======= 1 ======= 1 ======= 1
    # 2. Obtain a field in DG1 that is 1 at exterior DOFs adjacent to the exterior
    #    DOFs of V0 (i.e. the original space). For V0=DG0 there will be no exterior
    #    DOFs, but could be if velocity is in RT or if there is a temperature space.
    #    This is done by applying topological boundary conditions to a field in V0,
    #    before interpolating these into DG1.
    #    For instance, marking V0 DOFs with x, for rho and theta spaces this would give
    #    ------- 0 ------- 0 ------- 0          ---x--- 0 ---x--- 0 ---x--- 0
    #            |         |         ||                 |         |         ||
    #       x    |    x    |    x    ||                 |         |         ||
    #            |         |         ||                 |         |         ||
    #    ======= 0 ======= 0 ======= 0          ===x=== 1 ===x=== 1 ===x=== 1
    # 3. Obtain a field that is 1 at corners in 2D or along edges in 3D.
    #    We do this by using that corners in 2D and edges in 3D are intersections
    #    of edges/faces respectively. In 2D, this means that if a field which is 1 on a
    #    horizontal edge is summed with a field that is 1 on a vertical edge, the
    #    corner value will be 2. Subtracting the exterior DG1 field from step 1 leaves
    #    a field that is 1 in the corner. This is generalised to 3D.
    #    ------- 0 ------- 0    ------- 0 ------- 1    ------- 0 ------- 1    ------- 0 ------- 0
    #            |         ||           |         ||           |         ||            |         ||
    #            |         ||  +        |         ||  -        |         ||  =         |         ||
    #            |         ||           |         ||           |         ||            |         ||
    #    ======= 1 ======= 1    ======= 0 ======= 1    ======= 1 ======= 1     ======= 0 ======= 1
    # 4. The field of coords to be adjusted is then found by the following formula:
    #                            f1 + f3 - f2
    #    where f1, f2 and f3 are the DG1 fields obtained from steps 1, 2 and 3.

    # make DG1 field with 1 at all exterior coords
    all_ext_in_DG1 = Function(DG1)
    bcs = [DirichletBC(DG1, Constant(1.0), "on_boundary", method="geometric")]

    if DG1.extruded:
        bcs.append(DirichletBC(DG1, Constant(1.0), "top", method="geometric"))
        bcs.append(
            DirichletBC(DG1, Constant(1.0), "bottom", method="geometric"))

    for bc in bcs:
        bc.apply(all_ext_in_DG1)

    # make DG1 field with 1 at coords surrounding exterior coords of V0
    # first do topological BCs to get V0 function which is 1 at DOFs on edges
    all_ext_in_V0 = Function(V0)
    bcs = [DirichletBC(V0, Constant(1.0), "on_boundary", method="topological")]

    if V0.extruded:
        bcs.append(DirichletBC(V0, Constant(1.0), "top", method="topological"))
        bcs.append(
            DirichletBC(V0, Constant(1.0), "bottom", method="topological"))

    for bc in bcs:
        bc.apply(all_ext_in_V0)

    if DG1.value_size > 1:
        # for vector valued functions, DOFs aren't pointwise evaluation. We break into components and use a conditional interpolation to get values of 1
        V0_ext_in_DG1_components = []
        for i in range(DG1.value_size):
            V0_ext_in_DG1_components.append(
                Function(scalar_DG1).interpolate(
                    conditional(abs(all_ext_in_V0[i]) > 0.0, 1.0, 0.0)))
        V0_ext_in_DG1 = Function(DG1).project(
            as_vector(V0_ext_in_DG1_components))
    else:
        # for scalar functions (where DOFs are pointwise evaluation) we can simply interpolate to get these values
        V0_ext_in_DG1 = Function(DG1).interpolate(all_ext_in_V0)

    corners_in_DG1 = Function(DG1)
    if DG1.mesh().topological_dimension() == 2:
        if DG1.extruded:
            DG1_ext_hori = Function(DG1)
            DG1_ext_vert = Function(DG1)
            hori_bcs = [
                DirichletBC(DG1, Constant(1.0), "top", method="geometric"),
                DirichletBC(DG1, Constant(1.0), "bottom", method="geometric")
            ]
            vert_bc = DirichletBC(DG1,
                                  Constant(1.0),
                                  "on_boundary",
                                  method="geometric")
            for bc in hori_bcs:
                bc.apply(DG1_ext_hori)

            vert_bc.apply(DG1_ext_vert)
            corners_in_DG1.assign(DG1_ext_hori + DG1_ext_vert - all_ext_in_DG1)

        else:
            # we don't know whether its periodic or in how many directions
            DG1_ext_x = Function(DG1)
            DG1_ext_y = Function(DG1)
            x_bcs = [
                DirichletBC(DG1, Constant(1.0), 1, method="geometric"),
                DirichletBC(DG1, Constant(1.0), 2, method="geometric")
            ]
            y_bcs = [
                DirichletBC(DG1, Constant(1.0), 3, method="geometric"),
                DirichletBC(DG1, Constant(1.0), 4, method="geometric")
            ]

            # there is no easy way to know if the mesh is periodic or in which
            # directions, so we must use a try here
            # LookupError is the error for asking for a boundary number that doesn't exist
            try:
                for bc in x_bcs:
                    bc.apply(DG1_ext_x)
            except LookupError:
                pass
            try:
                for bc in y_bcs:
                    bc.apply(DG1_ext_y)
            except LookupError:
                pass

            corners_in_DG1.assign(DG1_ext_x + DG1_ext_y - all_ext_in_DG1)

    elif DG1.mesh().topological_dimension() == 3:
        DG1_vert_x = Function(DG1)
        DG1_vert_y = Function(DG1)
        DG1_hori = Function(DG1)
        x_bcs = [
            DirichletBC(DG1, Constant(1.0), 1, method="geometric"),
            DirichletBC(DG1, Constant(1.0), 2, method="geometric")
        ]
        y_bcs = [
            DirichletBC(DG1, Constant(1.0), 3, method="geometric"),
            DirichletBC(DG1, Constant(1.0), 4, method="geometric")
        ]
        hori_bcs = [
            DirichletBC(DG1, Constant(1.0), "top", method="geometric"),
            DirichletBC(DG1, Constant(1.0), "bottom", method="geometric")
        ]

        # there is no easy way to know if the mesh is periodic or in which
        # directions, so we must use a try here
        # LookupError is the error for asking for a boundary number that doesn't exist
        try:
            for bc in x_bcs:
                bc.apply(DG1_vert_x)
        except LookupError:
            pass

        try:
            for bc in y_bcs:
                bc.apply(DG1_vert_y)
        except LookupError:
            pass

        for bc in hori_bcs:
            bc.apply(DG1_hori)

        corners_in_DG1.assign(DG1_vert_x + DG1_vert_y + DG1_hori -
                              all_ext_in_DG1)

    # we now combine the different functions. We use max_value to avoid getting 2s or 3s at corners/edges
    # we do this component-wise because max_value only works component-wise
    if DG1.value_size > 1:
        coords_to_correct_components = []
        for i in range(DG1.value_size):
            coords_to_correct_components.append(
                Function(scalar_DG1).interpolate(
                    max_value(corners_in_DG1[i],
                              all_ext_in_DG1[i] - V0_ext_in_DG1[i])))
        coords_to_correct = Function(DG1).project(
            as_vector(coords_to_correct_components))
    else:
        coords_to_correct = Function(DG1).interpolate(
            max_value(corners_in_DG1, all_ext_in_DG1 - V0_ext_in_DG1))

    return coords_to_correct
def setup_hori_limiters(dirname):

    # declare grid shape
    L = 400.
    H = L
    ncolumns = int(L / 10.)
    nlayers = ncolumns

    # make mesh
    m = PeriodicIntervalMesh(ncolumns, L)
    mesh = ExtrudedMesh(m, layers=nlayers, layer_height=(H / nlayers))
    x, z = SpatialCoordinate(mesh)

    fieldlist = ['u']
    timestepping = TimesteppingParameters(dt=1.0, maxk=4, maxi=1)
    output = OutputParameters(dirname=dirname + "/limiting_hori",
                              dumpfreq=5,
                              dumplist=['u'],
                              perturbation_fields=['theta0', 'theta1'])
    parameters = CompressibleParameters()

    state = State(mesh,
                  vertical_degree=1,
                  horizontal_degree=1,
                  family="CG",
                  timestepping=timestepping,
                  output=output,
                  parameters=parameters,
                  fieldlist=fieldlist)

    # make elements
    # v is continuous in vertical, h is horizontal
    cell = mesh._base_mesh.ufl_cell().cellname()
    DG0_element = FiniteElement("DG", cell, 0)
    CG1_element = FiniteElement("CG", cell, 1)
    DG1_element = FiniteElement("DG", cell, 1)
    CG2_element = FiniteElement("CG", cell, 2)
    V0_element = TensorProductElement(DG0_element, CG1_element)
    V1_element = TensorProductElement(DG1_element, CG2_element)

    # spaces
    Vpsi = FunctionSpace(mesh, "CG", 2)
    VDG1 = FunctionSpace(mesh, "DG", 1)
    VCG1 = FunctionSpace(mesh, "CG", 1)
    V0 = FunctionSpace(mesh, V0_element)
    V1 = FunctionSpace(mesh, V1_element)

    V0_brok = FunctionSpace(mesh, BrokenElement(V0.ufl_element()))

    V0_spaces = (VDG1, VCG1, V0_brok)

    # declare initial fields
    u0 = state.fields("u")
    theta0 = state.fields("theta0", V0)
    theta1 = state.fields("theta1", V1)

    # make a gradperp
    gradperp = lambda u: as_vector([-u.dx(1), u.dx(0)])

    # Isentropic background state
    Tsurf = 300.
    thetab = Constant(Tsurf)
    theta_b1 = Function(V1).interpolate(thetab)
    theta_b0 = Function(V0).interpolate(thetab)

    # set up bubble
    xc = 200.
    zc = 200.
    rc = 100.
    theta_expr = conditional(
        sqrt((x - xc)**2.0) < rc,
        conditional(sqrt((z - zc)**2.0) < rc, Constant(2.0), Constant(0.0)),
        Constant(0.0))
    theta_pert1 = Function(V1).interpolate(theta_expr)
    theta_pert0 = Function(V0).interpolate(theta_expr)

    # set up velocity field
    u_max = Constant(10.0)

    psi_expr = -u_max * z

    psi0 = Function(Vpsi).interpolate(psi_expr)
    u0.project(gradperp(psi0))
    theta0.interpolate(theta_b0 + theta_pert0)
    theta1.interpolate(theta_b1 + theta_pert1)

    state.initialise([('u', u0), ('theta1', theta1), ('theta0', theta0)])
    state.set_reference_profiles([('theta1', theta_b1), ('theta0', theta_b0)])

    # set up advection schemes
    thetaeqn1 = EmbeddedDGAdvection(state, V1, equation_form="advective")
    thetaeqn0 = EmbeddedDGAdvection(state,
                                    V0,
                                    equation_form="advective",
                                    recovered_spaces=V0_spaces)

    # build advection dictionary
    advected_fields = []
    advected_fields.append(('u', NoAdvection(state, u0, None)))
    advected_fields.append(('theta1',
                            SSPRK3(state,
                                   theta1,
                                   thetaeqn1,
                                   limiter=ThetaLimiter(thetaeqn1))))
    advected_fields.append(('theta0',
                            SSPRK3(state,
                                   theta0,
                                   thetaeqn0,
                                   limiter=VertexBasedLimiter(VDG1))))

    # build time stepper
    stepper = AdvectionDiffusion(state, advected_fields)

    return stepper, 40.0
Example #17
0
def setup_limiters(dirname, direction, grid_params, ic_params):

    # declare grid shape
    L, H, ncolumns, nlayers = grid_params

    # make mesh
    m = PeriodicIntervalMesh(ncolumns, L)
    mesh = ExtrudedMesh(m, layers=nlayers, layer_height=(H / nlayers))
    x, z = SpatialCoordinate(mesh)

    fieldlist = ['u']
    timestepping = TimesteppingParameters(dt=1.0)
    output = OutputParameters(dirname=dirname,
                              dumpfreq=5,
                              dumplist=['u'],
                              perturbation_fields=['theta0', 'theta1'])
    parameters = CompressibleParameters()

    state = State(mesh,
                  vertical_degree=1,
                  horizontal_degree=1,
                  family="CG",
                  timestepping=timestepping,
                  output=output,
                  parameters=parameters,
                  fieldlist=fieldlist)

    # make elements
    # v is continuous in vertical, h is horizontal
    cell = mesh._base_mesh.ufl_cell().cellname()
    DG0_element = FiniteElement("DG", cell, 0)
    CG1_element = FiniteElement("CG", cell, 1)
    DG1_element = FiniteElement("DG", cell, 1)
    CG2_element = FiniteElement("CG", cell, 2)
    V0_element = TensorProductElement(DG0_element, CG1_element)
    V1_element = TensorProductElement(DG1_element, CG2_element)

    # spaces
    VDG1 = FunctionSpace(mesh, "DG", 1)
    VCG1 = FunctionSpace(mesh, "CG", 1)
    V0 = FunctionSpace(mesh, V0_element)
    V1 = FunctionSpace(mesh, V1_element)
    V0_brok = FunctionSpace(mesh, BrokenElement(V0.ufl_element()))

    # declare initial fields
    u0 = state.fields("u")
    theta0 = state.fields("theta0", V0)
    theta1 = state.fields("theta1", V1)
    Tsurf, xc, zc, rc, u_max = ic_params

    # Isentropic background state
    thetab = Constant(Tsurf)
    theta_b1 = Function(V1).interpolate(thetab)
    theta_b0 = Function(V0).interpolate(thetab)

    # set up bubble
    theta_expr = conditional(
        sqrt((x - xc)**2.0) < rc,
        conditional(sqrt((z - zc)**2.0) < rc, Constant(2.0), Constant(0.0)),
        Constant(0.0))
    theta_pert1 = Function(V1).interpolate(theta_expr)
    theta_pert0 = Function(V0).interpolate(theta_expr)

    if direction == "horizontal":
        Vpsi = FunctionSpace(mesh, "CG", 2)
        psi_expr = -u_max * z
        psi0 = Function(Vpsi).interpolate(psi_expr)
        gradperp = lambda u: as_vector([-u.dx(1), u.dx(0)])
        u0.project(gradperp(psi0))
    elif direction == "vertical":
        u0.project(as_vector([0, -u_max]))
    theta0.interpolate(theta_b0 + theta_pert0)
    theta1.interpolate(theta_b1 + theta_pert1)

    state.initialise([('u', u0), ('theta1', theta1), ('theta0', theta0)])
    state.set_reference_profiles([('theta1', theta_b1), ('theta0', theta_b0)])

    # set up advection schemes
    dg_opts = EmbeddedDGOptions()
    recovered_opts = RecoveredOptions(embedding_space=VDG1,
                                      recovered_space=VCG1,
                                      broken_space=V0_brok)
    thetaeqn1 = EmbeddedDGAdvection(state,
                                    V1,
                                    equation_form="advective",
                                    options=dg_opts)
    thetaeqn0 = EmbeddedDGAdvection(state,
                                    V0,
                                    equation_form="advective",
                                    options=recovered_opts)

    # build advection dictionary
    advected_fields = []
    advected_fields.append(
        ('theta1', SSPRK3(state, theta1, thetaeqn1, limiter=ThetaLimiter(V1))))
    advected_fields.append(('theta0',
                            SSPRK3(state,
                                   theta0,
                                   thetaeqn0,
                                   limiter=VertexBasedLimiter(VDG1))))

    # build time stepper
    stepper = AdvectionDiffusion(state, advected_fields)

    return stepper, 40.0
Example #18
0
def setup_limiters(dirname):

    dt = 0.01
    Ld = 1.
    tmax = 0.2
    rotations = 0.1
    m = PeriodicIntervalMesh(20, Ld)
    mesh = ExtrudedMesh(m, layers=20, layer_height=(Ld / 20))
    output = OutputParameters(
        dirname=dirname,
        dumpfreq=1,
        dumplist=['u', 'chemical', 'moisture_higher', 'moisture_lower'])
    parameters = CompressibleParameters()
    timestepping = TimesteppingParameters(dt=dt, maxk=4, maxi=1)
    fieldlist = [
        'u', 'rho', 'theta', 'chemical', 'moisture_higher', 'moisture_lower'
    ]
    diagnostic_fields = []
    state = State(mesh,
                  vertical_degree=1,
                  horizontal_degree=1,
                  family="CG",
                  timestepping=timestepping,
                  output=output,
                  parameters=parameters,
                  fieldlist=fieldlist,
                  diagnostic_fields=diagnostic_fields)

    x, z = SpatialCoordinate(mesh)

    Vr = state.spaces("DG")
    Vt = state.spaces("HDiv_v")
    Vpsi = FunctionSpace(mesh, "CG", 2)

    cell = mesh._base_mesh.ufl_cell().cellname()
    DG0_element = FiniteElement("DG", cell, 0)
    CG1_element = FiniteElement("CG", interval, 1)
    Vt0_element = TensorProductElement(DG0_element, CG1_element)
    Vt0 = FunctionSpace(mesh, Vt0_element)
    Vt0_brok = FunctionSpace(mesh, BrokenElement(Vt0_element))
    VCG1 = FunctionSpace(mesh, "CG", 1)

    u = state.fields("u", dump=True)
    chemical = state.fields("chemical", Vr, dump=True)
    moisture_higher = state.fields("moisture_higher", Vt, dump=True)
    moisture_lower = state.fields("moisture_lower", Vt0, dump=True)

    x_lower = 2 * Ld / 5
    x_upper = 3 * Ld / 5
    z_lower = 6 * Ld / 10
    z_upper = 8 * Ld / 10
    bubble_expr_1 = conditional(
        x > x_lower,
        conditional(
            x < x_upper,
            conditional(z > z_lower, conditional(z < z_upper, 1.0, 0.0), 0.0),
            0.0), 0.0)

    bubble_expr_2 = conditional(
        x > z_lower,
        conditional(
            x < z_upper,
            conditional(z > x_lower, conditional(z < x_upper, 1.0, 0.0), 0.0),
            0.0), 0.0)

    chemical.assign(1.0)
    moisture_higher.assign(280.)
    chem_pert_1 = Function(Vr).interpolate(bubble_expr_1)
    chem_pert_2 = Function(Vr).interpolate(bubble_expr_2)
    moist_h_pert_1 = Function(Vt).interpolate(bubble_expr_1)
    moist_h_pert_2 = Function(Vt).interpolate(bubble_expr_2)
    moist_l_pert_1 = Function(Vt0).interpolate(bubble_expr_1)
    moist_l_pert_2 = Function(Vt0).interpolate(bubble_expr_2)

    chemical.assign(chemical + chem_pert_1 + chem_pert_2)
    moisture_higher.assign(moisture_higher + moist_h_pert_1 + moist_h_pert_2)
    moisture_lower.assign(moisture_lower + moist_l_pert_1 + moist_l_pert_2)

    # set up solid body rotation for advection
    # we do this slightly complicated stream function to make the velocity 0 at edges
    # thus we avoid odd effects at boundaries
    xc = Ld / 2
    zc = Ld / 2
    r = sqrt((x - xc)**2 + (z - zc)**2)
    omega = rotations * 2 * pi / tmax
    r_out = 9 * Ld / 20
    r_in = 2 * Ld / 5
    A = omega * r_in / (2 * (r_in - r_out))
    B = -omega * r_in * r_out / (r_in - r_out)
    C = omega * r_in**2 * r_out / (r_in - r_out) / 2
    psi_expr = conditional(
        r < r_in, omega * r**2 / 2,
        conditional(r < r_out, A * r**2 + B * r + C,
                    A * r_out**2 + B * r_out + C))
    psi = Function(Vpsi).interpolate(psi_expr)

    gradperp = lambda v: as_vector([-v.dx(1), v.dx(0)])
    u.project(gradperp(psi))

    state.initialise([('u', u), ('chemical', chemical),
                      ('moisture_higher', moisture_higher),
                      ('moisture_lower', moisture_lower)])

    # set up advection schemes
    dg_opts = EmbeddedDGOptions()
    recovered_opts = RecoveredOptions(embedding_space=Vr,
                                      recovered_space=VCG1,
                                      broken_space=Vt0_brok,
                                      boundary_method=Boundary_Method.dynamics)

    chemeqn = AdvectionEquation(state, Vr, equation_form="advective")
    moisteqn_higher = EmbeddedDGAdvection(state,
                                          Vt,
                                          equation_form="advective",
                                          options=dg_opts)
    moisteqn_lower = EmbeddedDGAdvection(state,
                                         Vt0,
                                         equation_form="advective",
                                         options=recovered_opts)

    # build advection dictionary
    advected_fields = []
    advected_fields.append(('chemical',
                            SSPRK3(state,
                                   chemical,
                                   chemeqn,
                                   limiter=VertexBasedLimiter(Vr))))
    advected_fields.append(('moisture_higher',
                            SSPRK3(state,
                                   moisture_higher,
                                   moisteqn_higher,
                                   limiter=ThetaLimiter(Vt))))
    advected_fields.append(('moisture_lower',
                            SSPRK3(state,
                                   moisture_lower,
                                   moisteqn_lower,
                                   limiter=VertexBasedLimiter(Vr))))

    # build time stepper
    stepper = AdvectionDiffusion(state, advected_fields)

    return stepper, tmax
Example #19
0
    def initialize(self, pc):
        """ Set up the problem context. Takes the original
        mixed problem and transforms it into the equivalent
        hybrid-mixed system.

        A KSP object is created for the Lagrange multipliers
        on the top/bottom faces of the mesh cells.
        """

        from firedrake import (FunctionSpace, Function, Constant,
                               FiniteElement, TensorProductElement,
                               TrialFunction, TrialFunctions, TestFunction,
                               DirichletBC, interval, MixedElement,
                               BrokenElement)
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.formmanipulation import split_form
        from ufl.algorithms.replace import replace
        from ufl.cell import TensorProductCell

        # Extract PC context
        prefix = pc.getOptionsPrefix() + "vert_hybridization_"
        _, P = pc.getOperators()
        self.ctx = P.getPythonContext()

        if not isinstance(self.ctx, ImplicitMatrixContext):
            raise ValueError(
                "The python context must be an ImplicitMatrixContext")

        test, trial = self.ctx.a.arguments()

        V = test.function_space()
        mesh = V.mesh()

        # Magically determine which spaces are vector and scalar valued
        for i, Vi in enumerate(V):

            # Vector-valued spaces will have a non-empty value_shape
            if Vi.ufl_element().value_shape():
                self.vidx = i
            else:
                self.pidx = i

        Vv = V[self.vidx]
        Vp = V[self.pidx]

        # Create the space of approximate traces in the vertical.
        # NOTE: Technically a hack since the resulting space is technically
        # defined in cell interiors, however the degrees of freedom will only
        # be geometrically defined on edges. Arguments will only be used in
        # surface integrals
        deg, _ = Vv.ufl_element().degree()

        # Assumes a tensor product cell (quads, triangular-prisms, cubes)
        if not isinstance(Vp.ufl_element().cell(), TensorProductCell):
            raise NotImplementedError(
                "Currently only implemented for tensor product discretizations"
            )

        # Only want the horizontal cell
        cell, _ = Vp.ufl_element().cell()._cells

        DG = FiniteElement("DG", cell, deg)
        CG = FiniteElement("CG", interval, 1)
        Vv_tr_element = TensorProductElement(DG, CG)
        Vv_tr = FunctionSpace(mesh, Vv_tr_element)

        # Break the spaces
        broken_elements = MixedElement(
            [BrokenElement(Vi.ufl_element()) for Vi in V])
        V_d = FunctionSpace(mesh, broken_elements)

        # Set up relevant functions
        self.broken_solution = Function(V_d)
        self.broken_residual = Function(V_d)
        self.trace_solution = Function(Vv_tr)
        self.unbroken_solution = Function(V)
        self.unbroken_residual = Function(V)

        # Set up transfer kernels to and from the broken velocity space
        # NOTE: Since this snippet of code is used in a couple places in
        # in Gusto, might be worth creating a utility function that is
        # is importable and just called where needed.
        shapes = {
            "i": Vv.finat_element.space_dimension(),
            "j": np.prod(Vv.shape, dtype=int)
        }
        weight_kernel = """
        for (int i=0; i<{i}; ++i)
            for (int j=0; j<{j}; ++j)
                w[i*{j} + j] += 1.0;
        """.format(**shapes)

        self.weight = Function(Vv)
        par_loop(weight_kernel, dx, {"w": (self.weight, INC)})

        # Averaging kernel
        self.average_kernel = """
        for (int i=0; i<{i}; ++i)
            for (int j=0; j<{j}; ++j)
                vec_out[i*{j} + j] += vec_in[i*{j} + j]/w[i*{j} + j];
        """.format(**shapes)
        # Original mixed operator replaced with "broken" arguments
        arg_map = {test: TestFunction(V_d), trial: TrialFunction(V_d)}
        Atilde = Tensor(replace(self.ctx.a, arg_map))
        gammar = TestFunction(Vv_tr)
        n = FacetNormal(mesh)
        sigma = TrialFunctions(V_d)[self.vidx]

        # Again, assumes tensor product structure. Why use this if you
        # don't have some form of vertical extrusion?
        Kform = gammar('+') * jump(sigma, n=n) * dS_h

        # Here we deal with boundary conditions
        if self.ctx.row_bcs:
            # Find all the subdomains with neumann BCS
            # These are Dirichlet BCs on the vidx space
            neumann_subdomains = set()
            for bc in self.ctx.row_bcs:
                if bc.function_space().index == self.pidx:
                    raise NotImplementedError(
                        "Dirichlet conditions for scalar variable not supported. Use a weak bc."
                    )
                if bc.function_space().index != self.vidx:
                    raise NotImplementedError(
                        "Dirichlet bc set on unsupported space.")
                # append the set of sub domains
                subdom = bc.sub_domain
                if isinstance(subdom, str):
                    neumann_subdomains |= set([subdom])
                else:
                    neumann_subdomains |= set(as_tuple(subdom, int))

            # separate out the top and bottom bcs
            extruded_neumann_subdomains = neumann_subdomains & {
                "top", "bottom"
            }
            neumann_subdomains = neumann_subdomains - extruded_neumann_subdomains

            integrand = gammar * dot(sigma, n)
            measures = []
            trace_subdomains = []
            for subdomain in sorted(extruded_neumann_subdomains):
                measures.append({"top": ds_t, "bottom": ds_b}[subdomain])
                trace_subdomains.extend(
                    sorted({"top", "bottom"} - extruded_neumann_subdomains))

            measures.extend((ds(sd) for sd in sorted(neumann_subdomains)))
            markers = [int(x) for x in mesh.exterior_facets.unique_markers]
            dirichlet_subdomains = set(markers) - neumann_subdomains
            trace_subdomains.extend(sorted(dirichlet_subdomains))

            for measure in measures:
                Kform += integrand * measure

        else:
            trace_subdomains = ["top", "bottom"]

        trace_bcs = [
            DirichletBC(Vv_tr, Constant(0.0), subdomain)
            for subdomain in trace_subdomains
        ]

        # Make a SLATE tensor from Kform
        K = Tensor(Kform)

        # Assemble the Schur complement operator and right-hand side
        self.schur_rhs = Function(Vv_tr)
        self._assemble_Srhs = create_assembly_callable(
            K * Atilde.inv * AssembledVector(self.broken_residual),
            tensor=self.schur_rhs,
            form_compiler_parameters=self.ctx.fc_params)

        mat_type = PETSc.Options().getString(prefix + "mat_type", "aij")

        schur_comp = K * Atilde.inv * K.T
        self.S = allocate_matrix(schur_comp,
                                 bcs=trace_bcs,
                                 form_compiler_parameters=self.ctx.fc_params,
                                 mat_type=mat_type,
                                 options_prefix=prefix)
        self._assemble_S = create_assembly_callable(
            schur_comp,
            tensor=self.S,
            bcs=trace_bcs,
            form_compiler_parameters=self.ctx.fc_params,
            mat_type=mat_type)

        self._assemble_S()
        self.S.force_evaluation()
        Smat = self.S.petscmat

        nullspace = self.ctx.appctx.get("vert_trace_nullspace", None)
        if nullspace is not None:
            nsp = nullspace(Vv_tr)
            Smat.setNullSpace(nsp.nullspace(comm=pc.comm))

        # Set up the KSP for the system of Lagrange multipliers
        trace_ksp = PETSc.KSP().create(comm=pc.comm)
        trace_ksp.setOptionsPrefix(prefix)
        trace_ksp.setOperators(Smat)
        trace_ksp.setUp()
        trace_ksp.setFromOptions()
        self.trace_ksp = trace_ksp

        split_mixed_op = dict(split_form(Atilde.form))
        split_trace_op = dict(split_form(K.form))

        # Generate reconstruction calls
        self._reconstruction_calls(split_mixed_op, split_trace_op)
def test_limit_midpoints(profile):

    # ------------------------------------------------------------------------ #
    # Set up meshes and spaces
    # ------------------------------------------------------------------------ #

    m = IntervalMesh(3, 3)
    mesh = ExtrudedMesh(m, layers=1, layer_height=3.0)

    cell = m.ufl_cell().cellname()
    DG_hori_elt = FiniteElement("DG", cell, 1, variant='equispaced')
    DG_vert_elt = FiniteElement("DG", interval, 1, variant='equispaced')
    Vt_vert_elt = FiniteElement("CG", interval, 2)
    DG_elt = TensorProductElement(DG_hori_elt, DG_vert_elt)
    theta_elt = TensorProductElement(DG_hori_elt, Vt_vert_elt)
    Vt_brok = FunctionSpace(mesh, BrokenElement(theta_elt))
    DG1 = FunctionSpace(mesh, DG_elt)

    new_field = Function(Vt_brok)
    init_field = Function(Vt_brok)
    DG1_field = Function(DG1)

    # ------------------------------------------------------------------------ #
    # Initial conditions
    # ------------------------------------------------------------------------ #

    _, z = SpatialCoordinate(mesh)

    if profile == 'undershoot':
        # A quadratic whose midpoint is lower than the top and bottom values
        init_expr = (80. / 9.) * z**2 - 20. * z + 300.
    elif profile == 'overshoot':
        # A quadratic whose midpoint is higher than the top and bottom values
        init_expr = (-80. / 9.) * z**2 + (100. / 3) * z + 300.
    elif profile == 'linear':
        # Linear profile which must be unchanged
        init_expr = (20. / 3.) * z + 300.
    else:
        raise NotImplementedError

    # Linear DG field has the same values at top and bottom as quadratic
    DG_expr = (20. / 3.) * z + 300.

    init_field.interpolate(init_expr)
    DG1_field.interpolate(DG_expr)

    # ------------------------------------------------------------------------ #
    # Apply kernel
    # ------------------------------------------------------------------------ #

    kernel = kernels.LimitMidpoints(Vt_brok)
    kernel.apply(new_field, DG1_field, init_field)

    # ------------------------------------------------------------------------ #
    # Check values
    # ------------------------------------------------------------------------ #

    tol = 1e-12
    assert np.max(new_field.dat.data) <= np.max(init_field.dat.data) + tol, \
        'LimitMidpoints kernel is giving an overshoot'
    assert np.min(new_field.dat.data) >= np.min(init_field.dat.data) - tol, \
        'LimitMidpoints kernel is giving an undershoot'

    if profile == 'linear':
        assert np.allclose(init_field.dat.data, new_field.dat.data), \
            'For a profile with no maxima or minima, the LimitMidpoints ' + \
            'kernel should leave the field unchanged'
Example #21
0
    def __init__(self,
                 v_CG1,
                 v_DG1,
                 method=Boundary_Method.physics,
                 coords_to_adjust=None):

        self.v_DG1 = v_DG1
        self.v_CG1 = v_CG1
        self.v_DG1_old = Function(v_DG1.function_space())
        self.coords_to_adjust = coords_to_adjust

        self.method = method
        mesh = v_CG1.function_space().mesh()
        VDG0 = FunctionSpace(mesh, "DG", 0)
        VCG1 = FunctionSpace(mesh, "CG", 1)
        VDG1 = FunctionSpace(mesh, "DG", 1)

        self.num_ext = Function(VDG0)

        # check function spaces of functions
        if self.method == Boundary_Method.dynamics:
            if v_CG1.function_space() != VCG1:
                raise NotImplementedError(
                    "This boundary recovery method requires v1 to be in CG1.")
            if v_DG1.function_space() != VDG1:
                raise NotImplementedError(
                    "This boundary recovery method requires v_out to be in DG1."
                )
            # check whether mesh is valid
            if mesh.topological_dimension() == 2:
                # if mesh is extruded then we're fine, but if not needs to be quads
                if not VDG0.extruded and mesh.ufl_cell().cellname(
                ) != 'quadrilateral':
                    raise NotImplementedError(
                        'For 2D meshes this recovery method requires that elements are quadrilaterals'
                    )
            elif mesh.topological_dimension() == 3:
                # assume that 3D mesh is extruded
                if mesh._base_mesh.ufl_cell().cellname() != 'quadrilateral':
                    raise NotImplementedError(
                        'For 3D extruded meshes this recovery method requires a base mesh with quadrilateral elements'
                    )
            elif mesh.topological_dimension() != 1:
                raise NotImplementedError(
                    'This boundary recovery is implemented only on certain classes of mesh.'
                )
            if coords_to_adjust is None:
                raise ValueError(
                    'Need coords_to_adjust field for dynamics boundary methods'
                )

        elif self.method == Boundary_Method.physics:
            # check that mesh is valid -- must be an extruded mesh
            if not VDG0.extruded:
                raise NotImplementedError(
                    'The physics boundary method only works on extruded meshes'
                )
            # base spaces
            cell = mesh._base_mesh.ufl_cell().cellname()
            w_hori = FiniteElement("DG", cell, 0)
            w_vert = FiniteElement("CG", interval, 1)
            # build element
            theta_element = TensorProductElement(w_hori, w_vert)
            # spaces
            Vtheta = FunctionSpace(mesh, theta_element)
            Vtheta_broken = FunctionSpace(mesh, BrokenElement(theta_element))
            if v_CG1.function_space() != Vtheta:
                raise ValueError(
                    "This boundary recovery method requires v_CG1 to be in DG0xCG1 TensorProductSpace."
                )
            if v_DG1.function_space() != Vtheta_broken:
                raise ValueError(
                    "This boundary recovery method requires v_DG1 to be in the broken DG0xCG1 TensorProductSpace."
                )
        else:
            raise ValueError(
                "Boundary method should be a Boundary Method Enum object.")

        VuDG1 = VectorFunctionSpace(VDG0.mesh(), "DG", 1)
        x = SpatialCoordinate(VDG0.mesh())
        self.interpolator = Interpolator(self.v_CG1, self.v_DG1)

        if self.method == Boundary_Method.dynamics:

            # STRATEGY
            # obtain a coordinate field for all the nodes
            VuDG1 = VectorFunctionSpace(mesh, "DG", 1)
            self.act_coords = Function(VuDG1).project(x)  # actual coordinates
            self.eff_coords = Function(VuDG1).project(
                x)  # effective coordinates

            shapes = {
                "nDOFs":
                self.v_DG1.function_space().finat_element.space_dimension(),
                "dim":
                np.prod(VuDG1.shape, dtype=int)
            }

            num_ext_domain = ("{{[i]: 0 <= i < {nDOFs}}}").format(**shapes)
            num_ext_instructions = ("""
            <float64> SUM_EXT = 0
            for i
                SUM_EXT = SUM_EXT + EXT_V1[i]
            end

            NUM_EXT[0] = SUM_EXT
            """)

            coords_domain = ("{{[i, j, k, ii, jj, kk, ll, mm, iii, kkk]: "
                             "0 <= i < {nDOFs} and "
                             "0 <= j < {nDOFs} and 0 <= k < {dim} and "
                             "0 <= ii < {nDOFs} and 0 <= jj < {nDOFs} and "
                             "0 <= kk < {dim} and 0 <= ll < {dim} and "
                             "0 <= mm < {dim} and 0 <= iii < {nDOFs} and "
                             "0 <= kkk < {dim}}}").format(**shapes)
            coords_insts = (
                """
                            <float64> sum_V1_ext = 0
                            <int> index = 100
                            <float64> dist = 0.0
                            <float64> max_dist = 0.0
                            <float64> min_dist = 0.0
                            """

                # only do adjustment in cells with at least one DOF to adjust
                """
                            if NUM_EXT[0] > 0
                            """

                # find the maximum distance between DOFs in this cell, to serve as starting point for finding min distances
                """
                                for i
                                    for j
                                        dist = 0.0
                                        for k
                                            dist = dist + pow(ACT_COORDS[i,k] - ACT_COORDS[j,k], 2.0)
                                        end
                                        dist = pow(dist, 0.5) {{id=sqrt_max_dist, dep=*}}
                                        max_dist = fmax(dist, max_dist) {{id=max_dist, dep=sqrt_max_dist}}
                                    end
                                end
                            """

                # loop through cells and find which ones to adjust
                """
                                for ii
                                    if EXT_V1[ii] > 0.5
                            """

                # find closest interior node
                """
                                        min_dist = max_dist
                                        index = 100
                                        for jj
                                            if EXT_V1[jj] < 0.5
                                                dist = 0.0
                                                for kk
                                                    dist = dist + pow(ACT_COORDS[ii,kk] - ACT_COORDS[jj,kk], 2)
                                                end
                                                dist = pow(dist, 0.5)
                                                if dist <= min_dist
                                                    index = jj
                                                end
                                                min_dist = fmin(min_dist, dist)
                                                for ll
                                                    EFF_COORDS[ii,ll] = 0.5 * (ACT_COORDS[ii,ll] + ACT_COORDS[index,ll])
                                                end
                                            end
                                        end
                                    else
                            """

                # for DOFs that aren't exterior, use the original coordinates
                """
                                        for mm
                                            EFF_COORDS[ii, mm] = ACT_COORDS[ii, mm]
                                        end
                                    end
                                end
                            else
                            """

                # for interior elements, just use the original coordinates
                """
                                for iii
                                    for kkk
                                        EFF_COORDS[iii, kkk] = ACT_COORDS[iii, kkk]
                                    end
                                end
                            end
                            """).format(**shapes)

            elimin_domain = (
                "{{[i, ii_loop, jj_loop, kk, ll_loop, mm, iii_loop, kkk_loop, iiii, iiiii]: "
                "0 <= i < {nDOFs} and 0 <= ii_loop < {nDOFs} and "
                "ii_loop + 1 <= jj_loop < {nDOFs} and ii_loop <= kk < {nDOFs} and "
                "ii_loop + 1 <= ll_loop < {nDOFs} and ii_loop <= mm < {nDOFs} + 1 and "
                "0 <= iii_loop < {nDOFs} and {nDOFs} - iii_loop <= kkk_loop < {nDOFs} + 1 and "
                "0 <= iiii < {nDOFs} and 0 <= iiiii < {nDOFs}}}").format(
                    **shapes)
            elimin_insts = (
                """
                            <int> ii = 0
                            <int> jj = 0
                            <int> ll = 0
                            <int> iii = 0
                            <int> jjj = 0
                            <int> i_max = 0
                            <float64> A_max = 0.0
                            <float64> temp_f = 0.0
                            <float64> temp_A = 0.0
                            <float64> c = 0.0
                            <float64> f[{nDOFs}] = 0.0
                            <float64> a[{nDOFs}] = 0.0
                            <float64> A[{nDOFs},{nDOFs}] = 0.0
                            """

                # We are aiming to find the vector a that solves A*a = f, for matrix A and vector f.
                # This is done by performing row operations (swapping and scaling) to obtain A in upper diagonal form.
                # N.B. several for loops must be executed in numerical order (loopy does not necessarily do this).
                # For these loops we must manually iterate the index.
                """
                            if NUM_EXT[0] > 0.0
                            """

                # only do Gaussian elimination for elements with effective coordinates
                """
                                for i
                            """

                # fill f with the original field values and A with the effective coordinate values
                """
                                    f[i] = DG1_OLD[i]
                                    A[i,0] = 1.0
                                    A[i,1] = EFF_COORDS[i,0]
                                    if {nDOFs} > 3
                                        A[i,2] = EFF_COORDS[i,1]
                                        A[i,3] = EFF_COORDS[i,0]*EFF_COORDS[i,1]
                                        if {nDOFs} > 7
                                            A[i,4] = EFF_COORDS[i,{dim}-1]
                                            A[i,5] = EFF_COORDS[i,0]*EFF_COORDS[i,{dim}-1]
                                            A[i,6] = EFF_COORDS[i,1]*EFF_COORDS[i,{dim}-1]
                                            A[i,7] = EFF_COORDS[i,0]*EFF_COORDS[i,1]*EFF_COORDS[i,{dim}-1]
                                        end
                                    end
                                end
                            """

                # now loop through rows/columns of A
                """
                                for ii_loop
                                    A_max = fabs(A[ii,ii])
                                    i_max = ii
                            """

                # loop to find the largest value in the ith column
                # set i_max as the index of the row with this largest value.
                """
                                    jj = ii + 1
                                    for jj_loop
                                        if fabs(A[jj,ii]) > A_max
                                            i_max = jj
                                        end
                                        A_max = fmax(A_max, fabs(A[jj,ii]))
                                        jj = jj + 1
                                    end
                            """

                # if the max value in the ith column isn't in the ith row, we must swap the rows
                """
                                    if i_max != ii
                            """

                # swap the elements of f
                """
                                        temp_f = f[ii]  {{id=set_temp_f, dep=*}}
                                        f[ii] = f[i_max]  {{id=set_f_imax, dep=set_temp_f}}
                                        f[i_max] = temp_f  {{id=set_f_ii, dep=set_f_imax}}
                            """

                # swap the elements of A
                # N.B. kk runs from ii to (nDOFs-1) as elements below diagonal should be 0
                """
                                        for kk
                                            temp_A = A[ii,kk]  {{id=set_temp_A, dep=*}}
                                            A[ii, kk] = A[i_max, kk]  {{id=set_A_ii, dep=set_temp_A}}
                                            A[i_max, kk] = temp_A  {{id=set_A_imax, dep=set_A_ii}}
                                        end
                                    end
                            """

                # scale the rows below the ith row
                """
                                    ll = ii + 1
                                    for ll_loop
                                        if ll > ii
                            """

                # find scaling factor
                """
                                            c = - A[ll,ii] / A[ii,ii]
                            """

                # N.B. mm runs from ii to (nDOFs-1) as elements below diagonal should be 0
                """
                                            for mm
                                                A[ll, mm] = A[ll, mm] + c * A[ii,mm]
                                            end
                                            f[ll] = f[ll] + c * f[ii]
                                        end
                                        ll = ll + 1
                                    end
                                    ii = ii + 1
                                end
                            """

                # do back substitution of upper diagonal A to obtain a
                """
                                iii = 0
                                for iii_loop
                            """

                # jjj starts at the bottom row and works upwards
                """
                                    jjj = {nDOFs} - iii - 1  {{id=assign_jjj, dep=*}}
                                    a[jjj] = f[jjj]   {{id=set_a, dep=assign_jjj}}
                                    for kkk_loop
                                        a[jjj] = a[jjj] - A[jjj,kkk_loop] * a[kkk_loop]
                                    end
                                    a[jjj] = a[jjj] / A[jjj,jjj]
                                    iii = iii + 1
                                end
                            """

                # Having found a, this gives us the coefficients for the Taylor expansion with the actual coordinates.
                """
                                for iiii
                                    if {nDOFs} == 2
                                        DG1[iiii] = a[0] + a[1]*ACT_COORDS[iiii,0]
                                    elif {nDOFs} == 4
                                        DG1[iiii] = a[0] + a[1]*ACT_COORDS[iiii,0] + a[2]*ACT_COORDS[iiii,1] + a[3]*ACT_COORDS[iiii,0]*ACT_COORDS[iiii,1]
                                    elif {nDOFs} == 8
                                        DG1[iiii] = a[0] + a[1]*ACT_COORDS[iiii,0] + a[2]*ACT_COORDS[iiii,1] + a[3]*ACT_COORDS[iiii,0]*ACT_COORDS[iiii,1] + a[4]*ACT_COORDS[iiii,{dim}-1] + a[5]*ACT_COORDS[iiii,0]*ACT_COORDS[iiii,{dim}-1] + a[6]*ACT_COORDS[iiii,1]*ACT_COORDS[iiii,{dim}-1] + a[7]*ACT_COORDS[iiii,0]*ACT_COORDS[iiii,1]*ACT_COORDS[iiii,{dim}-1]
                                    end
                                end
                            """

                # if element is not external, just use old field values.
                """
                            else
                                for iiiii
                                    DG1[iiiii] = DG1_OLD[iiiii]
                                end
                            end
                            """).format(**shapes)

            _num_ext_kernel = (num_ext_domain, num_ext_instructions)
            _eff_coords_kernel = (coords_domain, coords_insts)
            self._gaussian_elimination_kernel = (elimin_domain, elimin_insts)

            # find number of external DOFs per cell
            par_loop(_num_ext_kernel,
                     dx, {
                         "NUM_EXT": (self.num_ext, WRITE),
                         "EXT_V1": (self.coords_to_adjust, READ)
                     },
                     is_loopy_kernel=True)

            # find effective coordinates
            logger.warning(
                'Finding effective coordinates for boundary recovery. This could give unexpected results for deformed meshes over very steep topography.'
            )
            par_loop(_eff_coords_kernel,
                     dx, {
                         "EFF_COORDS": (self.eff_coords, WRITE),
                         "ACT_COORDS": (self.act_coords, READ),
                         "NUM_EXT": (self.num_ext, READ),
                         "EXT_V1": (self.coords_to_adjust, READ)
                     },
                     is_loopy_kernel=True)

        elif self.method == Boundary_Method.physics:
            top_bottom_domain = ("{[i]: 0 <= i < 1}")
            bottom_instructions = ("""
                                   DG1[0] = 2 * CG1[0] - CG1[1]
                                   DG1[1] = CG1[1]
                                   """)
            top_instructions = ("""
                                DG1[0] = CG1[0]
                                DG1[1] = -CG1[0] + 2 * CG1[1]
                                """)

            self._bottom_kernel = (top_bottom_domain, bottom_instructions)
            self._top_kernel = (top_bottom_domain, top_instructions)
Example #22
0
    def __init__(self, v_in, v_out, VDG=None, boundary_method=None):

        # check if v_in is valid
        if isinstance(v_in, expression.Expression) or not isinstance(
                v_in, (ufl.core.expr.Expr, function.Function)):
            raise ValueError(
                "Can only recover UFL expression or Functions not '%s'" %
                type(v_in))

        self.v_in = v_in
        self.v_out = v_out
        self.V = v_out.function_space()
        if VDG is not None:
            self.v = Function(VDG)
            self.interpolator = Interpolator(v_in, self.v)
        else:
            self.v = v_in
            self.interpolator = None

        self.VDG = VDG
        self.boundary_method = boundary_method
        self.averager = Averager(self.v, self.v_out)

        # check boundary method options are valid
        if boundary_method is not None:
            if boundary_method != Boundary_Method.dynamics and boundary_method != Boundary_Method.physics:
                raise ValueError(
                    "Boundary method must be a Boundary_Method Enum object.")
            if VDG is None:
                raise ValueError(
                    "If boundary_method is specified, VDG also needs specifying."
                )

            # now specify things that we'll need if we are doing boundary recovery
            if boundary_method == Boundary_Method.physics:
                # check dimensions
                if self.V.value_size != 1:
                    raise ValueError(
                        'This method only works for scalar functions.')
                self.boundary_recoverer = Boundary_Recoverer(
                    self.v_out, self.v, method=Boundary_Method.physics)
            else:

                mesh = self.V.mesh()
                # this ensures we get the pure function space, not an indexed function space
                V0 = FunctionSpace(mesh,
                                   self.v_in.function_space().ufl_element())
                VCG1 = FunctionSpace(mesh, "CG", 1)
                if V0.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")
                VDG1 = FunctionSpace(mesh, DG1_element)

                if self.V.value_size == 1:
                    coords_to_adjust = find_coords_to_adjust(V0, VDG1)

                    self.boundary_recoverer = Boundary_Recoverer(
                        self.v_out,
                        self.v,
                        coords_to_adjust=coords_to_adjust,
                        method=Boundary_Method.dynamics)
                else:
                    VuDG1 = VectorFunctionSpace(mesh, DG1_element)
                    coords_to_adjust = find_coords_to_adjust(V0, VuDG1)

                    # now, break the problem down into components
                    v_scalars = []
                    v_out_scalars = []
                    self.boundary_recoverers = []
                    self.project_to_scalars_CG = []
                    self.extra_averagers = []
                    coords_to_adjust_list = []
                    for i in range(self.V.value_size):
                        v_scalars.append(Function(VDG1))
                        v_out_scalars.append(Function(VCG1))
                        coords_to_adjust_list.append(
                            Function(VDG1).project(coords_to_adjust[i]))
                        self.project_to_scalars_CG.append(
                            Projector(self.v_out[i], v_out_scalars[i]))
                        self.boundary_recoverers.append(
                            Boundary_Recoverer(
                                v_out_scalars[i],
                                v_scalars[i],
                                method=Boundary_Method.dynamics,
                                coords_to_adjust=coords_to_adjust_list[i]))
                        # need an extra averager that works on the scalar fields rather than the vector one
                        self.extra_averagers.append(
                            Averager(v_scalars[i], v_out_scalars[i]))

                    # the boundary recoverer needs to be done on a scalar fields
                    # so need to extract component and restore it after the boundary recovery is done
                    self.interpolate_to_vector = Interpolator(
                        as_vector(v_out_scalars), self.v_out)
Example #23
0
    def __init__(self, v_CG1, v_DG1, method=Boundary_Method.physics, eff_coords=None):

        self.v_DG1 = v_DG1
        self.v_CG1 = v_CG1
        self.v_DG1_old = Function(v_DG1.function_space())
        self.eff_coords = eff_coords

        self.method = method
        mesh = v_CG1.function_space().mesh()
        DG0 = FunctionSpace(mesh, "DG", 0)
        CG1 = FunctionSpace(mesh, "CG", 1)

        if DG0.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 = FunctionSpace(mesh, DG1_element)

        self.num_ext = find_domain_boundaries(mesh)

        # check function spaces of functions
        if self.method == Boundary_Method.dynamics:
            if v_CG1.function_space() != CG1:
                raise NotImplementedError("This boundary recovery method requires v1 to be in CG1.")
            if v_DG1.function_space() != DG1:
                raise NotImplementedError("This boundary recovery method requires v_out to be in DG1.")
            if eff_coords is None:
                raise ValueError('Need eff_coords field for dynamics boundary methods')

        elif self.method == Boundary_Method.physics:
            # check that mesh is valid -- must be an extruded mesh
            if not DG0.extruded:
                raise NotImplementedError('The physics boundary method only works on extruded meshes')
            # check that function spaces are valid
            sub_elements = v_CG1.function_space().ufl_element().sub_elements()
            if (sub_elements[0].family() not in ['Discontinuous Lagrange', 'DQ']
                    or sub_elements[1].family() != 'Lagrange'
                    or v_CG1.function_space().ufl_element().degree() != (0, 1)):
                raise ValueError("This boundary recovery method requires v_CG1 to be in DG0xCG1 TensorProductSpace.")

            brok_elt = v_DG1.function_space().ufl_element()
            if (brok_elt.degree() != (0, 1)
                or (type(brok_elt) is not BrokenElement
                    and (brok_elt.sub_elements[0].family() not in ['Discontinuous Lagrange', 'DQ']
                         or brok_elt.sub_elements[1].family() != 'Discontinuous Lagrange'))):
                raise ValueError("This boundary recovery method requires v_DG1 to be in the broken DG0xCG1 TensorProductSpace.")
        else:
            raise ValueError("Boundary method should be a Boundary Method Enum object.")

        vec_DG1 = VectorFunctionSpace(DG0.mesh(), DG1_element)
        x = SpatialCoordinate(DG0.mesh())
        self.interpolator = Interpolator(self.v_CG1, self.v_DG1)

        if self.method == Boundary_Method.dynamics:

            # STRATEGY
            # obtain a coordinate field for all the nodes
            self.act_coords = Function(vec_DG1).project(x)  # actual coordinates
            self.eff_coords = eff_coords  # effective coordinates
            self.output = Function(DG1)
            self.on_exterior = find_domain_boundaries(mesh)

            self.gaussian_elimination_kernel = kernels.GaussianElimination(DG1)

        elif self.method == Boundary_Method.physics:

            self.bottom_kernel = kernels.PhysicsRecoveryBottom()
            self.top_kernel = kernels.PhysicsRecoveryTop()