Esempio n. 1
0
def PeriodicIntervalMesh(ncells, length):
    """Generate a periodic mesh of an interval.

    :arg ncells: The number of cells over the interval.
    :arg length: The length the interval."""

    if ncells < 3:
        raise ValueError("1D periodic meshes with fewer than 3 \
cells are not currently supported")

    m = CircleManifoldMesh(ncells)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=1)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """double Y,pi;
            Y = 0.5*(old_coords[0][1]-old_coords[1][1]);
            pi=3.141592653589793;
            for(int i=0;i<2;i++){
            new_coords[i][0] = atan2(old_coords[i][1],old_coords[i][0])/pi/2;
            if(new_coords[i][0]<0.) new_coords[i][0] += 1;
            if(new_coords[i][0]==0 && Y<0.) new_coords[i][0] = 1.0;
            new_coords[i][0] *= L[0];
            }"""

    cL = Constant(length)

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ),
              "L": (cL, READ)})

    return mesh.Mesh(new_coordinates)
Esempio n. 2
0
def PeriodicIntervalMesh(ncells, length):
    """Generate a periodic mesh of an interval.

    :arg ncells: The number of cells over the interval.
    :arg length: The length the interval."""

    m = CircleManifoldMesh(ncells)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=1)
    old_coordinates = Function(m.coordinates)
    new_coordinates = Function(coord_fs)

    periodic_kernel = """double Y,pi;
            Y = 0.5*(old_coords[0][1]-old_coords[1][1]);
            pi=3.141592653589793;
            for(int i=0;i<2;i++){
            new_coords[i][0] = atan2(old_coords[i][1],old_coords[i][0])/pi/2;
            if(new_coords[i][0]<0.) new_coords[i][0] += 1;
            if(new_coords[i][0]==0 && Y<0.) new_coords[i][0] = 1.0;
            new_coords[i][0] *= L;
            }"""

    periodic_kernel = periodic_kernel.replace('L', str(length))

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ)})

    m.coordinates = new_coordinates
    return m
Esempio n. 3
0
def PeriodicIntervalMesh(ncells, length):
    """Generate a periodic mesh of an interval.

    :arg ncells: The number of cells over the interval.
    :arg length: The length the interval."""

    m = CircleManifoldMesh(ncells)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=1)
    old_coordinates = Function(m.coordinates)
    new_coordinates = Function(coord_fs)

    periodic_kernel = """double Y,pi;
            Y = 0.5*(old_coords[0][1]-old_coords[1][1]);
            pi=3.141592653589793;
            for(int i=0;i<2;i++){
            new_coords[i][0] = atan2(old_coords[i][1],old_coords[i][0])/pi/2;
            if(new_coords[i][0]<0.) new_coords[i][0] += 1;
            if(new_coords[i][0]==0 && Y<0.) new_coords[i][0] = 1.0;
            new_coords[i][0] *= L;
            }"""

    periodic_kernel = periodic_kernel.replace('L', str(length))

    par_loop(
        periodic_kernel, dx, {
            "new_coords": (new_coordinates, WRITE),
            "old_coords": (old_coordinates, READ)
        })

    m.coordinates = new_coordinates
    return m
Esempio n. 4
0
def PeriodicIntervalMesh(ncells, length):
    """Generate a periodic mesh of an interval.

    :arg ncells: The number of cells over the interval.
    :arg length: The length the interval."""

    if ncells < 3:
        raise ValueError("1D periodic meshes with fewer than 3 \
cells are not currently supported")

    m = CircleManifoldMesh(ncells)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=1)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """double Y,pi;
            Y = 0.5*(old_coords[0][1]-old_coords[1][1]);
            pi=3.141592653589793;
            for(int i=0;i<2;i++){
            new_coords[i][0] = atan2(old_coords[i][1],old_coords[i][0])/pi/2;
            if(new_coords[i][0]<0.) new_coords[i][0] += 1;
            if(new_coords[i][0]==0 && Y<0.) new_coords[i][0] = 1.0;
            new_coords[i][0] *= L[0];
            }"""

    cL = Constant(length)

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ),
              "L": (cL, READ)})

    return mesh.Mesh(new_coordinates)
Esempio n. 5
0
def create_function_marker(PHI, W, xlimits, ylimits):
    x, y, z = fd.SpatialCoordinate(PHI.ufl_domain())
    x_func, y_func, z_func = (
        fd.Function(PHI),
        fd.Function(PHI),
        fd.Function(PHI),
    )
    with fda.stop_annotating():
        x_func.interpolate(x)
        y_func.interpolate(y)
        z_func.interpolate(z)

    domain = "{[i, j]: 0 <= i < f.dofs and 0<= j <= 3}"
    instruction = f"""
    f[i, j] = 1.0 if (x[i, 0] < {xlimits[1]} and x[i, 0] > {xlimits[0]}) and (y[i, 0] < {ylimits[0]} or y[i, 0] > {ylimits[1]}) and z[i, 0] < 1e-7 else 0.0
    """
    I_BC = fd.Function(W)
    fd.par_loop(
        (domain, instruction),
        dx,
        {
            "f": (I_BC, fd.RW),
            "x": (x_func, fd.READ),
            "y": (y_func, fd.READ),
            "z": (z_func, fd.READ),
        },
        is_loopy_kernel=True,
    )

    return I_BC
