lagrange.polyddg

Classes

DifferentialOperators

Polygonal mesh discrete differential operators

Functions

compute_principal_curvatures(…)

Compute per-vertex principal curvatures and principal curvature directions.

compute_smooth_direction_field(mesh, ops, *[, nrosy, ...])

Compute the globally smoothest n-direction field on a surface mesh.

hodge_decomposition_1_form(…)

Compute the Helmholtz-Hodge decomposition of a 1-form on a closed surface mesh.

hodge_decomposition_vector_field(…)

Compute the Helmholtz-Hodge decomposition of a per-vertex vector field on a surface mesh.

Module Contents

class lagrange.polyddg.DifferentialOperators(mesh)

Polygonal mesh discrete differential operators

Parameters:

mesh (lagrange.core.SurfaceMesh)

adjoint_gradient() scipy.sparse.csc_matrix[float]
adjoint_gradient(vid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=3, None, order='F')]

Compute the adjoint gradient operator for a single vertex (Eq. (24), de Goes et al. 2020).

Returns a (3, k) dense matrix of area-weighted, parallel-transported per-corner gradient vectors, where k is the number of incident faces. Columns are in the same incident-face traversal order used by adjoint_shape_operator(vid).

Parameters:

vid – Vertex index.

Returns:

A dense matrix of shape (3, k) where k is the number of incident faces.

adjoint_shape_operator() scipy.sparse.csc_matrix[float]
adjoint_shape_operator(vid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=2, 2, order='F')]

Compute the adjoint shape operator for a single vertex (Eq. (26), de Goes et al. 2020).

The vertex-centred dual of the per-facet shape operator. Applies the adjoint gradient to the unit normals of the incident faces and symmetrizes the result in the vertex tangent plane. The returned 2x2 matrix is symmetric; its trace divided by two gives the mean curvature at the vertex, and its determinant gives the Gaussian curvature.

Parameters:

vid – Vertex index.

Returns:

A 2x2 dense symmetric matrix.

connection_laplacian(*, beta: float = 1) scipy.sparse.csc_matrix[float]
connection_laplacian(fid: int, *, beta: float = 1) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

Compute the discrete connection Laplacian operator for a single facet.

Parameters:
  • fid – Facet index.

  • beta – Weight of projection term (default: 1).

Returns:

A dense matrix representing the per-facet connection Laplacian operator.

connection_laplacian_nrosy(*, n: int, beta: float = 1) scipy.sparse.csc_matrix[float]
connection_laplacian_nrosy(fid: int, *, n: int, beta: float = 1) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

n-rosy variant of connection_laplacian(fid).

Parameters:
  • fid – Facet index.

  • n – Symmetry order of the rosy field (applies the connection n times).

  • beta – Weight of projection term (default: 1).

Returns:

A dense matrix of size (2*nf, 2*nf) representing the per-facet connection Laplacian.

covariant_derivative() scipy.sparse.csc_matrix[float]
covariant_derivative(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=4, None, order='F')]

Compute the discrete covariant derivative operator for a single facet.

Parameters:

fid – Facet index.

Returns:

A dense matrix representing the per-facet covariant derivative operator.

covariant_derivative_nrosy(*, n: int) scipy.sparse.csc_matrix[float]
covariant_derivative_nrosy(fid: int, n: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=4, None, order='F')]

n-rosy variant of covariant_derivative(fid).

Parameters:
  • fid – Facet index.

  • n – Symmetry order of the rosy field (applies the connection n times).

Returns:

A dense matrix of size (4, 2*nf) representing the per-facet covariant derivative.

covariant_projection(fid)

Compute the discrete covariant projection operator for a single facet.

Parameters:

fid (int) – Facet index.

Returns:

A dense matrix representing the per-facet covariant projection operator.

Return type:

Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=(None, None), order=’F’)]

covariant_projection_nrosy(fid, n)

n-rosy variant of covariant_projection(fid).

Parameters:
  • fid (int) – Facet index.

  • n (int) – Symmetry order of the rosy field (applies the connection n times).

Returns:

A dense matrix of size (2*nf, 2*nf) representing the per-facet covariant projection.

Return type:

Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=(None, None), order=’F’)]

curl()

Compute the discrete polygonal curl operator.

Returns:

A sparse matrix representing the curl operator.

Return type:

scipy.sparse.csc_matrix[float]

