diff --git a/demo/HyperElasticity.py b/demo/HyperElasticity.py index 936715c18..ef3746c5e 100644 --- a/demo/HyperElasticity.py +++ b/demo/HyperElasticity.py @@ -30,7 +30,7 @@ # Cell and its properties cell = tetrahedron -d = cell.geometric_dimension() +d = 3 # Elements u_element = basix.ufl.element("P", cell.cellname(), 2, shape=(3,)) diff --git a/demo/ProjectionManifold.py b/demo/ProjectionManifold.py index 2e973b240..b0e6d5c09 100644 --- a/demo/ProjectionManifold.py +++ b/demo/ProjectionManifold.py @@ -24,10 +24,10 @@ from ufl import FunctionSpace, Mesh, TestFunctions, TrialFunctions, div, dx, inner # Define element over this domain -V = basix.ufl.element("RT", "triangle", 1, gdim=3) -Q = basix.ufl.element("DG", "triangle", 0, gdim=3) +V = basix.ufl.element("RT", "triangle", 1) +Q = basix.ufl.element("DG", "triangle", 0) element = basix.ufl.mixed_element([V, Q]) -domain = Mesh(basix.ufl.element("Lagrange", "triangle", 1, shape=(3,), gdim=3)) +domain = Mesh(basix.ufl.element("Lagrange", "triangle", 1, shape=(3,))) space = FunctionSpace(domain, element) (u, p) = TrialFunctions(space) diff --git a/ffcx/codegeneration/C/expressions.py b/ffcx/codegeneration/C/expressions.py index 874e2cfdd..1f07cd208 100644 --- a/ffcx/codegeneration/C/expressions.py +++ b/ffcx/codegeneration/C/expressions.py @@ -102,19 +102,40 @@ def generator(ir, options): d["constant_names"] = "NULL" code = [] + vs_code = [] # FIXME: Should be handled differently, revise how # ufcx_function_space is generated (also for ufcx_form) - for name, (element, dofmap, cmap_family, cmap_degree) in ir.function_spaces.items(): + for name, ( + element, + dofmap, + cmap_family, + cmap_degree, + cmap_celltype, + cmap_variant, + value_shape, + ) in ir.function_spaces.items(): code += [f"static ufcx_function_space function_space_{name}_{ir.name_from_uflfile} ="] code += ["{"] code += [f".finite_element = &{element},"] code += [f".dofmap = &{dofmap},"] code += [f'.geometry_family = "{cmap_family}",'] - code += [f".geometry_degree = {cmap_degree}"] + code += [f".geometry_degree = {cmap_degree},"] + code += [f".geometry_basix_cell = {int(cmap_celltype)},"] + code += [f".geometry_basix_variant = {int(cmap_variant)},"] + code += [f".value_rank = {len(value_shape)},"] + if len(value_shape) == 0: + code += [".value_shape = NULL"] + else: + vs_code += [ + f"int value_shape_{name}_{ir.name_from_uflfile}[{len(value_shape)}] = {{", + " " + ", ".join([f"{i}" for i in value_shape]), + "};", + ] + code += [f".value_shape = value_shape_{name}_{ir.name_from_uflfile}"] code += ["};"] - d["function_spaces_alloc"] = "\n".join(code) + d["function_spaces_alloc"] = "\n".join(vs_code) + "\n" + "\n".join(code) d["function_spaces"] = "" if len(ir.function_spaces) > 0: diff --git a/ffcx/codegeneration/C/finite_element.py b/ffcx/codegeneration/C/finite_element.py index 72fea2d07..d874a0e6a 100644 --- a/ffcx/codegeneration/C/finite_element.py +++ b/ffcx/codegeneration/C/finite_element.py @@ -25,19 +25,16 @@ def generator(ir, options): """Generate UFC code for a finite element.""" logger.info("Generating code for finite element:") logger.info(f"--- degree: {ir.degree}") - logger.info(f"--- value shape: {ir.value_shape}") + logger.info(f"--- value shape: {ir.reference_value_shape}") logger.info(f"--- name: {ir.name}") d = {} d["factory_name"] = ir.name d["signature"] = f'"{ir.signature}"' - d["geometric_dimension"] = ir.geometric_dimension d["topological_dimension"] = ir.topological_dimension d["cell_shape"] = ir.cell_shape d["element_type"] = ir.element_type d["space_dimension"] = ir.space_dimension - d["value_rank"] = len(ir.value_shape) - d["value_size"] = ufl.product(ir.value_shape) d["reference_value_rank"] = len(ir.reference_value_shape) d["reference_value_size"] = ufl.product(ir.reference_value_shape) d["degree"] = ir.degree @@ -64,15 +61,6 @@ def generator(ir, options): else: d["basix_cell"] = int(ir.basix_cell) - if len(ir.value_shape) > 0: - d["value_shape"] = f"value_shape_{ir.name}" - values = ", ".join(str(i) for i in ir.value_shape) - sizes = len(ir.value_shape) - d["value_shape_init"] = f"int value_shape_{ir.name}[{sizes}] = {{{values}}};" - else: - d["value_shape"] = "NULL" - d["value_shape_init"] = "" - if len(ir.reference_value_shape) > 0: d["reference_value_shape"] = f"reference_value_shape_{ir.name}" values = ", ".join(str(i) for i in ir.reference_value_shape) diff --git a/ffcx/codegeneration/C/finite_element_template.py b/ffcx/codegeneration/C/finite_element_template.py index acb7fbf61..ab9f5d283 100644 --- a/ffcx/codegeneration/C/finite_element_template.py +++ b/ffcx/codegeneration/C/finite_element_template.py @@ -11,7 +11,6 @@ factory = """ // Code for element {factory_name} -{value_shape_init} {reference_value_shape_init} {sub_elements_init} {custom_element_init} @@ -23,11 +22,7 @@ .cell_shape = {cell_shape}, .element_type = {element_type}, .topological_dimension = {topological_dimension}, - .geometric_dimension = {geometric_dimension}, .space_dimension = {space_dimension}, - .value_rank = {value_rank}, - .value_shape = {value_shape}, - .value_size = {value_size}, .reference_value_rank = {reference_value_rank}, .reference_value_shape = {reference_value_shape}, .reference_value_size = {reference_value_size}, diff --git a/ffcx/codegeneration/C/form.py b/ffcx/codegeneration/C/form.py index 00ab81b79..9c48059d6 100644 --- a/ffcx/codegeneration/C/form.py +++ b/ffcx/codegeneration/C/form.py @@ -127,6 +127,7 @@ def generator(ir, options): "form_integral_offsets_init" ] = f"int form_integral_offsets_{ir.name}[{sizes}] = {{{values}}};" + vs_code = [] code = [] # FIXME: Should be handled differently, revise how @@ -138,6 +139,7 @@ def generator(ir, options): cmap_degree, cmap_celltype, cmap_variant, + value_shape, ) in ir.function_spaces.items(): code += [f"static ufcx_function_space functionspace_{name} ="] code += ["{"] @@ -146,7 +148,17 @@ def generator(ir, options): code += [f'.geometry_family = "{cmap_family}",'] code += [f".geometry_degree = {cmap_degree},"] code += [f".geometry_basix_cell = {int(cmap_celltype)},"] - code += [f".geometry_basix_variant = {int(cmap_variant)}"] + code += [f".geometry_basix_variant = {int(cmap_variant)},"] + code += [f".value_rank = {len(value_shape)},"] + if len(value_shape) == 0: + code += [".value_shape = NULL"] + else: + vs_code += [ + f"int value_shape_{ir.name}_{name}[{len(value_shape)}] = {{", + " " + ", ".join([f"{i}" for i in value_shape]), + "};", + ] + code += [f".value_shape = value_shape_{ir.name}_{name}"] code += ["};"] for name in ir.function_spaces.keys(): @@ -155,6 +167,7 @@ def generator(ir, options): code += ["return NULL;\n"] d["functionspace"] = "\n".join(code) + d["value_shape_init"] = "\n".join(vs_code) # Check that no keys are redundant or have been missed from string import Formatter diff --git a/ffcx/codegeneration/C/form_template.py b/ffcx/codegeneration/C/form_template.py index cac4f8711..dfce2f14a 100644 --- a/ffcx/codegeneration/C/form_template.py +++ b/ffcx/codegeneration/C/form_template.py @@ -55,6 +55,7 @@ // Alias name ufcx_form* {name_from_uflfile} = &{factory_name}; +{value_shape_init} ufcx_function_space* functionspace_{name_from_uflfile}(const char* function_name) {{ {functionspace} diff --git a/ffcx/codegeneration/access.py b/ffcx/codegeneration/access.py index 40b0a5760..003ea0812 100644 --- a/ffcx/codegeneration/access.py +++ b/ffcx/codegeneration/access.py @@ -293,10 +293,10 @@ def cell_vertices(self, mt, tabledata, num_points): # Get dimension and dofmap of scalar element assert isinstance(coordinate_element, basix.ufl._BlockedElement) - assert coordinate_element.value_shape == (gdim,) + assert coordinate_element.reference_value_shape == (gdim,) (ufl_scalar_element,) = set(coordinate_element.sub_elements) scalar_element = ufl_scalar_element - assert scalar_element.value_size == 1 and scalar_element.block_size == 1 + assert scalar_element.reference_value_size == 1 and scalar_element.block_size == 1 vertex_scalar_dofs = scalar_element.entity_dofs[0] num_scalar_dofs = scalar_element.dim @@ -327,10 +327,10 @@ def cell_edge_vectors(self, mt, tabledata, num_points): # Get dimension and dofmap of scalar element assert isinstance(coordinate_element, basix.ufl._BlockedElement) - assert coordinate_element.value_shape == (gdim,) + assert coordinate_element.reference_value_shape == (gdim,) (ufl_scalar_element,) = set(coordinate_element.sub_elements) scalar_element = ufl_scalar_element - assert scalar_element.value_size == 1 and scalar_element.block_size == 1 + assert scalar_element.reference_value_size == 1 and scalar_element.block_size == 1 vertex_scalar_dofs = scalar_element.entity_dofs[0] num_scalar_dofs = scalar_element.dim @@ -367,10 +367,10 @@ def facet_edge_vectors(self, mt, tabledata, num_points): # Get dimension and dofmap of scalar element assert isinstance(coordinate_element, basix.ufl._BlockedElement) - assert coordinate_element.value_shape == (gdim,) + assert coordinate_element.reference_value_shape == (gdim,) (ufl_scalar_element,) = set(coordinate_element.sub_elements) scalar_element = ufl_scalar_element - assert scalar_element.value_size == 1 and scalar_element.block_size == 1 + assert scalar_element.reference_value_size == 1 and scalar_element.block_size == 1 scalar_element = ufl_scalar_element num_scalar_dofs = scalar_element.dim diff --git a/ffcx/codegeneration/ufcx.h b/ffcx/codegeneration/ufcx.h index fff83483b..dbaa7f95f 100644 --- a/ffcx/codegeneration/ufcx.h +++ b/ffcx/codegeneration/ufcx.h @@ -92,21 +92,9 @@ extern "C" /// Topological dimension of the cell int topological_dimension; - /// Geometric dimension of the cell - int geometric_dimension; - /// Dimension of the finite element function space int space_dimension; - /// Rank of the value space - int value_rank; - - /// Dimension of the value space for axis i - int* value_shape; - - /// Number of components of the value space - int value_size; - /// Rank of the reference value space int reference_value_rank; @@ -479,6 +467,12 @@ extern "C" /// The Basix variant of the finite element for the geometry map int geometry_basix_variant; + + /// Rank of the value space + int value_rank; + + /// Shape of the value space + int* value_shape; } ufcx_function_space; #ifdef __cplusplus diff --git a/ffcx/ir/representation.py b/ffcx/ir/representation.py index 8746c63b9..f27862962 100644 --- a/ffcx/ir/representation.py +++ b/ffcx/ir/representation.py @@ -47,7 +47,9 @@ class FormIR(typing.NamedTuple): num_coefficients: int num_constants: int name_from_uflfile: str - function_spaces: dict[str, tuple[str, str, str, int, basix.CellType, basix.LagrangeVariant]] + function_spaces: dict[ + str, tuple[str, str, str, int, basix.CellType, basix.LagrangeVariant, tuple[int]] + ] original_coefficient_position: list[int] coefficient_names: list[str] constant_names: list[str] @@ -90,9 +92,7 @@ class ElementIR(typing.NamedTuple): signature: str cell_shape: str topological_dimension: int - geometric_dimension: int space_dimension: int - value_shape: tuple[int, ...] reference_value_shape: tuple[int, ...] degree: int num_sub_elements: int @@ -174,7 +174,9 @@ class ExpressionIR(typing.NamedTuple): coefficient_names: list[str] constant_names: list[str] needs_facet_permutations: bool - function_spaces: dict[str, tuple[str, str, str, int, basix.CellType, basix.LagrangeVariant]] + function_spaces: dict[ + str, tuple[str, str, str, int, basix.CellType, basix.LagrangeVariant, tuple[int]] + ] name_from_uflfile: str original_coefficient_positions: list[int] @@ -289,7 +291,6 @@ def _compute_element_ir(element, element_numbers, finite_element_names): ir["signature"] = repr(element) ir["cell_shape"] = element.cell_type.name ir["topological_dimension"] = cell.topological_dimension() - ir["geometric_dimension"] = cell.geometric_dimension() ir["space_dimension"] = element.dim + element.num_global_support_dofs ir["element_type"] = element.ufcx_element_type ir["lagrange_variant"] = element.lagrange_variant @@ -298,7 +299,6 @@ def _compute_element_ir(element, element_numbers, finite_element_names): ir["basix_cell"] = element.cell_type ir["discontinuous"] = element.discontinuous ir["degree"] = element.degree - ir["value_shape"] = element.value_shape ir["reference_value_shape"] = element.reference_value_shape ir["num_sub_elements"] = element.num_sub_elements @@ -662,12 +662,15 @@ def _compute_form_ir( if not str(name).isidentifier(): raise ValueError(f'Function name "{name}" must be a valid object identifier.') el = function.ufl_function_space().ufl_element() - cmap = function.ufl_function_space().ufl_domain().ufl_coordinate_element() + space = function.ufl_function_space() + domain = space.ufl_domain() + cmap = domain.ufl_coordinate_element() # Default point spacing for CoordinateElement is equispaced if not isinstance(cmap, basix.ufl._ElementBase) and cmap.variant() is None: cmap._sub_element._variant = "equispaced" family = cmap.family_name degree = cmap.degree + value_shape = space.value_shape fs[name] = ( finite_element_names[el], dofmap_names[el], @@ -675,6 +678,7 @@ def _compute_form_ir( degree, cmap.cell_type, cmap.lagrange_variant, + value_shape, ) form_name = object_names.get(id(form_data.original_form), form_id) @@ -782,10 +786,21 @@ def _compute_expression_ir( if not str(name).isidentifier(): raise ValueError(f'Function name "{name}" must be a valid object identifier.') el = function.ufl_function_space().ufl_element() - cmap = function.ufl_function_space().ufl_domain().ufl_coordinate_element() + space = function.ufl_function_space() + domain = space.ufl_domain() + cmap = domain.ufl_coordinate_element() family = cmap.family_name degree = cmap.degree - fs[name] = (finite_element_names[el], dofmap_names[el], family, degree) + value_shape = space.value_shape + fs[name] = ( + finite_element_names[el], + dofmap_names[el], + family, + degree, + cmap.cell_type, + cmap.lagrange_variant, + value_shape, + ) expression_name = object_names.get(id(original_expression), index) diff --git a/test/test_blocked_elements.py b/test/test_blocked_elements.py index e7964b710..d72cb4e78 100644 --- a/test/test_blocked_elements.py +++ b/test/test_blocked_elements.py @@ -19,10 +19,7 @@ def test_finite_element(compile_args): ufcx_element, ufcx_dofmap = jit_compiled_elements[0] assert ufcx_element.topological_dimension == 2 - assert ufcx_element.geometric_dimension == 2 assert ufcx_element.space_dimension == 3 - assert ufcx_element.value_rank == 0 - assert ufcx_element.value_size == 1 assert ufcx_element.reference_value_rank == 0 assert ufcx_element.reference_value_size == 1 assert ufcx_element.block_size == 1 @@ -48,11 +45,7 @@ def test_vector_element(compile_args): ufcx_element, ufcx_dofmap = jit_compiled_elements[0] assert ufcx_element.topological_dimension == 2 - assert ufcx_element.geometric_dimension == 2 assert ufcx_element.space_dimension == 6 - assert ufcx_element.value_rank == 1 - assert ufcx_element.value_shape[0] == 2 - assert ufcx_element.value_size == 2 assert ufcx_element.reference_value_rank == 1 assert ufcx_element.reference_value_shape[0] == 2 assert ufcx_element.reference_value_size == 2 @@ -79,12 +72,7 @@ def test_tensor_element(compile_args): ufcx_element, ufcx_dofmap = jit_compiled_elements[0] assert ufcx_element.topological_dimension == 2 - assert ufcx_element.geometric_dimension == 2 assert ufcx_element.space_dimension == 12 - assert ufcx_element.value_rank == 2 - assert ufcx_element.value_shape[0] == 2 - assert ufcx_element.value_shape[1] == 2 - assert ufcx_element.value_size == 4 assert ufcx_element.reference_value_rank == 2 assert ufcx_element.reference_value_shape[0] == 2 assert ufcx_element.reference_value_shape[1] == 2 @@ -114,11 +102,7 @@ def test_vector_quadrature_element(compile_args): ufcx_element, ufcx_dofmap = jit_compiled_elements[0] assert ufcx_element.topological_dimension == 3 - assert ufcx_element.geometric_dimension == 3 assert ufcx_element.space_dimension == 12 - assert ufcx_element.value_rank == 1 - assert ufcx_element.value_shape[0] == 3 - assert ufcx_element.value_size == 3 assert ufcx_element.reference_value_rank == 1 assert ufcx_element.reference_value_shape[0] == 3 assert ufcx_element.reference_value_size == 3 diff --git a/test/test_jit_forms.py b/test/test_jit_forms.py index 8addbe93b..697619aa7 100644 --- a/test/test_jit_forms.py +++ b/test/test_jit_forms.py @@ -994,13 +994,13 @@ def test_facet_vertex_quadrature(compile_args): def test_manifold_derivatives(compile_args): """Test higher order derivatives on manifolds""" - c_el = basix.ufl.element("Lagrange", "interval", 1, shape=(2,), gdim=2) + c_el = basix.ufl.element("Lagrange", "interval", 1, shape=(2,)) mesh = ufl.Mesh(c_el) x = ufl.SpatialCoordinate(mesh) dx = ufl.Measure("dx", domain=mesh) order = 4 - el = basix.ufl.element("Lagrange", "interval", order, gdim=2) + el = basix.ufl.element("Lagrange", "interval", order) V = ufl.FunctionSpace(mesh, el) u = ufl.Coefficient(V) diff --git a/test/test_tensor_product.py b/test/test_tensor_product.py index 2ce2a511b..a5ef2a370 100644 --- a/test/test_tensor_product.py +++ b/test/test_tensor_product.py @@ -25,14 +25,13 @@ def cell_to_gdim(cell_type): def create_tensor_product_element(cell_type, degree, variant, shape=None): """Create tensor product element.""" - gdim = cell_to_gdim(cell_type) family = basix.ElementFamily.P element = basix.create_tp_element(family, cell_type, degree, variant) - uflelement = basix.ufl.wrap_element(element, gdim=gdim) + uflelement = basix.ufl.wrap_element(element) if shape is None: return uflelement else: - return basix.ufl.blocked_element(uflelement, shape=shape, gdim=gdim) + return basix.ufl.blocked_element(uflelement, shape=shape) def generate_kernel(forms, dtype, options):