Esempio n. 6
0
    def V_dof_weights(self, V):
        """Dof weights for Fortin projection.

        :arg V: function space to compute weights for.
        :returns: A PETSc Vec.
        """
        key = V.dim()
        try:
            return self._V_dof_weights[key]
        except KeyError:
            # Compute dof multiplicity for V
            # Spin over all (owned) cells incrementing visible dofs by 1.
            # After halo exchange, the Vec representation is the
            # global Vector counting the number of cells that see each
            # dof.
            f = firedrake.Function(V)
            firedrake.par_loop(
                """for (int i = 0; i < A.dofs; i++)
                       for (int j = 0; j < {}; j++)
                          A[i][j] += 1;
                """.format(V.value_size),
                firedrake.dx,
                {"A": (f, firedrake.INC)})
            with f.dat.vec_ro as fv:
                return self._V_dof_weights.setdefault(key, fv.copy())
Esempio n. 7
0
def PeriodicIntervalMesh(ncells, length, comm=COMM_WORLD):
    """Generate a periodic mesh of an interval.

    :arg ncells: The number of cells over the interval.
    :arg length: The length the interval.
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """

    if ncells < 3:
        raise ValueError("1D periodic meshes with fewer than 3 \
cells are not currently supported")

    m = CircleManifoldMesh(ncells, comm=comm)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=1)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """
    const double pi = 3.141592653589793;
    const double eps = 1e-12;
    double a = atan2(old_coords[0][1], old_coords[0][0]) / (2*pi);
    double b = atan2(old_coords[1][1], old_coords[1][0]) / (2*pi);
    int swap = 0;
    if ( a >= b ) {
        const double tmp = b;
        b = a;
        a = tmp;
        swap = 1;
    }
    if ( fabs(b) < eps && a < -eps ) {
        b = 1.0;
    }
    if ( a < -eps ) {
        a += 1;
    }
    if ( b < -eps ) {
        b += 1;
    }
    if ( swap ) {
        const double tmp = b;
        b = a;
        a = tmp;
    }
    new_coords[0][0] = a * L[0];
    new_coords[1][0] = b * L[0];
    """

    cL = Constant(length)

    par_loop(
        periodic_kernel, dx, {
            "new_coords": (new_coordinates, WRITE),
            "old_coords": (old_coordinates, READ),
            "L": (cL, READ)
        })

    return mesh.Mesh(new_coordinates)
Esempio n. 8
0
def get_latlon_mesh(mesh):
    """Build 2D projected mesh of spherical mesh"""
    crds_orig = mesh.coordinates
    mesh_dg_fs = VectorFunctionSpace(mesh, "DG", 1)
    crds_dg = Function(mesh_dg_fs)
    crds_latlon = Function(mesh_dg_fs)
    par_loop(
        """
for (int i=0; i<3; i++) {
    for (int j=0; j<3; j++) {
        dg[i][j] = cg[i][j];
    }
}
""", dx, {
            'dg': (crds_dg, WRITE),
            'cg': (crds_orig, READ)
        })

    # lat-lon 'x' = atan2(y, x)
    crds_latlon.dat.data[:, 0] = arctan2(crds_dg.dat.data[:, 1],
                                         crds_dg.dat.data[:, 0])
    # lat-lon 'y' = asin(z/sqrt(x^2 + y^2 + z^2))
    crds_latlon.dat.data[:, 1] = (arcsin(
        crds_dg.dat.data[:, 2] /
        np_sqrt(crds_dg.dat.data[:, 0]**2 + crds_dg.dat.data[:, 1]**2 +
                crds_dg.dat.data[:, 2]**2)))
    crds_latlon.dat.data[:, 2] = 0.0

    kernel = op2.Kernel(
        """
#define PI 3.141592653589793
#define TWO_PI 6.283185307179586
void splat_coords(double **coords) {
    double diff0 = (coords[0][0] - coords[1][0]);
    double diff1 = (coords[0][0] - coords[2][0]);
    double diff2 = (coords[1][0] - coords[2][0]);

    if (fabs(diff0) > PI || fabs(diff1) > PI || fabs(diff2) > PI) {
        const int sign0 = coords[0][0] < 0 ? -1 : 1;
        const int sign1 = coords[1][0] < 0 ? -1 : 1;
        const int sign2 = coords[2][0] < 0 ? -1 : 1;
        if (sign0 < 0) {
            coords[0][0] += TWO_PI;
        }
        if (sign1 < 0) {
            coords[1][0] += TWO_PI;
        }
        if (sign2 < 0) {
            coords[2][0] += TWO_PI;
        }
    }
}
""", "splat_coords")

    op2.par_loop(kernel, crds_latlon.cell_set,
                 crds_latlon.dat(op2.RW, crds_latlon.cell_node_map()))
    return Mesh(crds_latlon)
Esempio n. 9
0
 def multiplicity(V):
     # Lawrence's magic code for calculating dof multiplicities
     shapes = (V.finat_element.space_dimension(), np.prod(V.shape))
     domain = "{[i,j]: 0 <= i < %d and 0 <= j < %d}" % shapes
     instructions = """
     for i, j
         w[i,j] = w[i,j] + 1
     end
     """
     weight = firedrake.Function(V)
     firedrake.par_loop((domain, instructions),
                        firedrake.dx, {"w": (weight, op2.INC)},
                        is_loopy_kernel=True)
     return weight
Esempio n. 10
0
 def extract_comp(mode, comp, result):
     instruction = f"""
                    component[0] = abs(u[i, {comp}])
                    """
     fd.par_loop(
         (domain, instruction),
         fd.dx,
         {
             "u": (coords, fd.READ),
             "component": (result, mode)
         },
         is_loopy_kernel=True,
     )
     return result