d0() scipy.sparse.csc_matrix[float]
d0(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

Compute the discrete d0 operator for a single facet.

The discrete d0 operator for a single facet is a n by n matrix, where n is the number vertices/edges in the facet. It maps a scalar functions defined on the vertices to a 1-form defined on the edges.

Parameters:

fid – Facet index.

Returns:

A dense matrix representing the per-facet d0 operator.

d1() scipy.sparse.csc_matrix[float]
d1(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, order='C')]

Compute the discrete d1 operator for a single facet.

The discrete d1 operator for a single facet is a row vector of size 1 by n, where n is the number edges in the facet. It maps a 1-form defined on the edges to a 2-form defined on the facet.

Parameters:

fid – Facet index.

Returns:

A dense matrix representing the per-facet d1 operator.

delta1(*, beta=1)

Compute the discrete co-differential operator \(\delta_1\) in weak form.

The co-differential is the formal adjoint of d0 with respect to the 1-form inner product. In this implementation delta1 returns the weak-form operator \(d_0^T \cdot M_1\): when applied to a primal per-edge scalar 1-form it produces a dual 0-form (an integrated scalar per vertex star), not a pointwise primal per-vertex quantity. To obtain a primal 0-form one must apply the inverse vertex mass matrix, e.g. \(M_0^{-1} \cdot \text{delta1}(\text{beta}) \cdot \alpha\) for a 1-form \(\alpha\).

Equal to divergence(beta) in weak form.

Parameters:

beta (float) – Weight of projection term for the 1-form inner product (default: 1).

Returns:

A sparse matrix of size (#V, #E) implementing \(d_0^T \cdot M_1\).

Return type:

scipy.sparse.csc_matrix[float]

delta2()

Compute the discrete co-differential operator (\(\delta_2 : \Omega^2 \to \Omega^1\)).

The co-differential is the formal adjoint of d1 with respect to the 2-form inner product. It maps a per-facet scalar 2-form to a per-edge scalar 1-form.

Returns:

A sparse matrix of size (#E, #F).

Return type:

scipy.sparse.csc_matrix[float]

divergence(*, beta=1)

Compute the discrete polygonal divergence operator.

Parameters:

beta (float) – Weight of projection term for the 1-form inner product (default: 1).

Returns:

A sparse matrix representing the divergence operator.

Return type:

scipy.sparse.csc_matrix[float]

facet_basis(fid)

Compute the local tangent basis for a single facet.

Parameters:

fid (int) – Facet index.

Returns:

A 3x2 matrix whose columns are orthonormal tangent vectors.

Return type:

Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=(3, 2), order=’F’)]

facet_tangent_coordinates()

Compute the per-facet coordinate transformation from the global 3D frame to the local tangent basis at each facet.

Returns:

A sparse matrix of size (#F * 2, #F * 3).

Return type:

scipy.sparse.csc_matrix[float]

flat() scipy.sparse.csc_matrix[float]
flat(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, 3, order='F')]

Compute the discrete flat operator for a single facet.

The discrete flat operator for a single facet is a n by 3 matrix, where n is the number of edges of the facet. It maps a vector field defined on the facet to a 1-form defined on the edges of the facet.

Parameters:

fid – Facet index.

Returns:

A Nx3 dense matrix representing the per-facet flat operator.

gradient() scipy.sparse.csc_matrix[float]
gradient(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=3, None, order='F')]

Compute the per-corner gradient vectors for a single facet (Eq. (8), de Goes et al. 2020).

Returns a (3, nf) matrix whose column l is (a_f x e_f^l) / (2 * |a_f|^2), where a_f is the facet vector area and e_f^l = x_{l-1} - x_{l+1} spans the opposite edge.

Parameters:

fid – Facet index.

Returns:

A dense matrix of shape (3, nf).

inner_product_0_form() scipy.sparse.csc_matrix[float]
inner_product_0_form(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

Compute the discrete inner product operator for 0-forms for a single facet.

Parameters:

fid – Facet index.

Returns:

A dense matrix representing the per-facet inner product operator for 0-forms.

inner_product_1_form(*, beta: float = 1) scipy.sparse.csc_matrix[float]
inner_product_1_form(fid: int, beta: float = 1) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

Compute the discrete inner product operator for 1-forms for a single facet.

Parameters:
  • fid – Facet index.

  • beta – Weight of projection term (default: 1).

Returns:

A dense matrix representing the per-facet inner product operator for 1-forms.

inner_product_2_form() scipy.sparse.csc_matrix[float]
inner_product_2_form(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=1, order='C')]

