Source code for physicsnemo.mesh.transformations.deform.sobolev
# SPDX-FileCopyrightText: Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES.
# SPDX-FileCopyrightText: All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Uniform-mass P1 Sobolev deformation for simplicial meshes."""
from __future__ import annotations
from typing import TYPE_CHECKING, Literal
import torch
from jaxtyping import Bool, Float
from physicsnemo.mesh.transformations.deform._utils import (
_mesh_with_deformed_points,
_resolve_point_field,
)
if TYPE_CHECKING:
from physicsnemo.mesh.mesh import Mesh
[docs]
def sobolev_deform(
mesh: "Mesh",
displacement: str
| tuple[str, ...]
| Float[torch.Tensor, "n_points n_spatial_dims"],
*,
length_scale: float,
fixed_points: str | tuple[str, ...] | Bool[torch.Tensor, " n_points"] | None = None,
max_iterations: int = 128,
tolerance: float | None = None,
implementation: Literal["torch", "warp"] | None = None,
) -> "Mesh":
r"""Deform a mesh with a uniform-mass P1 Sobolev displacement.
Filters a dense per-vertex displacement by solving
.. math::
(M + \ell^2 K)u = M d
and returns a new mesh with points ``mesh.points + u``. Here
:math:`M=\bar m I` is a uniform vertex mass matrix scaled by the mean
positive lumped P1 mass. :math:`K` is the P1 stiffness matrix, and
:math:`\ell` is ``length_scale`` in mesh coordinate units. The uniform mass
makes the filter self-adjoint for PyTorch vertex tensors. Connectivity and
attached fields are unchanged.
Call it as ``sobolev_deform(mesh, ...)`` or as
``mesh.sobolev_deform(...)``. The bound method supplies ``mesh``
automatically.
Parameters
----------
mesh : Mesh
Simplicial mesh whose points are deformed. The source is not modified.
displacement : str, tuple[str, ...], or torch.Tensor
Raw displacement with shape
``(mesh.n_points, mesh.n_spatial_dims)``, or a
:attr:`~physicsnemo.mesh.mesh.Mesh.point_data` key resolving to one.
It must match the point dtype and device.
length_scale : float
Nonnegative physical smoothing length. Zero applies the raw
displacement directly at unfixed points.
fixed_points : str, tuple[str, ...], torch.Tensor, or None, optional
Optional bool mask with shape ``(mesh.n_points,)``, or a point-data key
resolving to one. True entries receive zero displacement. Default is
``None``.
max_iterations : int, optional
Maximum PCG iterations. Default is ``128``.
tolerance : float or None, optional
Positive relative residual tolerance. ``None`` selects a
dtype-dependent default. Default is ``None``.
implementation : {"torch", "warp"} or None, optional
Backend override. ``None`` selects Torch on CPU. On CUDA, it selects
Warp for segments, triangles, and tetrahedra when available. It
otherwise selects Torch, with a one-time :class:`RuntimeWarning` when
Warp is unavailable. The Warp backend requires CUDA tensors.
Returns
-------
Mesh
New mesh with Sobolev-filtered points, unchanged connectivity, and
unchanged attached fields.
Notes
-----
Unfixed mesh boundaries use the natural homogeneous Neumann condition.
Constant displacements are retained when no points are fixed. Isolated
points receive their raw displacement.
Both backends participate in autograd through the source points and the raw
displacement. Their reverse-mode derivatives solve the adjoint Helmholtz
system, which makes the operation suitable for smooth vertex-based
optimization. The Warp backend evaluates the geometry vector-Jacobian
product analytically. A forward or adjoint solve that does not reach
``tolerance`` within ``max_iterations`` raises a :class:`RuntimeError`.
Warp supports segment, triangle, and tetrahedron cells. Higher-dimensional
simplices use Torch by default.
Warp CUDA results and point gradients may vary at roundoff between runs.
CUDA Graph capture is not supported because P1 operator assembly and
solver diagnostics are not capture-safe.
Geometry caches are invalidated and topology caches are retained. At
positive length scales, cells must be finite, nondegenerate simplices. The
operation does not detect inverted or self-intersecting output cells.
"""
displacement_t = _resolve_point_field(
mesh,
displacement,
argument_name="displacement",
)
fixed_points_t = (
None
if fixed_points is None
else _resolve_point_field(
mesh,
fixed_points,
argument_name="fixed_points",
)
)
from physicsnemo.nn.functional.geometry.deform import sobolev_deform_points
points = sobolev_deform_points(
mesh.points,
mesh.cells,
displacement_t,
length_scale=length_scale,
fixed_points=fixed_points_t,
max_iterations=max_iterations,
tolerance=tolerance,
implementation=implementation,
)
return _mesh_with_deformed_points(mesh, points)
__all__ = ["sobolev_deform"]