Esempio n. 11
0
    def __init__(self, Vf, Vc, Vf_bcs, Vc_bcs):
        self.Vf = Vf
        self.Vc = Vc
        self.Vf_bcs = Vf_bcs
        self.Vc_bcs = Vc_bcs

        self.uc = firedrake.Function(Vc)
        self.uf = firedrake.Function(Vf)
        self.prolong_kernel = self.prolongation_transfer_kernel_action(
            Vf, self.uc)

        matrix_kernel = self.prolongation_transfer_kernel_action(
            Vf, firedrake.TestFunction(Vc))
        element_kernel = loopy.generate_code_v2(
            matrix_kernel.code).device_code()
        element_kernel = element_kernel.replace(
            "void expression_kernel", "static void expression_kernel")
        dimc = Vc.finat_element.space_dimension() * Vc.value_size
        dimf = Vf.finat_element.space_dimension() * Vf.value_size
        self.restrict_code = f"""
{element_kernel}


void restriction(double *restrict Rc, const double *restrict Rf, const double *restrict w)
{{
    double Afc[{dimf}*{dimc}] = {{0}};
    expression_kernel(Afc);
    for (int32_t i = 0; i < {dimf}; i++)
       for (int32_t j = 0; j < {dimc}; j++)
           Rc[j] += Afc[i*{dimc} + j] * Rf[i] / w[i];
}}
"""

        self.restrict_kernel = op2.Kernel(self.restrict_code, "restriction")
        self.mesh = Vf.mesh()

        # Lawrence's magic code for calculating dof multiplicities
        shapes = (Vf.finat_element.space_dimension(), np.prod(Vf.shape))
        domain = "{[i,j]: 0 <= i < %d and 0 <= j < %d}" % shapes
        instructions = """
        for i, j
            w[i,j] = w[i,j] + 1
        end
        """
        self.weight = firedrake.Function(Vf)
        firedrake.par_loop((domain, instructions),
                           firedrake.dx, {"w": (self.weight, op2.INC)},
                           is_loopy_kernel=True)
Esempio n. 12
0
def get_latlon_mesh(mesh):
    coords_orig = mesh.coordinates
    mesh_dg_fs = VectorFunctionSpace(mesh, "DG", 1)
    coords_dg = Function(mesh_dg_fs)
    coords_latlon = Function(mesh_dg_fs)
    par_loop("""
    for (int i=0; i<3; i++) {
    for (int j=0; j<3; j++) {
    dg[i][j] = cg[i][j];
    }
    }
    """, dx, {'dg': (coords_dg, WRITE),
              'cg': (coords_orig, READ)})

    # 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))
    coords_latlon.dat.data[:,2] = 0.0

    kernel = op2.Kernel("""
    #define PI 3.141592653589793
    #define TWO_PI 6.283185307179586
    void splat_coords(double **coords) {
        double diff0 = (coords[0][0] - coords[1][0]);
        double diff1 = (coords[0][0] - coords[2][0]);
        double diff2 = (coords[1][0] - coords[2][0]);

        if (fabs(diff0) > PI || fabs(diff1) > PI || fabs(diff2) > PI) {
            const int sign0 = coords[0][0] < 0 ? -1 : 1;
            const int sign1 = coords[1][0] < 0 ? -1 : 1;
            const int sign2 = coords[2][0] < 0 ? -1 : 1;
            if (sign0 < 0) {
                coords[0][0] += TWO_PI;
            }
            if (sign1 < 0) {
                coords[1][0] += TWO_PI;
            }
            if (sign2 < 0) {
                coords[2][0] += TWO_PI;
            }
        }
    }""", "splat_coords")

    op2.par_loop(kernel, coords_latlon.cell_set,
                 coords_latlon.dat(op2.RW, coords_latlon.cell_node_map()))
    return Mesh(coords_latlon)
Esempio n. 13
0
def calculate_max_vel(velocity: fd.Function):
    mesh = velocity.ufl_domain()
    MAXSP = fd.FunctionSpace(mesh, "R", 0)
    maxv = fd.Function(MAXSP)
    domain = "{[i, j] : 0 <= i < u.dofs}"
    instruction = """
                    maxv[0] = abs(u[i, 0]) + abs(u[i, 1])
                    """
    fd.par_loop(
        (domain, instruction),
        fd.dx,
        {
            "u": (velocity, fd.READ),
            "maxv": (maxv, fd.MAX)
        },
        is_loopy_kernel=True,
    )
    maxval = maxv.dat.data[0]
    return maxval
Esempio n. 14
0
    def V_dof_weights(self, V):
        """Dof weights for Fortin projection.

        :arg V: function space to compute weights for.
        :returns: A PETSc Vec.
        """
        key = V.dim()
        try:
            return self._V_dof_weights[key]
        except KeyError:
            # Compute dof multiplicity for V
            # Spin over all (owned) cells incrementing visible dofs by 1.
            # After halo exchange, the Vec representation is the
            # global Vector counting the number of cells that see each
            # dof.
            f = firedrake.Function(V)
            firedrake.par_loop(
                ("{[i, j]: 0 <= i < A.dofs and 0 <= j < %d}" % V.value_size,
                 "A[i, j] = A[i, j] + 1"),
                firedrake.dx, {"A": (f, firedrake.INC)},
                is_loopy_kernel=True)
            with f.dat.vec_ro as fv:
                return self._V_dof_weights.setdefault(key, fv.copy())
