Source code for scimba_torch.domain.sdf

"""Defines the signed distance function."""

from abc import ABC, abstractmethod
from typing import List, Tuple

import torch


[docs] class SignedDistance(ABC): """Describe the signed Distance function. Approximative or exact Signed distance function. Args: dim: dimension of the domain threshold: threshold to determinate how we sample inside the domain. We use signedDistance(x) < threshold """ def __init__(self, dim: int, threshold: float = 0.0): super().__init__() self.dim = dim self.threshold = threshold self.sdf = ( self.__call__ ) # Alias for the __call__ method, to be able to use the object as before @abstractmethod def __call__(self, x: torch.Tensor) -> torch.Tensor: # this make a SignedDistance object callable # so instead of doing sdf_obj.sdf(x), we can do sdf_obj(x) # magic methods in python often makes the code more readable :) """Returns the signed distance function at the points of x. Args: x: the ScimbaTensor Returns: the value of the sdf at the points of data """
[docs] class PolygonalApproxSignedDistance(SignedDistance): """Describe the approximate signed Distance function as described in [Sukumar2022]_. Args: dim: dimension of the domain points: coordinates of the vertices of the polygon threshold: threshold to determinate how we sample inside the domain. We use signedDistance(x) < threshold .. [Sukumar2022] N. Sukumar, Ankit Srivastava. Exact imposition of boundary conditions with distance functions in physics-informed deep neural networks. Computer Methods in Applied Mechanics and Engineering, Volume 389, 2022, 114333. """ def __init__( self, dim: int, points: List[Tuple[float, float]] | torch.Tensor, threshold: float = 0.01, ): super().__init__(dim, threshold) self.points: torch.Tensor = ( points if isinstance(points, torch.Tensor) else torch.tensor(points, dtype=torch.get_default_dtype()) )
[docs] def vectorial_product(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor: """Returns the vectorial product between two batched vectors. Args: x: left tensor y: right tensor Returns: the vectorial product between x and y """ if self.dim == 2: res = x[:, 0] * y[:, 1] - y[:, 0] * x[:, 1] return res
[docs] def dot_product(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor: """Returns the scalar product between two batched vectors. Args: x: left tensor y: right tensor Returns: the dot product between x and y """ if self.dim == 2: res = x[:, 0] * y[:, 0] + y[:, 1] * x[:, 1] return res
[docs] def vect_x_to_xi(self, x: torch.Tensor, i: int): """Returns the batched vector x-xi with xi vertices of the polygonal. Args: x: left tensor i: the number of the polygonal point Returns: the batched vector x-xi """ n = x.shape[0] xi = self.points[i, :].repeat((n, 1)) return xi - x
[docs] def dist(self, y: torch.Tensor) -> torch.Tensor: """Returns the batched distance of a batched tensor of vector. Args: y: the tensor Returns: the batched norm of y """ return torch.norm(y, dim=1)
[docs] def phi(self, x: torch.Tensor) -> torch.Tensor: """Returns the batched function phi for a polygonal. Args: x: the tensor Returns: the value of phi at the point x """ res = torch.zeros_like(x[:, 0]) for i in range(0, len(self.points)): j = (i + 1) % len(self.points) ri = self.vect_x_to_xi(x, i) rj = self.vect_x_to_xi(x, j) di = self.dist(ri) dj = self.dist(rj) ti = self.vectorial_product(ri, rj) / (self.dot_product(ri, rj) + di * dj) res = res + (1.0 / di + 1.0 / dj) * ti return res[:, None]
def __call__(self, x: torch.Tensor) -> torch.Tensor: """Returns the batched approximated signed distance for a polygonal. Args: x: the ScimbaTensor Returns: the value of the sdf at the points of data """ return -self.dim / self.phi(x)