def CubedSphereMesh(radius, refinement_level=0, degree=1, reorder=None, use_dmplex_refinement=False, comm=COMM_WORLD): """Generate an cubed approximation to the surface of the sphere. :arg radius: The radius of the sphere to approximate. :kwarg refinement_level: optional number of refinements (0 is a cube). :kwarg degree: polynomial degree of coordinate space (defaults to 1: bilinear quads) :kwarg reorder: (optional), should the mesh be reordered? :kwarg use_dmplex_refinement: (optional), use dmplex to apply the refinement. """ if refinement_level < 0 or refinement_level % 1: raise RuntimeError("Number of refinements must be a non-negative integer") if degree < 1: raise ValueError("Mesh coordinate degree must be at least 1") cells, coords = _cubedsphere_cells_and_coords(radius, refinement_level) plex = mesh._from_cell_list(2, cells, coords, comm) m = mesh.Mesh(plex, dim=3, reorder=reorder) if degree > 1: new_coords = function.Function(functionspace.VectorFunctionSpace(m, "Q", degree)) new_coords.interpolate(expression.Expression(("x[0]", "x[1]", "x[2]"))) # "push out" to sphere new_coords.dat.data[:] *= (radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape(-1, 1) m = mesh.Mesh(new_coords) return m
def IcosahedralSphereMesh(radius, refinement_level=0, degree=1, reorder=None): """Generate an icosahedral approximation to the surface of the sphere. :arg radius: The radius of the sphere to approximate. For a radius R the edge length of the underlying icosahedron will be. .. math:: a = \\frac{R}{\\sin(2 \\pi / 5)} :kwarg refinement_level: optional number of refinements (0 is an icosahedron). :kwarg degree: polynomial degree of coordinate space (defaults to 1: flat triangles) :kwarg reorder: (optional), should the mesh be reordered? """ if degree < 1: raise ValueError("Mesh coordinate degree must be at least 1") from math import sqrt phi = (1 + sqrt(5)) / 2 # vertices of an icosahedron with an edge length of 2 vertices = np.array([[-1, phi, 0], [1, phi, 0], [-1, -phi, 0], [1, -phi, 0], [0, -1, phi], [0, 1, phi], [0, -1, -phi], [0, 1, -phi], [phi, 0, -1], [phi, 0, 1], [-phi, 0, -1], [-phi, 0, 1]]) # faces of the base icosahedron faces = np.array( [[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1]], dtype=np.int32) plex = mesh._from_cell_list(2, faces, vertices) plex.setRefinementUniform(True) for i in range(refinement_level): plex = plex.refine() coords = plex.getCoordinatesLocal().array.reshape(-1, 3) scale = (radius / np.linalg.norm(coords, axis=1)).reshape(-1, 1) coords *= scale m = mesh.Mesh(plex, dim=3, reorder=reorder) if degree > 1: new_coords = function.Function( functionspace.VectorFunctionSpace(m, "CG", degree)) new_coords.interpolate(expression.Expression(("x[0]", "x[1]", "x[2]"))) # "push out" to sphere new_coords.dat.data[:] *= ( radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape( -1, 1) m = mesh.Mesh(new_coords) m._icosahedral_sphere = radius return m
def interpolate(self, expression, subset=None): """Interpolate an expression onto this :class:`Function`. :param expression: :class:`.Expression` to interpolate :returns: this :class:`Function` object""" # Make sure we have an expression of the right length i.e. a value for # each component in the value shape of each function space dims = [ np.prod(fs.ufl_element().value_shape(), dtype=int) for fs in self.function_space() ] if np.prod(expression.value_shape(), dtype=int) != sum(dims): raise RuntimeError( 'Expression of length %d required, got length %d' % (sum(dims), np.prod(expression.value_shape(), dtype=int))) if hasattr(expression, 'eval'): fs = self.function_space() if isinstance(fs, functionspace.MixedFunctionSpace): raise NotImplementedError( "Python expressions for mixed functions are not yet supported." ) self._interpolate(fs, self.dat, expression, subset) else: # Slice the expression and pass in the right number of values for # each component function space of this function d = 0 for fs, dat, dim in zip(self.function_space(), self.dat, dims): idx = d if fs.rank == 0 else slice(d, d + dim) if fs.rank == 0: code = expression.code[idx] else: # Reshape so the sub-expression we build has the right shape. code = expression.code[idx].reshape( fs.ufl_element().value_shape()) self._interpolate( fs, dat, expression_t.Expression(code, **expression._kwargs), subset) d += dim return self
def CubedSphereMesh(radius, refinement_level=0, degree=1, reorder=None, use_dmplex_refinement=False): """Generate an cubed approximation to the surface of the sphere. :arg radius: The radius of the sphere to approximate. :kwarg refinement_level: optional number of refinements (0 is a cube). :kwarg degree: polynomial degree of coordinate space (defaults to 1: bilinear quads) :kwarg reorder: (optional), should the mesh be reordered? :kwarg use_dmplex_refinement: (optional), use dmplex to apply the refinement. """ if degree < 1: raise ValueError("Mesh coordinate degree must be at least 1") if use_dmplex_refinement: # vertices of a cube with an edge length of 2 vertices = np.array([[-1., -1., -1.], [1., -1., -1.], [-1., 1., -1.], [1., 1., -1.], [-1., -1., 1.], [1., -1., 1.], [-1., 1., 1.], [1., 1., 1.]]) # faces of the base cube # bottom face viewed from above # 2 3 # 0 1 # top face viewed from above # 6 7 # 4 5 faces = np.array([[0, 1, 3, 2], # bottom [4, 5, 7, 6], # top [0, 1, 5, 4], [2, 3, 7, 6], [0, 2, 6, 4], [1, 3, 7, 5]], dtype=np.int32) plex = mesh._from_cell_list(2, faces, vertices) plex.setRefinementUniform(True) for i in range(refinement_level): plex = plex.refine() # rescale points to the sphere # this is not the same as the gnonomic transformation coords = plex.getCoordinatesLocal().array.reshape(-1, 3) scale = (radius / np.linalg.norm(coords, axis=1)).reshape(-1, 1) coords *= scale else: cells, coords = _cubedsphere_cells_and_coords(radius, refinement_level) plex = mesh._from_cell_list(2, cells, coords) m = mesh.Mesh(plex, dim=3, reorder=reorder) if degree > 1: new_coords = function.Function(functionspace.VectorFunctionSpace(m, "Q", degree)) new_coords.interpolate(expression.Expression(("x[0]", "x[1]", "x[2]"))) # "push out" to sphere new_coords.dat.data[:] *= (radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape(-1, 1) m = mesh.Mesh(new_coords) return m