Esempio n. 15
0
    def V_dof_weights(self, V):
        """Dof weights for Fortin projection.

        :arg V: function space to compute weights for.
        :returns: A PETSc Vec.
        """
        key = V.dim()
        try:
            return self._V_dof_weights[key]
        except KeyError:
            # Compute dof multiplicity for V
            # Spin over all (owned) cells incrementing visible dofs by 1.
            # After halo exchange, the Vec representation is the
            # global Vector counting the number of cells that see each
            # dof.
            f = firedrake.Function(V)
            firedrake.par_loop(("{[i, j]: 0 <= i < A.dofs and 0 <= j < %d}" % V.value_size,
                               "A[i, j] = A[i, j] + 1"),
                               firedrake.dx,
                               {"A": (f, firedrake.INC)},
                               is_loopy_kernel=True)
            with f.dat.vec_ro as fv:
                return self._V_dof_weights.setdefault(key, fv.copy())
par_loop("""
double v0[3];
double v1[3];
double n[3];
double com[3];
double dot;
double norm;
norm = 0.0;
dot = 0.0;
// form "x1 - x0" and "x2 - x0" of cell base
for (int i=0; i<3; ++i) {
    v0[i] = coords[2][i] - coords[0][i];
    v1[i] = coords[4][i] - coords[0][i];
}

for (int i=0; i<3; ++i) {
    com[i] = 0.0;
}

// take cross-product to form normal vector
n[0] = v0[1] * v1[2] - v0[2] * v1[1];
n[1] = v0[2] * v1[0] - v0[0] * v1[2];
n[2] = v0[0] * v1[1] - v0[1] * v1[0];

// get (scaled) centre-of-mass of cell
for (int i=0; i<6; ++i) {
    com[0] += coords[i][0];
    com[1] += coords[i][1];
    com[2] += coords[i][2];
}

// is the normal pointing outwards or inwards w.r.t. origin?
for (int i=0; i<3; ++i) {
    dot += com[i]*n[i];
}

for (int i=0; i<3; ++i) {
    norm += n[i]*n[i];
}

// normalise normal vector and multiply by -1 if dot product was < 0
norm = sqrt(norm);
norm *= (dot < 0.0 ? -1.0 : 1.0);

for (int i=0; i<3; ++i) {
    normals[0][i] = n[i] / norm;
}
""", dx,
         {'normals': (zhat, WRITE),
          'coords': (mesh.coordinates, READ)})
Esempio n. 17
0
def PeriodicRectangleMesh(nx, ny, Lx, Ly, quadrilateral=False, reorder=None):
    """Generate a periodic rectangular mesh

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    """

    if nx < 3 or ny < 3:
        raise ValueError("2D periodic meshes with fewer than 3 \
cells in each direction are not currently supported")

    m = TorusMesh(nx, ny, 1.0, 0.5, quadrilateral=quadrilateral, reorder=reorder)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=2)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """
double pi = 3.141592653589793;
double eps = 1e-12;
double bigeps = 1e-1;
double phi, theta, Y, Z;
Y = 0.0;
Z = 0.0;

for(int i=0; i<old_coords.dofs; i++) {
    Y += old_coords[i][1];
    Z += old_coords[i][2];
}

for(int i=0; i<new_coords.dofs; i++) {
    phi = atan2(old_coords[i][1], old_coords[i][0]);
    if (fabs(sin(phi)) > bigeps)
        theta = atan2(old_coords[i][2], old_coords[i][1]/sin(phi) - 1.0);
    else
        theta = atan2(old_coords[i][2], old_coords[i][0]/cos(phi) - 1.0);

    new_coords[i][0] = phi/(2.0*pi);
    if(new_coords[i][0] < -eps) {
        new_coords[i][0] += 1.0;
    }
    if(fabs(new_coords[i][0]) < eps && Y < 0.0) {
        new_coords[i][0] = 1.0;
    }

    new_coords[i][1] = theta/(2.0*pi);
    if(new_coords[i][1] < -eps) {
        new_coords[i][1] += 1.0;
    }
    if(fabs(new_coords[i][1]) < eps && Z < 0.0) {
        new_coords[i][1] = 1.0;
    }

    new_coords[i][0] *= Lx[0];
    new_coords[i][1] *= Ly[0];
}
"""

    cLx = Constant(Lx)
    cLy = Constant(Ly)

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ),
              "Lx": (cLx, READ),
              "Ly": (cLy, READ)})

    return mesh.Mesh(new_coordinates)
