Source code for scimba_torch.physical_models.elliptic_pde.laplacians

"""Laplacian operators in 1D and 2D."""

from typing import Callable

import torch

from scimba_torch.approximation_space.abstract_space import AbstractApproxSpace
from scimba_torch.physical_models.elliptic_pde.abstract_elliptic_pde import (
    RitzFormEllipticPDE,
    StrongFormEllipticPDE,
)
from scimba_torch.utils.scimba_tensors import LabelTensor, MultiLabelTensor
from scimba_torch.utils.typing_protocols import VarArgCallable


[docs] class Laplacian1DNeumannStrongForm(StrongFormEllipticPDE): """Implementation of a 1D Laplacian problem with Neumann BCs in strong form. Args: space: The approximation space for the problem. f: Callable representing the source term f(x, μ). g: Callable representing the Neumann boundary condition g(x, μ). **kwargs: Additional keyword arguments. """ def __init__(self, space: AbstractApproxSpace, f: Callable, g: Callable, **kwargs): super().__init__( space, linear=True, residual_size=1, bc_residual_size=1, **kwargs ) self.f = f self.g = g
[docs] def rhs(self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor) -> torch.Tensor: r"""Compute the right-hand side (RHS) of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The source term \( f(x, \mu) \). """ return self.f(x, mu)
[docs] def operator( self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the differential operator of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The result of applying the operator to the state. """ alpha = mu.get_components() u_x = self.grad(w, x) assert isinstance(u_x, torch.Tensor) u_xx = self.grad(u_x, x) assert isinstance(u_xx, torch.Tensor) assert isinstance(alpha, torch.Tensor) return -alpha * u_xx
[docs] def bc_rhs( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: r"""Compute the RHS for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary condition \( g(x, \mu) \). """ return self.g(x, mu)
[docs] def bc_operator( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the operator for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary operator applied to the state. """ u = w.get_components() n_ = n.get_components() u_x = self.grad(u, x) return u_x * n_
[docs] class Laplacian1DDirichletStrongForm(StrongFormEllipticPDE): """Implementation of a 1D Laplacian problem with Dirichlet BCs in strong form. Args: space: The approximation space for the problem. f: Callable representing the source term f(x, μ). g: Callable representing the Dirichlet boundary condition g(x, μ). **kwargs: Additional keyword arguments. """ def __init__(self, space: AbstractApproxSpace, f: Callable, g: Callable, **kwargs): super().__init__( space, linear=True, residual_size=1, bc_residual_size=1, **kwargs ) self.f = f self.g = g
[docs] def rhs(self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor) -> torch.Tensor: """Compute the right-hand side (RHS) of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The source term f(x, μ). """ return self.f(x, mu)
[docs] def operator( self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the differential operator of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The result of applying the operator to the state. """ u = w.get_components() alpha = mu.get_components() u_xx = self.grad(self.grad(u, x), x) return -alpha * u_xx
[docs] def bc_rhs( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the RHS for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary condition g(x, μ). """ return self.g(x, mu)
[docs] def bc_operator( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the operator for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary operator applied to the state. """ return w.get_components()
[docs] class Laplacian2DDirichletStrongForm(StrongFormEllipticPDE): """Implementation of a 2D Laplacian problem with Dirichlet BCs in strong form. Args: space: The approximation space for the problem. f: Callable representing the source term f(x, μ). g: Callable representing the Dirichlet boundary condition g(x, μ). **kwargs: Additional keyword arguments. """ def __init__(self, space: AbstractApproxSpace, f: Callable, g: Callable, **kwargs): super().__init__( space, linear=True, residual_size=1, bc_residual_size=1, **kwargs ) self.f = f self.g = g
[docs] def rhs(self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor) -> torch.Tensor: """Compute the right-hand side (RHS) of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The source term f(x, μ). """ return self.f(x, mu)
[docs] def operator( self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the differential operator of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The result of applying the operator to the state. """ u = w.get_components() alpha = mu.get_components() u_x, u_y = self.grad(u, x) u_xx, _ = self.grad(u_x, x) _, u_yy = self.grad(u_y, x) return -alpha * (u_xx + u_yy)
[docs] def bc_rhs( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the RHS for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary condition g(x, μ). """ return self.g(x, mu)
# Dirichlet condition
[docs] def bc_operator( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the operator for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary operator applied to the state. """ u = w.get_components() return u
[docs] def functional_operator_bc( self, func: VarArgCallable, x: torch.Tensor, n: torch.Tensor, mu: torch.Tensor, theta: torch.Tensor, ) -> torch.Tensor: """Apply the functional operator for boundary conditions. Args: func: The callable function to apply. x: Spatial coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. theta: Theta parameter tensor. Returns: The result of applying the functional operator. """ return func(x, mu, theta)
[docs] def functional_operator( self, func: VarArgCallable, x: torch.Tensor, mu: torch.Tensor, theta: torch.Tensor, ) -> torch.Tensor: """Apply the functional operator for the differential equation. Args: func: The callable function to apply. x: Spatial coordinates tensor. mu: Parameter tensor. theta: Theta parameter tensor. Returns: The result of applying the functional operator. """ grad_u = torch.func.jacrev(func, 0) hessian_u = torch.func.jacrev(grad_u, 0, chunk_size=None)(x, mu, theta) return -mu[0] * (hessian_u[..., 0, 0] + hessian_u[..., 1, 1])
[docs] class Laplacian2DDirichletRitzForm(RitzFormEllipticPDE): """Implementation of a 2D Laplacian problem with Dirichlet BCs in Ritz form. Args: space: The approximation space for the problem. f: Callable representing the source term f(x, μ). g: Callable representing the Dirichlet boundary condition g(x, μ). **kwargs: Additional keyword arguments. """ def __init__(self, space: AbstractApproxSpace, f: Callable, g: Callable, **kwargs): super().__init__(space, bc_residual_size=1, **kwargs) self.f = f self.g = g
[docs] def linearform( self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the linear form of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The linear form result. """ u = w.get_components() return self.f(x, mu) * u
[docs] def quadraticform( self, w: MultiLabelTensor, x: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the quadratic form of the PDE. Args: w: State tensor. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The quadratic form result. """ u = w.get_components() alpha = mu.get_components() u_x, u_y = self.grad(u, x) return 0.5 * alpha * (u_x**2.0 + u_y**2.0)
[docs] def energy_matrix( self, vals: dict, x: torch.Tensor, mu: torch.Tensor ) -> torch.Tensor: """Compute the energy matrix for the Ritz formulation. Args: vals: Dictionary containing precomputed values. x: Spatial coordinates tensor. mu: Parameter tensor. Returns: The energy matrix. """ N = x.shape[0] Phi = (vals["eval_and_gradx_and_gradtheta"]).squeeze() Phi2 = Phi * mu.view(-1, 1, 1) return torch.einsum("ijl,ikl->jk", Phi2, Phi) / N
# def functional_bilinearform( # self, # u: VarArgCallable, # v: VarArgCallable, # x: torch.Tensor, # mu: torch.Tensor, # theta: torch.Tensor, # ) -> torch.Tensor: # grad_u_x_mu_theta = torch.func.grad(u, 0)(x, mu, theta) # grad_v_x_mu_theta = torch.func.grad(v, 0)(x, mu, theta) # return ( # mu[0] # * ( # grad_u_x_mu_theta[0] * grad_v_x_mu_theta[0] # + grad_u_x_mu_theta[1] * grad_v_x_mu_theta[1] # )[None] # )
[docs] def bc_rhs( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the RHS for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary condition g(x, μ). """ return self.g(x, mu)
# Dirichlet condition
[docs] def bc_operator( self, w: MultiLabelTensor, x: LabelTensor, n: LabelTensor, mu: LabelTensor ) -> torch.Tensor: """Compute the operator for the boundary conditions. Args: w: State tensor. x: Boundary coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. Returns: The boundary operator applied to the state. """ u = w.get_components() return u
[docs] def functional_operator_bc( self, func: VarArgCallable, x: torch.Tensor, n: torch.Tensor, mu: torch.Tensor, theta: torch.Tensor, ) -> torch.Tensor: """Apply the functional operator for boundary conditions. Args: func: The callable function to apply. x: Spatial coordinates tensor. n: Normal vector tensor. mu: Parameter tensor. theta: Theta parameter tensor. Returns: The result of applying the functional operator. """ return func(x, mu, theta)