From cdf48653450938b5f065da63f2b06b6799d773bd Mon Sep 17 00:00:00 2001 From: h-risa <119637121+h-risa@users.noreply.github.com> Date: Wed, 29 Apr 2026 05:32:42 -0600 Subject: [PATCH 1/2] Add mypy typing fixes to Python demos --- demo/python/demo_create_and_tabulate.py | 11 +++- demo/python/demo_custom_element.py | 59 +++++++++++++------ .../demo_custom_element_conforming_cr.py | 23 ++++++-- demo/python/demo_dof_transformations.py | 21 +++++-- demo/python/demo_facet_integral.py | 26 ++++++-- demo/python/demo_lagrange_variants.py | 22 +++++-- demo/python/demo_quadrature.py | 24 +++++--- 7 files changed, 141 insertions(+), 45 deletions(-) diff --git a/demo/python/demo_create_and_tabulate.py b/demo/python/demo_create_and_tabulate.py index 19c14d508..658725d07 100644 --- a/demo/python/demo_create_and_tabulate.py +++ b/demo/python/demo_create_and_tabulate.py @@ -13,6 +13,15 @@ import basix from basix import CellType, ElementFamily, LagrangeVariant +# Imports for type checking + +import typing +import numpy.typing as npt + +# Alias for type casting to maintain readability + +FloatArray = npt.NDArray[np.float64] + # Next, we create a degree 4 Lagrange element on a quadrilateral using the function # `create_element`. The first input is the element family: for Lagrange elements, # we use `ElementFamily.P`. The second input is the cell type. The third @@ -38,7 +47,7 @@ # functions (and no derivatives). We pass in the points as the second input. points = np.array([[0.0, 0.0], [0.1, 0.1], [0.2, 0.3], [0.3, 0.6], [0.4, 1.0]]) -tab = lagrange.tabulate(0, points) +tab = typing.cast(FloatArray, lagrange.tabulate(0, points)) print(tab) print(tab.shape) diff --git a/demo/python/demo_custom_element.py b/demo/python/demo_custom_element.py index 5f0ae0875..8f36e92fb 100644 --- a/demo/python/demo_custom_element.py +++ b/demo/python/demo_custom_element.py @@ -12,6 +12,18 @@ import basix from basix import CellType, LatticeType, MapType, PolynomialType, PolysetType, SobolevSpace +# Imporrts for type checking + +import typing + +import numpy.typing as npt + +# Aliases for type casting to maintain readability + +FloatArray = npt.NDArray[np.float64] +FloatingArray = npt.NDArray[np.floating] +QuadratureRule = tuple[FloatArray, FloatArray] + # Lagrange element with bubble # ============================ # @@ -76,11 +88,14 @@ # the largest degree that the integrand will be, so these integrals will # be exact). -pts, wts = basix.make_quadrature(CellType.quadrilateral, 4) -poly = basix.tabulate_polynomials(PolynomialType.legendre, CellType.quadrilateral, 2, pts) -x = pts[:, 0] -y = pts[:, 1] -f = x * (1 - x) * y * (1 - y) +pts, wts = typing.cast(QuadratureRule, basix.make_quadrature(CellType.quadrilateral, 4)) +poly = typing.cast( + FloatArray, + basix.tabulate_polynomials(PolynomialType.legendre, CellType.quadrilateral, 2, pts), +) +x_coord = pts[:, 0] +y_coord = pts[:, 1] +f = x_coord * (1 - x_coord) * y_coord * (1 - y_coord) for i in range(9): wcoeffs[4, i] = sum(f * poly[i, :] * wts) @@ -100,7 +115,7 @@ # # The shape of each of the point lists is (number of points, dimension). -x = [[], [], [], []] +x: list[list[FloatingArray]] = [[], [], [], []] x[0].append(np.array([[0.0, 0.0]])) x[0].append(np.array([[1.0, 0.0]])) x[0].append(np.array([[0.0, 1.0]])) @@ -121,7 +136,7 @@ # The shape of each matrix is (number of DOFs, value size, number of # points, number of derivatives). -M = [[], [], [], []] +M: list[list[FloatingArray]] = [[], [], [], []] for _ in range(4): M[0].append(np.array([[[[1.0]]]])) M[2].append(np.array([[[[1.0]]]])) @@ -156,7 +171,7 @@ element = basix.create_custom_element( CellType.quadrilateral, - [], + (), wcoeffs, x, M, @@ -213,13 +228,16 @@ wcoeffs[0, 0] = 1 wcoeffs[1, 3] = 1 -pts, wts = basix.make_quadrature(CellType.triangle, 2) -poly = basix.tabulate_polynomials(PolynomialType.legendre, CellType.triangle, 1, pts) -x = pts[:, 0] -y = pts[:, 1] +pts, wts = typing.cast(QuadratureRule, basix.make_quadrature(CellType.triangle, 2)) +poly = typing.cast( + FloatArray, + basix.tabulate_polynomials(PolynomialType.legendre, CellType.triangle, 1, pts), +) +x_coord = pts[:, 0] +y_coord = pts[:, 1] for i in range(3): - wcoeffs[2, i] = sum(x * poly[i, :] * wts) - wcoeffs[2, 3 + i] = sum(y * poly[i, :] * wts) + wcoeffs[2, i] = sum(x_coord * poly[i, :] * wts) + wcoeffs[2, 3 + i] = sum(y_coord * poly[i, :] * wts) # Interpolation # ------------- @@ -228,11 +246,11 @@ # the element are integrals. We begin by defining a degree 1 quadrature rule on an interval. # This quadrature rule will be used to integrate on the edges of the triangle. -pts, wts = basix.make_quadrature(CellType.interval, 1) +pts, wts = typing.cast(QuadratureRule, basix.make_quadrature(CellType.interval, 1)) # The points associated with each edge are calculated by mapping the quadrature points to each edge. -x = [[], [], [], []] +x = typing.cast(list[list[FloatingArray]], [[], [], [], []]) for _ in range(3): x[0].append(np.zeros((0, 2))) x[1].append(np.array([[1 - p[0], p[0]] for p in pts])) @@ -245,7 +263,7 @@ # edge, and no extra derivatives are used. The entries of these matrices are the quadrature weights # multiplied by the normal directions. -M = [[], [], [], []] +M = typing.cast(list[list[FloatingArray]], [[], [], [], []]) for _ in range(3): M[0].append(np.zeros((0, 2, 0, 1))) for normal in [[-1, -1], [-1, 0], [0, 1]]: @@ -260,7 +278,7 @@ element = basix.create_custom_element( CellType.triangle, - [2], + (2,), wcoeffs, x, M, @@ -278,5 +296,8 @@ rt = basix.create_element(basix.ElementFamily.RT, CellType.triangle, 1) -points = basix.create_lattice(CellType.triangle, 1, LatticeType.equispaced, True) +points = typing.cast( + FloatArray, + basix.create_lattice(CellType.triangle, 1, LatticeType.equispaced, True), +) assert np.allclose(rt.tabulate(0, points), element.tabulate(0, points)) diff --git a/demo/python/demo_custom_element_conforming_cr.py b/demo/python/demo_custom_element_conforming_cr.py index bf951ae6a..184b1a2b4 100644 --- a/demo/python/demo_custom_element_conforming_cr.py +++ b/demo/python/demo_custom_element_conforming_cr.py @@ -14,6 +14,15 @@ import basix from basix import CellType, LatticeType, MapType, PolynomialType, PolysetType, SobolevSpace +# Imports for type checking + +import typing +import numpy.typing as npt + +# Aliases for type casting to maintain readability + +FloatArray = npt.NDArray[np.float64] + # Conforming CR element on a triangle # =================================== # @@ -125,11 +134,14 @@ def create_ccr_triangle(degree): # We now visualise the basis functions of the element we have created. -pts = basix.create_lattice(CellType.triangle, 30, LatticeType.equispaced, True) +pts = typing.cast( + FloatArray, + basix.create_lattice(CellType.triangle, 30, LatticeType.equispaced, True), +) x = pts[:, 0] y = pts[:, 1] -z = e.tabulate(0, pts)[0] +z = typing.cast(FloatArray, e.tabulate(0, pts))[0] fig = plt.figure(figsize=(8, 8)) @@ -151,11 +163,14 @@ def create_ccr_triangle(degree): e = create_ccr_triangle(3) -pts = basix.create_lattice(CellType.triangle, 30, LatticeType.equispaced, True) +pts = typing.cast( + FloatArray, + basix.create_lattice(CellType.triangle, 30, LatticeType.equispaced, True), +) x = pts[:, 0] y = pts[:, 1] -z = e.tabulate(0, pts)[0] +z = typing.cast(FloatArray, e.tabulate(0, pts))[0] fig = plt.figure(figsize=(11, 8)) diff --git a/demo/python/demo_dof_transformations.py b/demo/python/demo_dof_transformations.py index a6de8b396..d5b766fbc 100644 --- a/demo/python/demo_dof_transformations.py +++ b/demo/python/demo_dof_transformations.py @@ -27,6 +27,16 @@ import basix from basix import CellType, ElementFamily, LagrangeVariant, LatticeType +# Imports for type checking + +import typing +import numpy.typing as npt + +# Aliases for type casting to maintain readability + +FloatArray = npt.NDArray[np.float64] +IntArray = npt.NDArray[np.int_] + # Degree 5 Lagrange element # ========================= # @@ -129,7 +139,10 @@ # To demonstrate how these transformations can be used, we create a # lattice of points where we will tabulate the element. -points = basix.create_lattice(CellType.tetrahedron, 5, LatticeType.equispaced, True) +points = typing.cast( + FloatArray, + basix.create_lattice(CellType.tetrahedron, 5, LatticeType.equispaced, True), +) # If (for example) the direction of edge 2 in the physical cell does # not match its direction on the reference, then we need to adjust the @@ -145,10 +158,10 @@ # and over the value size. For each of these values, we apply the # transformation matrix to the relevant DOFs. -data = nedelec.tabulate(0, points) +data = typing.cast(FloatArray, nedelec.tabulate(0, points)) -transformation = nedelec.entity_transformations()["interval"][0] -dofs = nedelec.entity_dofs[1][2] +transformation = typing.cast(FloatArray, nedelec.entity_transformations()["interval"][0]) +dofs = typing.cast(IntArray, np.asarray(nedelec.entity_dofs[1][2])) for point in range(data.shape[1]): for dim in range(data.shape[3]): diff --git a/demo/python/demo_facet_integral.py b/demo/python/demo_facet_integral.py index 58bcf6358..c426d3654 100644 --- a/demo/python/demo_facet_integral.py +++ b/demo/python/demo_facet_integral.py @@ -18,6 +18,17 @@ import basix from basix import CellType, ElementFamily, LagrangeVariant +# Imports for type checking + +import typing +import numpy.typing as npt + +# Aliases for type casting to maintain readability + +FloatArray = npt.NDArray[np.float64] +IntArray = npt.NDArray[np.int_] +QuadratureRule = tuple[FloatArray, FloatArray] + # We define a degree 3 Lagrange space on a tetrahedron. lagrange = basix.create_element( @@ -28,7 +39,7 @@ # rule on a triangle. We use an order 3 rule so that we can integrate the # basis functions in our space exactly. -points, weights = basix.make_quadrature(CellType.triangle, 3) +points, weights = typing.cast(QuadratureRule, basix.make_quadrature(CellType.triangle, 3)) # Next, we must map the quadrature points to our facet. We use the function # `geometry` to get the coordinates of the vertices of the tetrahedron, and @@ -39,8 +50,11 @@ # # Using this information, we can map the quadrature points to the facet. -vertices = basix.geometry(CellType.tetrahedron) -facet = basix.cell.sub_entity_connectivity(CellType.tetrahedron)[2][0][0] +vertices = typing.cast(FloatArray, basix.geometry(CellType.tetrahedron)) +facet = typing.cast( + IntArray, + basix.cell.sub_entity_connectivity(CellType.tetrahedron)[2][0][0], +) mapped_points = np.array( [ vertices[facet[0]] * (1 - x - y) + vertices[facet[1]] * x + vertices[facet[2]] * y @@ -64,8 +78,8 @@ # We then multiply the three derivatives of the basis function by # the three components of the normal. -normal = basix.cell.facet_outward_normals(CellType.tetrahedron)[0] -tab = lagrange.tabulate(1, mapped_points)[1:, :, 5, 0] +normal = typing.cast(FloatArray, basix.cell.facet_outward_normals(CellType.tetrahedron)[0]) +tab = typing.cast(FloatArray, lagrange.tabulate(1, mapped_points))[1:, :, 5, 0] normal_deriv = tab[0] * normal[0] + tab[1] * normal[1] + tab[2] * normal[2] # As our facet is not the reference triangle, we must multiply the @@ -73,6 +87,6 @@ # cross product of the two columns of the Jacobian, and then compute # the integral. -jacobian = basix.cell.facet_jacobians(CellType.tetrahedron)[0] +jacobian = typing.cast(FloatArray, basix.cell.facet_jacobians(CellType.tetrahedron)[0]) size_jacobian = np.linalg.norm(np.cross(jacobian[:, 0], jacobian[:, 1])) print(np.sum(normal_deriv * weights) * size_jacobian) diff --git a/demo/python/demo_lagrange_variants.py b/demo/python/demo_lagrange_variants.py index c8ce7f09e..1b4b4d081 100644 --- a/demo/python/demo_lagrange_variants.py +++ b/demo/python/demo_lagrange_variants.py @@ -30,6 +30,15 @@ import basix from basix import CellType, ElementFamily, LagrangeVariant, LatticeType +# Imports for type checking + +import typing +import numpy.typing as npt + +# Alias for type casting + +FloatArray = npt.NDArray[np.float64] + # In this demo, we consider Lagrange elements defined on a triangle. We start # by creating a degree 15 Lagrange element that uses equally spaced points. # This element will exhibit Runge's phenomenon, so we expect a large Lebesgue @@ -55,8 +64,11 @@ # # As expected, the value is large. -points = basix.create_lattice(CellType.triangle, 50, LatticeType.equispaced, True) -tab = lagrange.tabulate(0, points)[0] +points = typing.cast( + FloatArray, + basix.create_lattice(CellType.triangle, 50, LatticeType.equispaced, True), +) +tab = typing.cast(FloatArray, lagrange.tabulate(0, points))[0] print(max(np.sum(np.abs(tab), axis=0))) # A Lagrange element with a lower Lebesgue constant can be created by placing @@ -70,7 +82,8 @@ # for the equally spaced element. gll = basix.create_element(ElementFamily.P, CellType.triangle, 15, LagrangeVariant.gll_warped) -print(max(np.sum(np.abs(gll.tabulate(0, points)[0]), axis=0))) +gll_tab = typing.cast(FloatArray, gll.tabulate(0, points))[0] +print(max(np.sum(np.abs(gll_tab), axis=0))) # An even lower Lebesgue constant can be obtained by placing the DOF points # at GLL points mapped onto a triangle following the method proposed in @@ -78,4 +91,5 @@ # Simplices (Isaac, 2020) `_. gll2 = basix.create_element(ElementFamily.P, CellType.triangle, 15, LagrangeVariant.gll_isaac) -print(max(np.sum(np.abs(gll2.tabulate(0, points)[0]), axis=0))) +gll2_tab = typing.cast(FloatArray, gll2.tabulate(0, points))[0] +print(max(np.sum(np.abs(gll2_tab), axis=0))) diff --git a/demo/python/demo_quadrature.py b/demo/python/demo_quadrature.py index 9ecdd7a2c..2345fd0a9 100644 --- a/demo/python/demo_quadrature.py +++ b/demo/python/demo_quadrature.py @@ -19,6 +19,16 @@ import basix from basix import CellType, ElementFamily, LagrangeVariant +# Imports for type checking + +import typing +import numpy.typing as npt + +# Aliases for type casting + +FloatArray = npt.NDArray[np.float64] +QuadratureRule = tuple[FloatArray, FloatArray] + # To get a quadrature rule on a triangle, we use the function `make_quadrature`. # This function takes two or three three inputs. We want to use the default # quadrature, pass in two inputs: a cell type and an order. The order of the rule @@ -29,14 +39,15 @@ # `make_quadrature` returns two values: the points and the weights of the # quadrature rule. -points, weights = basix.make_quadrature(CellType.triangle, 4) +points, weights = typing.cast(QuadratureRule, basix.make_quadrature(CellType.triangle, 4)) # If we want to control the type of quadrature used, we can pass in three # inputs to `make_quadrautre`. For example, the following code would force basix # to use a Gauss-Jacobi quadrature rule: -points, weights = basix.make_quadrature( - CellType.triangle, 4, rule=basix.QuadratureType.gauss_jacobi +points, weights = typing.cast( + QuadratureRule, + basix.make_quadrature(CellType.triangle, 4, rule=basix.QuadratureType.gauss_jacobi), ) # We now use this quadrature rule to integrate the functions :math:`f(x,y)=x^3y` @@ -50,12 +61,11 @@ # We define Python functions that compute :math:`f` and :math:`g` for every point. # These functions use features of Numpy to compute all the values at once. - -def f(points): +def f(points: FloatArray) -> FloatArray: return points[:, 0] ** 3 * points[:, 1] -def g(points): +def g(points: FloatArray) -> FloatArray: return points[:, 0] ** 3 * points[:, 1] ** 2 @@ -77,7 +87,7 @@ def g(points): lagrange = basix.create_element(ElementFamily.P, CellType.triangle, 3, LagrangeVariant.equispaced) -values = lagrange.tabulate(0, points) +values = typing.cast(FloatArray, lagrange.tabulate(0, points)) # We compute the integral of the third (note that the counting starts at 0) basis # function in this space. We can obtain the values of this basis function from From aae19e6cc9b6c8bdeed543f8f3cd48e4b173ff19 Mon Sep 17 00:00:00 2001 From: h-risa <119637121+h-risa@users.noreply.github.com> Date: Wed, 29 Apr 2026 06:17:45 -0600 Subject: [PATCH 2/2] Include Python demos in mypy check --- .github/workflows/pythonapp.yml | 2 +- demo/python/demo_create_and_tabulate.py | 8 +++----- demo/python/demo_custom_element.py | 9 +++------ demo/python/demo_custom_element_conforming_cr.py | 8 +++----- demo/python/demo_dof_transformations.py | 8 +++----- demo/python/demo_facet_integral.py | 8 +++----- demo/python/demo_lagrange_variants.py | 8 +++----- demo/python/demo_quadrature.py | 9 ++++----- 8 files changed, 23 insertions(+), 37 deletions(-) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index c386fd9b6..2ba4b7e79 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -39,7 +39,7 @@ jobs: run: | pip install mypy numpy pip install git+https://github.com/FEniCS/ufl.git - mypy python/basix + mypy python/basix demo/python build: name: Build and test diff --git a/demo/python/demo_create_and_tabulate.py b/demo/python/demo_create_and_tabulate.py index 658725d07..b895afd04 100644 --- a/demo/python/demo_create_and_tabulate.py +++ b/demo/python/demo_create_and_tabulate.py @@ -8,16 +8,14 @@ # # First, we import Basix and Numpy. +import typing # For type checking + import numpy as np +import numpy.typing as npt import basix from basix import CellType, ElementFamily, LagrangeVariant -# Imports for type checking - -import typing -import numpy.typing as npt - # Alias for type casting to maintain readability FloatArray = npt.NDArray[np.float64] diff --git a/demo/python/demo_custom_element.py b/demo/python/demo_custom_element.py index 8f36e92fb..46034efb4 100644 --- a/demo/python/demo_custom_element.py +++ b/demo/python/demo_custom_element.py @@ -7,17 +7,14 @@ # # First, we import Basix and Numpy. +import typing # For type checking + import numpy as np +import numpy.typing as npt import basix from basix import CellType, LatticeType, MapType, PolynomialType, PolysetType, SobolevSpace -# Imporrts for type checking - -import typing - -import numpy.typing as npt - # Aliases for type casting to maintain readability FloatArray = npt.NDArray[np.float64] diff --git a/demo/python/demo_custom_element_conforming_cr.py b/demo/python/demo_custom_element_conforming_cr.py index 184b1a2b4..2902d8d6f 100644 --- a/demo/python/demo_custom_element_conforming_cr.py +++ b/demo/python/demo_custom_element_conforming_cr.py @@ -7,18 +7,16 @@ # # First, we import Basix and Numpy. +import typing # For type checking + import matplotlib.pyplot as plt import numpy as np +import numpy.typing as npt from mpl_toolkits import mplot3d # noqa: F401 import basix from basix import CellType, LatticeType, MapType, PolynomialType, PolysetType, SobolevSpace -# Imports for type checking - -import typing -import numpy.typing as npt - # Aliases for type casting to maintain readability FloatArray = npt.NDArray[np.float64] diff --git a/demo/python/demo_dof_transformations.py b/demo/python/demo_dof_transformations.py index d5b766fbc..795de4aa3 100644 --- a/demo/python/demo_dof_transformations.py +++ b/demo/python/demo_dof_transformations.py @@ -22,16 +22,14 @@ # # First, we import Basix and Numpy. +import typing # For type checking + import numpy as np +import numpy.typing as npt import basix from basix import CellType, ElementFamily, LagrangeVariant, LatticeType -# Imports for type checking - -import typing -import numpy.typing as npt - # Aliases for type casting to maintain readability FloatArray = npt.NDArray[np.float64] diff --git a/demo/python/demo_facet_integral.py b/demo/python/demo_facet_integral.py index c426d3654..35974f111 100644 --- a/demo/python/demo_facet_integral.py +++ b/demo/python/demo_facet_integral.py @@ -13,16 +13,14 @@ # # We start by importing Basis and Numpy. +import typing # For type checking + import numpy as np +import numpy.typing as npt import basix from basix import CellType, ElementFamily, LagrangeVariant -# Imports for type checking - -import typing -import numpy.typing as npt - # Aliases for type casting to maintain readability FloatArray = npt.NDArray[np.float64] diff --git a/demo/python/demo_lagrange_variants.py b/demo/python/demo_lagrange_variants.py index 1b4b4d081..32e551864 100644 --- a/demo/python/demo_lagrange_variants.py +++ b/demo/python/demo_lagrange_variants.py @@ -25,16 +25,14 @@ # # We begin by importing Basix and Numpy. +import typing # For type checking + import numpy as np +import numpy.typing as npt import basix from basix import CellType, ElementFamily, LagrangeVariant, LatticeType -# Imports for type checking - -import typing -import numpy.typing as npt - # Alias for type casting FloatArray = npt.NDArray[np.float64] diff --git a/demo/python/demo_quadrature.py b/demo/python/demo_quadrature.py index 2345fd0a9..88bda6127 100644 --- a/demo/python/demo_quadrature.py +++ b/demo/python/demo_quadrature.py @@ -14,16 +14,14 @@ # # First, we import Basix and Numpy. +import typing # For type checking + import numpy as np +import numpy.typing as npt import basix from basix import CellType, ElementFamily, LagrangeVariant -# Imports for type checking - -import typing -import numpy.typing as npt - # Aliases for type casting FloatArray = npt.NDArray[np.float64] @@ -61,6 +59,7 @@ # We define Python functions that compute :math:`f` and :math:`g` for every point. # These functions use features of Numpy to compute all the values at once. + def f(points: FloatArray) -> FloatArray: return points[:, 0] ** 3 * points[:, 1]