Esempio n. 18
0
def clement_interpolant(source, target_space=None, boundary_tag=None):
    r"""
    Compute the Clement interpolant of a :math:`\mathbb P0`
    source field, i.e. take the volume average over
    neighbouring cells at each vertex.

    :arg source: the :math:`\mathbb P0` source field
    :kwarg target_space: the :math:`\mathbb P1` space to
        interpolate into
    :boundary_tag: optional boundary tag to compute the
        Clement interpolant over.
    """
    V = source.function_space()
    assert V.ufl_element().family() == "Discontinuous Lagrange"
    assert V.ufl_element().degree() == 0
    rank = len(V.ufl_element().value_shape())
    mesh = V.mesh()
    dim = mesh.topological_dimension()
    P1 = firedrake.FunctionSpace(mesh, "CG", 1)
    dX = ufl.dx if boundary_tag is None else ufl.ds(boundary_tag)
    if target_space is None:
        if rank == 0:
            target_space = P1
        elif rank == 1:
            target_space = firedrake.VectorFunctionSpace(mesh, "CG", 1)
        elif rank == 2:
            target_space = firedrake.TensorFunctionSpace(mesh, "CG", 1)
        else:
            raise ValueError(f"Rank-{rank} tensors are not supported.")
    else:
        assert target_space.ufl_element().family() == "Lagrange"
        assert target_space.ufl_element().degree() == 1
    target = firedrake.Function(target_space)

    # Compute the patch volume at each vertex
    if boundary_tag is None:
        P0 = firedrake.FunctionSpace(mesh, "DG", 0)
        dx = ufl.dx(domain=mesh)
        volume = firedrake.assemble(firedrake.TestFunction(P0) * dx)
    else:
        volume = get_facet_areas(mesh)
    patch_volume = firedrake.Function(P1)
    kernel = "for (int i=0; i < p.dofs; i++) p[i] += v[0];"
    keys = {
        "v": (volume, op2.READ),
        "p": (patch_volume, op2.INC),
    }
    firedrake.par_loop(kernel, dX, keys)

    # Volume average
    keys = {
        "s": (source, op2.READ),
        "v": (volume, op2.READ),
        "t": (target, op2.INC),
    }
    if rank == 0:
        firedrake.par_loop(
            """
            for (int i=0; i < t.dofs; i++) {
              t[i] += s[0]*v[0];
            }
            """,
            dX,
            keys,
        )
    elif rank == 1:
        firedrake.par_loop(
            """
            int d = %d;
            for (int i=0; i < t.dofs; i++) {
              for (int j=0; j < d; j++) {
                t[i*d + j] += s[j]*v[0];
              }
            }
            """
            % dim,
            dX,
            keys,
        )
    elif rank == 2:
        firedrake.par_loop(
            """
            int d = %d;
            int Nd = d*d;
            for (int i=0; i < t.dofs; i++) {
              for (int j=0; j < d; j++) {
                for (int k=0; k < d; k++) {
                  t[i*Nd + j*d + k] += s[j*d + k]*v[0];
                }
              }
            }
            """
            % dim,
            dX,
            keys,
        )
    else:
        raise ValueError(f"Rank-{rank} tensors are not supported.")
    target.interpolate(target / patch_volume)
    if boundary_tag is not None:
        target.dat.data_with_halos[:] = np.nan_to_num(target.dat.data_with_halos)
    return target
Esempio n. 19
0
def PartiallyPeriodicRectangleMesh(nx, ny, Lx, Ly, direction="x", quadrilateral=False, reorder=None, comm=COMM_WORLD):
    """Generates RectangleMesh that is periodic in the x or y direction.

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :kwarg direction: The direction of the periodicity (default x).
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    If direction == "x" the boundary edges in this mesh are numbered as follows:

    * 1: plane y == 0
    * 2: plane y == Ly

    If direction == "y" the boundary edges are:

    * 1: plane x == 0
    * 2: plane x == Lx
    """

    if direction not in ("x", "y"):
        raise ValueError("Unsupported periodic direction '%s'" % direction)

    # handle x/y directions: na, La are for the periodic axis
    na, nb, La, Lb = nx, ny, Lx, Ly
    if direction == "y":
        na, nb, La, Lb = ny, nx, Ly, Lx

    if na < 3:
        raise ValueError("2D periodic meshes with fewer than 3 \
cells in each direction are not currently supported")

    m = CylinderMesh(na, nb, 1.0, 1.0, longitudinal_direction="z",
                     quadrilateral=quadrilateral, reorder=reorder, comm=comm)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=2)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    # make x-periodic mesh
    # unravel x coordinates like in periodic interval
    # set y coordinates to z coordinates
    periodic_kernel = """double Y,pi;
            Y = 0.0;
            for(int i=0; i<old_coords.dofs; i++) {
                Y += old_coords[i][1];
            }

            pi=3.141592653589793;
            for(int i=0;i<new_coords.dofs;i++){
            new_coords[i][0] = atan2(old_coords[i][1],old_coords[i][0])/pi/2;
            if(new_coords[i][0]<0.) new_coords[i][0] += 1;
            if(new_coords[i][0]==0 && Y<0.) new_coords[i][0] = 1.0;
            new_coords[i][0] *= Lx[0];
            new_coords[i][1] = old_coords[i][2]*Ly[0];
            }"""

    cLx = Constant(La)
    cLy = Constant(Lb)

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ),
              "Lx": (cLx, READ),
              "Ly": (cLy, READ)})

    if direction == "y":
        # flip x and y coordinates
        operator = np.asarray([[0, 1],
                               [1, 0]])
        new_coordinates.dat.data[:] = np.dot(new_coordinates.dat.data, operator.T)

    return mesh.Mesh(new_coordinates)
Esempio n. 20
0
def PeriodicRectangleMesh(nx, ny, Lx, Ly, quadrilateral=False, reorder=None):
    """Generate a periodic rectangular mesh

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    """

    if nx < 3 or ny < 3:
        raise ValueError("2D periodic meshes with fewer than 3 \
cells in each direction are not currently supported")

    m = TorusMesh(nx,
                  ny,
                  1.0,
                  0.5,
                  quadrilateral=quadrilateral,
                  reorder=reorder)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=2)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """
double pi = 3.141592653589793;
double eps = 1e-12;
double bigeps = 1e-1;
double phi, theta, Y, Z;
Y = 0.0;
Z = 0.0;

for(int i=0; i<old_coords.dofs; i++) {
    Y += old_coords[i][1];
    Z += old_coords[i][2];
}

for(int i=0; i<new_coords.dofs; i++) {
    phi = atan2(old_coords[i][1], old_coords[i][0]);
    if (fabs(sin(phi)) > bigeps)
        theta = atan2(old_coords[i][2], old_coords[i][1]/sin(phi) - 1.0);
    else
        theta = atan2(old_coords[i][2], old_coords[i][0]/cos(phi) - 1.0);

    new_coords[i][0] = phi/(2.0*pi);
    if(new_coords[i][0] < -eps) {
        new_coords[i][0] += 1.0;
    }
    if(fabs(new_coords[i][0]) < eps && Y < 0.0) {
        new_coords[i][0] = 1.0;
    }

    new_coords[i][1] = theta/(2.0*pi);
    if(new_coords[i][1] < -eps) {
        new_coords[i][1] += 1.0;
    }
    if(fabs(new_coords[i][1]) < eps && Z < 0.0) {
        new_coords[i][1] = 1.0;
    }

    new_coords[i][0] *= Lx[0];
    new_coords[i][1] *= Ly[0];
}
"""

    cLx = Constant(Lx)
    cLy = Constant(Ly)

    par_loop(
        periodic_kernel, dx, {
            "new_coords": (new_coordinates, WRITE),
            "old_coords": (old_coordinates, READ),
            "Lx": (cLx, READ),
            "Ly": (cLy, READ)
        })

    return mesh.Mesh(new_coordinates)
