lagrange.polyddg¶
Classes¶
Polygonal mesh discrete differential operators |
Functions¶
Compute per-vertex principal curvatures and principal curvature directions. |
|
|
Compute the globally smoothest n-direction field on a surface mesh. |
Compute the Helmholtz-Hodge decomposition of a 1-form on a closed surface mesh. |
|
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 byadjoint_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
d0with respect to the 1-form inner product. In this implementationdelta1returns 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
d1with 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 columnlis(a_f x e_f^l) / (2 * |a_f|^2), wherea_fis the facet vector area ande_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 fromconnection_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 tolaplacian()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_fis the per-face sharp operator andP_f = I - V_f U_fprojects onto the kernel ofU_f. The result is a symmetric positive-definite sparse matrix (non-diagonal for polygonal meshes).beta = 1is recommended and gives the best accuracy.This is consistent with
star0()andstar2(), 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
DifferentialOperatorsinstance 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_attributeis 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
DifferentialOperatorsfor 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, wheresigma_minis 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
DifferentialOperatorsinstance 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
DifferentialOperatorsinstance 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.