Compute the discrete inner product operator for 2-forms for a single facet.

Parameters:

fid – Facet index.

Returns:

A 1x1 dense matrix representing the per-facet inner product operator for 2-forms.

laplacian(*, beta: float = 1) scipy.sparse.csc_matrix[float]
laplacian(fid: int, *, beta: float = 1) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

Compute the discrete Laplacian operator for a single facet.

Parameters:
  • fid – Facet index.

  • beta – Weight of projection term (default: 1).

Returns:

A dense matrix representing the per-facet Laplacian operator.

laplacian1(*, beta=1)

Compute the discrete Hodge Laplacian on 1-forms (\(\Delta_1 : \Omega^1 \to \Omega^1\)).

Combines the exact part (d0 · delta1(beta)) and the co-exact part (delta2() · d1()). Required for full Helmholtz-Hodge decomposition. Distinct from connection_laplacian(), which acts on tangent vector fields at vertices.

Parameters:

beta (float) – Weight of projection term for the 1-form inner product (default: 1).

Returns:

A sparse matrix of size (#E, #E).

Return type:

scipy.sparse.csc_matrix[float]

laplacian2()

Compute the discrete Laplacian on 2-forms (\(\Delta_2 : \Omega^2 \to \Omega^2\)).

Equal to d1 · delta2(). Analogous to laplacian() but acting on per-facet scalar 2-forms. Required for computing the co-exact component in Helmholtz-Hodge decomposition.

Returns:

A sparse matrix of size (#F, #F).

Return type:

scipy.sparse.csc_matrix[float]

levi_civita() scipy.sparse.csc_matrix[float]
levi_civita(fid: int, lv: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=2, 2, order='F')]
levi_civita(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

Compute the discrete Levi-Civita connection for a single facet.

Parameters:

fid – Facet index.

Returns:

A dense matrix representing the per-facet Levi-Civita connection.

levi_civita_nrosy(*, n: int) scipy.sparse.csc_matrix[float]
levi_civita_nrosy(fid: int, lv: int, *, n: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=2, 2, order='F')]
levi_civita_nrosy(fid: int, *, n: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

n-rosy variant of levi_civita(fid).

Parameters:
  • fid – Facet index.

  • n – Symmetry order of the rosy field (applies the connection n times).

Returns:

A dense matrix of size (2*nf, 2*nf) representing the per-facet Levi-Civita connection.

projection() scipy.sparse.csc_matrix[float]
projection(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=None, None, order='F')]

Compute the discrete projection operator for a single facet.

Parameters:

fid – Facet index.

Returns:

A dense matrix representing the per-facet projection operator.

shape_operator() scipy.sparse.csc_matrix[float]
shape_operator(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=2, 2, order='F')]

Compute the discrete shape operator for a single facet (Eq. (23), de Goes et al. 2020).

Applies the per-facet gradient to the precomputed per-vertex normals and symmetrizes the result in the facet tangent plane. The returned 2x2 matrix is symmetric; its trace divided by two gives the mean curvature at the facet, and its determinant gives the Gaussian curvature.

Parameters:

fid – Facet index.

Returns:

A 2x2 dense symmetric matrix.

sharp() scipy.sparse.csc_matrix[float]
sharp(fid: int) Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=3, None, order='F')]

Compute the discrete sharp operator for a single facet.

Parameters:

fid – Facet index.

Returns:

A 3xN dense matrix representing the per-facet sharp operator.

star0()