Esempio n. 21
0
    def __init__(
        self,
        S,
        mesh,
        beta=1,
        gamma=1.0e4,
        bcs=None,
        dx=dx,
        design_domain=None,
        solver_parameters=direct_parameters,
        output_dir="./",
    ):
        """
        Solver class to regularize the shape derivatives as explained in
        Frédéric de Gournay
        Velocity Extension for the Level-set Method and Multiple Eigenvalues in Shape Optimization
        SIAM J. Control Optim., 45(1), 343–367. (25 pages)
        Args:
            S ([type]): Function space of the mesh coordinates
            mesh ([type]): Mesh
            beta ([type], optional): Regularization parameter.
                                    It should be finite multiple of the mesh size.
                                    Defaults to 1.
            gamma ([type], optional): Penalty parameter for the penalization of the normal components
                                      of the regularized shape derivatives on the boundary. Defaults to 1.0e4.
            bcs ([type], optional): Dirichlet Boundary conditions.
                                    They should be setting the regularized shape derivatives to zero
                                    wherever there are boundary conditions on the original PDE.
                                    Defaults to None.
            dx ([type], optional): [description]. Defaults to dx.
            design_domain ([type], optional): If we're interested in setting the shape derivatives to
                                              zero outside of the design_domain,
                                              we pass design_domain marker.
                                              This is convenient when we want to fix certain regions in the domain.
                                              Defaults to None.
            solver_parameters ([type], optional): Solver options. Defaults to direct_parameters.
            output_dir (str, optional): Plot the output somewhere. Defaults to "./".
        """
        n = FacetNormal(mesh)
        theta, xi = [TrialFunction(S), TestFunction(S)]
        self.xi = xi

        hmin = min_mesh_size(mesh)
        if beta > 20.0 * hmin:
            warning(
                f"Length scale parameter beta ({beta}) is much larger than the mesh size {hmin}"
            )

        self.beta_param = Constant(beta)

        self.a = (self.beta_param * inner(grad(theta), grad(xi)) +
                  inner(theta, xi)) * (dx)
        if isinstance(mesh.topology, ExtrudedMeshTopology):
            ds_reg = ds_b + ds_v + ds_tb + ds_t
        else:
            ds_reg = ds
        self.a += Constant(gamma) * (inner(dot(theta, n), dot(xi, n))) * ds_reg

        # Dirichlet boundary conditions equal to zero for regions where we want
        # the domain to be static, i.e. zero velocities

        if bcs is None:
            self.bcs = []
        else:
            self.bcs = Enlist(bcs)
        if design_domain is not None:
            # Heaviside step function in domain of interest
            V_DG0_B = FunctionSpace(mesh, "DG", 0)
            I_B = Function(V_DG0_B)
            I_B.assign(1.0)
            # Set to zero all the cells within sim_domain
            par_loop(
                ("{[i] : 0 <= i < f.dofs}", "f[i, 0] = 0.0"),
                dx(design_domain),
                {"f": (I_B, WRITE)},
                is_loopy_kernel=True,
            )

            I_cg_B = Function(S)
            dim = S.mesh().geometric_dimension()
            # Assume that `A` is a :class:`.Function` in CG1 and `B` is a
            # `.Function` in DG0. Then the following code sets each DoF in
            # `A` to the maximum value that `B` attains in the cells adjacent to
            # that DoF::
            par_loop(
                (
                    "{{[i, j] : 0 <= i < A.dofs and 0 <= j < {0} }}".format(
                        dim),
                    "A[i, j] = fmax(A[i, j], B[0, 0])",
                ),
                dx,
                {
                    "A": (I_cg_B, RW),
                    "B": (I_B, READ)
                },
                is_loopy_kernel=True,
            )

            import numpy as np

            class MyBC(DirichletBC):
                def __init__(self, V, value, markers):
                    # Call superclass init
                    # We provide a dummy subdomain id.
                    super(MyBC, self).__init__(V, value, 0)
                    # Override the "nodes" property which says where the boundary
                    # condition is to be applied.
                    self.nodes = np.unique(
                        np.where(markers.dat.data_ro_with_halos > 0)[0])

            self.bcs.append(MyBC(S, 0, I_cg_B))

        self.Av = assemble(self.a, bcs=self.bcs)

        self.solver_parameters = solver_parameters
Esempio n. 22
0
def PeriodicRectangleMesh(nx, ny, Lx, Ly, direction="both",
                          quadrilateral=False, reorder=None, comm=COMM_WORLD):
    """Generate a periodic rectangular mesh

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :arg direction: The direction of the periodicity, one of
        ``"both"``, ``"x"`` or ``"y"``.
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    If direction == "x" the boundary edges in this mesh are numbered as follows:

    * 1: plane y == 0
    * 2: plane y == Ly

    If direction == "y" the boundary edges are:

    * 1: plane x == 0
    * 2: plane x == Lx
    """

    if direction not in ("both", "x", "y"):
        raise ValueError("Cannot have a periodic mesh with periodicity '%s'" % direction)
    if direction != "both":
        return PartiallyPeriodicRectangleMesh(nx, ny, Lx, Ly, direction=direction,
                                              quadrilateral=quadrilateral,
                                              reorder=reorder,
                                              comm=comm)
    if nx < 3 or ny < 3:
        raise ValueError("2D periodic meshes with fewer than 3 \
cells in each direction are not currently supported")

    m = TorusMesh(nx, ny, 1.0, 0.5, quadrilateral=quadrilateral, reorder=reorder,
                  comm=comm)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=2)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """
double pi = 3.141592653589793;
double eps = 1e-12;
double bigeps = 1e-1;
double phi, theta, Y, Z;
Y = 0.0;
Z = 0.0;

for(int i=0; i<old_coords.dofs; i++) {
    Y += old_coords[i][1];
    Z += old_coords[i][2];
}

for(int i=0; i<new_coords.dofs; i++) {
    phi = atan2(old_coords[i][1], old_coords[i][0]);
    if (fabs(sin(phi)) > bigeps)
        theta = atan2(old_coords[i][2], old_coords[i][1]/sin(phi) - 1.0);
    else
        theta = atan2(old_coords[i][2], old_coords[i][0]/cos(phi) - 1.0);

    new_coords[i][0] = phi/(2.0*pi);
    if(new_coords[i][0] < -eps) {
        new_coords[i][0] += 1.0;
    }
    if(fabs(new_coords[i][0]) < eps && Y < 0.0) {
        new_coords[i][0] = 1.0;
    }

    new_coords[i][1] = theta/(2.0*pi);
    if(new_coords[i][1] < -eps) {
        new_coords[i][1] += 1.0;
    }
    if(fabs(new_coords[i][1]) < eps && Z < 0.0) {
        new_coords[i][1] = 1.0;
    }

    new_coords[i][0] *= Lx[0];
    new_coords[i][1] *= Ly[0];
}
"""

    cLx = Constant(Lx)
    cLy = Constant(Ly)

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ),
              "Lx": (cLx, READ),
              "Ly": (cLy, READ)})

    return mesh.Mesh(new_coordinates)
Esempio n. 23
0
def PartiallyPeriodicRectangleMesh(nx, ny, Lx, Ly, direction="x", quadrilateral=False, reorder=None, comm=COMM_WORLD):
    """Generates RectangleMesh that is periodic in the x or y direction.

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :kwarg direction: The direction of the periodicity (default x).
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    If direction == "x" the boundary edges in this mesh are numbered as follows:

    * 1: plane y == 0
    * 2: plane y == Ly

    If direction == "y" the boundary edges are:

    * 1: plane x == 0
    * 2: plane x == Lx
    """

    if direction not in ("x", "y"):
        raise ValueError("Unsupported periodic direction '%s'" % direction)

    # handle x/y directions: na, La are for the periodic axis
    na, nb, La, Lb = nx, ny, Lx, Ly
    if direction == "y":
        na, nb, La, Lb = ny, nx, Ly, Lx

    if na < 3:
        raise ValueError("2D periodic meshes with fewer than 3 \
cells in each direction are not currently supported")

    m = CylinderMesh(na, nb, 1.0, 1.0, longitudinal_direction="z",
                     quadrilateral=quadrilateral, reorder=reorder, comm=comm)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=2)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    # make x-periodic mesh
    # unravel x coordinates like in periodic interval
    # set y coordinates to z coordinates
    periodic_kernel = """double Y,pi;
            Y = 0.0;
            for(int i=0; i<old_coords.dofs; i++) {
                Y += old_coords[i][1];
            }

            pi=3.141592653589793;
            for(int i=0;i<new_coords.dofs;i++){
            new_coords[i][0] = atan2(old_coords[i][1],old_coords[i][0])/pi/2;
            if(new_coords[i][0]<0.) new_coords[i][0] += 1;
            if(new_coords[i][0]==0 && Y<0.) new_coords[i][0] = 1.0;
            new_coords[i][0] *= Lx[0];
            new_coords[i][1] = old_coords[i][2]*Ly[0];
            }"""

    cLx = Constant(La)
    cLy = Constant(Lb)

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ),
              "Lx": (cLx, READ),
              "Ly": (cLy, READ)})

    if direction == "y":
        # flip x and y coordinates
        operator = np.asarray([[0, 1],
                               [1, 0]])
        new_coordinates.dat.data[:] = np.dot(new_coordinates.dat.data, operator.T)

    return mesh.Mesh(new_coordinates)