Compute the discrete Hodge star operator for 0-forms (diagonal mass matrix, size #V x #V).

Returns:

A diagonal sparse matrix of size (#V, #V).

Return type:

scipy.sparse.csc_matrix[float]

star1(beta=1.0)

Compute the discrete Hodge star operator for 1-forms (size #E x #E).

Following de Goes, Butts and Desbrun (ACM Trans. Graph. 2020, Section 4.4), this is the VEM-stabilized 1-form inner product assembled from per-face Gram matrices:

M_f = area_f * U_f^T U_f  +  beta * P_f^T P_f

where U_f is the per-face sharp operator and P_f = I - V_f U_f projects onto the kernel of U_f. The result is a symmetric positive-definite sparse matrix (non-diagonal for polygonal meshes). beta = 1 is recommended and gives the best accuracy.

This is consistent with star0() and star2(), which also delegate to their respective inner-product operators.

Parameters:

beta (float) – Stabilization weight for the VEM projection term (default 1).

Returns:

A symmetric positive-definite sparse matrix of size (#E, #E).

Return type:

scipy.sparse.csc_matrix[float]

star2()

Compute the discrete Hodge star operator for 2-forms (diagonal mass matrix, size #F x #F).

Returns:

A diagonal sparse matrix of size (#F, #F).

Return type:

scipy.sparse.csc_matrix[float]

vertex_basis(vid)

Compute the local tangent basis for a single vertex.

Parameters:

vid (int) – Vertex index.

Returns:

A 3x2 matrix whose columns are orthonormal tangent vectors.

Return type:

Annotated[numpy.typing.NDArray[numpy.float64], dict(shape=(3, 2), order=’F’)]

vertex_tangent_coordinates()

Compute the per-vertex coordinate transformation from the global 3D frame to the local tangent basis at each vertex.

Returns:

A sparse matrix of size (#V * 2, #V * 3).

Return type:

scipy.sparse.csc_matrix[float]

property centroid_attribute_id: int

Attribute ID of the per-facet centroid attribute.

Return type:

int

property vector_area_attribute_id: int

Attribute ID of the per-facet vector area attribute.

Return type:

int

property vertex_normal_attribute_id: int

Attribute ID of the per-vertex normal attribute.

Return type:

int

lagrange.polyddg.compute_principal_curvatures(mesh: lagrange.core.SurfaceMesh, ops: DifferentialOperators, *, kappa_min_attribute: str = '@kappa_min', kappa_max_attribute: str = '@kappa_max', direction_min_attribute: str = '@principal_direction_min', direction_max_attribute: str = '@principal_direction_max') tuple[int, int, int, int]
lagrange.polyddg.compute_principal_curvatures(mesh: lagrange.core.SurfaceMesh, *, kappa_min_attribute: str = '@kappa_min', kappa_max_attribute: str = '@kappa_max', direction_min_attribute: str = '@principal_direction_min', direction_max_attribute: str = '@principal_direction_max') tuple[int, int, int, int]

Compute per-vertex principal curvatures and principal curvature directions.

Eigendecomposes the adjoint shape operator at each vertex. A DifferentialOperators instance is constructed internally. The eigenvalues (kappa_min <= kappa_max) are the principal curvatures and the eigenvectors, mapped back to 3-D through the vertex tangent basis, are the principal directions. All four quantities are stored as vertex attributes in the mesh.

Parameters:
  • mesh – Input surface mesh (modified in place with new attributes).

  • kappa_min_attribute – Output attribute name for the minimum principal curvature (default: "@kappa_min").

  • kappa_max_attribute – Output attribute name for the maximum principal curvature (default: "@kappa_max").

  • direction_min_attribute – Output attribute name for the kappa_min direction (default: "@principal_direction_min").

  • direction_max_attribute – Output attribute name for the kappa_max direction (default: "@principal_direction_max").

Returns:

A tuple (kappa_min_id, kappa_max_id, direction_min_id, direction_max_id) of attribute IDs.

lagrange.polyddg.compute_smooth_direction_field(mesh, ops, *, nrosy=4, beta=1.0, alignment_attribute='', alignment_weight=1.0, direction_field_attribute='@smooth_direction_field')

Compute the globally smoothest n-direction field on a surface mesh.

Based on: Knöppel et al., “Globally optimal direction fields”, ACM ToG 32(4), 2013.

Without alignment constraints (alignment_attribute is empty), solves the generalized eigenvalue problem \(L u = \sigma M u\) for the smallest eigenvector. The result minimizes the Dirichlet energy of the connection.

With alignment constraints, reads per-vertex prescribed 3-D tangent vectors from the given attribute (zero-length vectors are unconstrained) and solves the shifted linear system \((L - \alpha M) u = M q\), where \(q\) is the M-normalized prescribed field and \(\alpha = \texttt{alignment\_lambda} \cdot \sigma_{\min}\).

Parameters:
  • mesh (lagrange.core.SurfaceMesh) – Input surface mesh (modified in place with the new attribute).

  • ops (DifferentialOperators) – Precomputed DifferentialOperators for the mesh.

  • nrosy (int) – Symmetry order of the direction field (1 = vector field, 2 = line field, 4 = cross field, default: 4).

  • beta (float) – Stabilization weight for the VEM projection term in the connection Laplacian (default: 1).

  • alignment_attribute (str) – Name of a per-vertex 3-D alignment vector attribute (zero = unconstrained). If empty, the unconstrained smoothest field is computed.

  • alignment_weight (float) – Scaling factor for the spectral shift (default: 1). The actual shift is alignment_weight * sigma_min, where sigma_min is the smallest eigenvalue of the connection Laplacian (computed automatically). Values in (0, 1) give weaker alignment (more smoothness).

  • direction_field_attribute (str) – Output attribute name for the per-vertex 3-D direction field (default: "@smooth_direction_field").

Returns:

Attribute ID of the output per-vertex direction field.

Return type:

int

lagrange.polyddg.hodge_decomposition_1_form(mesh: lagrange.core.SurfaceMesh, ops: DifferentialOperators, *, input_attribute: str = '@hodge_input', exact_attribute: str = '@hodge_exact', coexact_attribute: str = '@hodge_coexact', harmonic_attribute: str = '@hodge_harmonic', beta: float = 1.0) tuple[int, int, int]
lagrange.polyddg.hodge_decomposition_1_form(mesh: lagrange.core.SurfaceMesh, *, input_attribute: str = '@hodge_input', exact_attribute: str = '@hodge_exact', coexact_attribute: str = '@hodge_coexact', harmonic_attribute: str = '@hodge_harmonic', beta: float = 1.0) tuple[int, int, int]

Compute the Helmholtz-Hodge decomposition of a 1-form on a closed surface mesh.

Convenience overload that constructs a DifferentialOperators instance internally.

Parameters:
  • mesh – Input surface mesh (modified in place with new attributes). The input attribute (per-edge scalar) must already exist on the mesh.

  • input_attribute – Edge attribute name of the input 1-form (default: "@hodge_1form_input").

  • exact_attribute – Output edge attribute name for the exact component (default: "@hodge_1form_exact").

  • coexact_attribute – Output edge attribute name for the co-exact component (default: "@hodge_1form_coexact").

  • harmonic_attribute – Output edge attribute name for the harmonic component (default: "@hodge_1form_harmonic").

  • beta – Stabilization weight for the VEM 1-form inner product (default: 1).

Returns:

A tuple (exact_id, coexact_id, harmonic_id) of edge attribute IDs.

lagrange.polyddg.hodge_decomposition_vector_field(mesh: lagrange.core.SurfaceMesh, ops: DifferentialOperators, *, input_attribute: str = '@hodge_input', exact_attribute: str = '@hodge_exact', coexact_attribute: str = '@hodge_coexact', harmonic_attribute: str = '@hodge_harmonic', beta: float = 1.0, nrosy: int = 1) tuple[int, int, int]
lagrange.polyddg.hodge_decomposition_vector_field(mesh: lagrange.core.SurfaceMesh, *, input_attribute: str = '@hodge_input', exact_attribute: str = '@hodge_exact', coexact_attribute: str = '@hodge_coexact', harmonic_attribute: str = '@hodge_harmonic', beta: float = 1.0, nrosy: int = 1) tuple[int, int, int]

Compute the Helmholtz-Hodge decomposition of a per-vertex vector field on a surface mesh.

Convenience overload that constructs a DifferentialOperators instance internally.

When nrosy > 1, the input is treated as one representative vector of an n-rosy field.

Parameters:
  • mesh – Input surface mesh (modified in place with new attributes). The input attribute (per-vertex 3D vector) must already exist on the mesh.

  • input_attribute – Vertex attribute name of the input vector field (default: "@hodge_input").

  • exact_attribute – Output vertex attribute name for the exact component (default: "@hodge_exact").

  • coexact_attribute – Output vertex attribute name for the co-exact component (default: "@hodge_coexact").

  • harmonic_attribute – Output vertex attribute name for the harmonic component (default: "@hodge_harmonic").

  • beta – Stabilization weight for the VEM 1-form inner product (default: 1).

  • nrosy – N-rosy symmetry order (default: 1 for plain vector fields).

Returns:

A tuple (exact_id, coexact_id, harmonic_id) of vertex attribute IDs.