Esempio n. 24
0
    def solve(self, phi: fd.Function, iters: int = 5) -> fd.Function:

        marking = fd.Function(self.DG0)
        marking_bc_nodes = fd.Function(self.V)
        # Mark cells cut by phi(x) = 0
        domain = "{[i, j]: 0 <= i < b.dofs}"
        instructions = """
                <float64> min_value = 1e20
                <float64> max_value = -1e20
                for i
                    min_value = fmin(min_value, b[i, 0])
                    max_value = fmax(max_value, b[i, 0])
                end
                a[0, 0] = 1.0 if (min_value < 0 and max_value > 0) else 0.0
                """
        fd.par_loop(
            (domain, instructions),
            dx,
            {
                "a": (marking, RW),
                "b": (phi, READ)
            },
            is_loopy_kernel=True,
        )
        # Mark the nodes in the marked cells
        fd.par_loop(
            ("{[i] : 0 <= i < A.dofs}", "A[i, 0] = fmax(A[i, 0], B[0, 0])"),
            dx,
            {
                "A": (marking_bc_nodes, RW),
                "B": (marking, READ)
            },
            is_loopy_kernel=True,
        )
        # Mark the nodes in the marked cells
        # Project the gradient of phi on the cut cells
        self.phi.assign(phi)
        V = self.V
        rho, sigma = fd.TrialFunction(V), fd.TestFunction(V)
        a = rho * sigma * marking * dx
        L_proj = (self.phi / sqrt(inner(grad(self.phi), grad(self.phi))) *
                  marking * sigma * dx)
        bc_proj = BCOut(V, fd.Constant(0.0), marking_bc_nodes)
        self.A_proj = fd.assemble(a, tensor=self.A_proj, bcs=bc_proj)
        b_proj = fd.assemble(L_proj, bcs=bc_proj)
        solver_proj = fd.LinearSolver(self.A_proj,
                                      solver_parameters=self.solver_parameters)
        solver_proj.solve(self.phi_int, b_proj)

        def nabla_phi_bar(phi):
            return sqrt(inner(grad(phi), grad(phi)))

        def d1(s):
            return fd.Constant(1.0) - fd.Constant(1.0) / s

        def d2(s):
            return conditional(le(s, fd.Constant(1.0)), s - fd.Constant(1.0),
                               d1(s))

        def residual_phi(phi):
            return fd.norm(
                fd.assemble(
                    d2(nabla_phi_bar(phi)) * inner(grad(phi), grad(sigma)) *
                    dx))

        a = inner(grad(rho), grad(sigma)) * dx
        L = (inner(
            (-d2(nabla_phi_bar(self.phi)) + fd.Constant(1.0)) * grad(self.phi),
            grad(sigma),
        ) * dx)
        bc = BCInt(V, self.phi_int, marking_bc_nodes)
        phi_sol = fd.Function(V)
        A = fd.assemble(a, bcs=bc)
        b = fd.assemble(L, bcs=bc)
        solver = fd.LinearSolver(A, solver_parameters=self.solver_parameters)

        # Solve the Signed distance equation with Picard iteration
        bc.apply(phi_sol)
        init_res = residual_phi(phi_sol)
        res = 1e10
        it = 0
        while res > init_res or it < iters:
            solver.solve(phi_sol, b)
            self.phi.assign(phi_sol)
            b = fd.assemble(L, bcs=bc, tensor=b)
            it += 1
            res = residual_phi(phi_sol)
        if res > init_res:
            fd.warning(
                f"Residual in signed distance function increased: {res}, before: {init_res}"
            )
        return self.phi
Esempio n. 25
0
def PeriodicRectangleMesh(nx, ny, Lx, Ly, direction="both",
                          quadrilateral=False, reorder=None, comm=COMM_WORLD):
    """Generate a periodic rectangular mesh

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :arg direction: The direction of the periodicity, one of
        ``"both"``, ``"x"`` or ``"y"``.
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    If direction == "x" the boundary edges in this mesh are numbered as follows:

    * 1: plane y == 0
    * 2: plane y == Ly

    If direction == "y" the boundary edges are:

    * 1: plane x == 0
    * 2: plane x == Lx
    """

    if direction == "both" and ny == 1 and quadrilateral:
        return OneElementThickMesh(nx, Lx, Ly)

    if direction not in ("both", "x", "y"):
        raise ValueError("Cannot have a periodic mesh with periodicity '%s'" % direction)
    if direction != "both":
        return PartiallyPeriodicRectangleMesh(nx, ny, Lx, Ly, direction=direction,
                                              quadrilateral=quadrilateral,
                                              reorder=reorder,
                                              comm=comm)
    if nx < 3 or ny < 3:
        raise ValueError("2D periodic meshes with fewer than 3 \
cells in each direction are not currently supported")

    m = TorusMesh(nx, ny, 1.0, 0.5, quadrilateral=quadrilateral, reorder=reorder,
                  comm=comm)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=2)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """
double pi = 3.141592653589793;
double eps = 1e-12;
double bigeps = 1e-1;
double phi, theta, Y, Z;
Y = 0.0;
Z = 0.0;

for(int i=0; i<old_coords.dofs; i++) {
    Y += old_coords[i][1];
    Z += old_coords[i][2];
}

for(int i=0; i<new_coords.dofs; i++) {
    phi = atan2(old_coords[i][1], old_coords[i][0]);
    if (fabs(sin(phi)) > bigeps)
        theta = atan2(old_coords[i][2], old_coords[i][1]/sin(phi) - 1.0);
    else
        theta = atan2(old_coords[i][2], old_coords[i][0]/cos(phi) - 1.0);

    new_coords[i][0] = phi/(2.0*pi);
    if(new_coords[i][0] < -eps) {
        new_coords[i][0] += 1.0;
    }
    if(fabs(new_coords[i][0]) < eps && Y < 0.0) {
        new_coords[i][0] = 1.0;
    }

    new_coords[i][1] = theta/(2.0*pi);
    if(new_coords[i][1] < -eps) {
        new_coords[i][1] += 1.0;
    }
    if(fabs(new_coords[i][1]) < eps && Z < 0.0) {
        new_coords[i][1] = 1.0;
    }

    new_coords[i][0] *= Lx[0];
    new_coords[i][1] *= Ly[0];
}
"""

    cLx = Constant(Lx)
    cLy = Constant(Ly)

    par_loop(periodic_kernel, dx,
             {"new_coords": (new_coordinates, WRITE),
              "old_coords": (old_coordinates, READ),
              "Lx": (cLx, READ),
              "Ly": (cLy, READ)})

    return mesh.Mesh(new_coordinates)