Source code for scimba_torch.physical_models.temporal_pde.heat_equation

"""Heat equation in strong form."""

from typing import Callable

import torch

from scimba_torch.approximation_space.abstract_space import AbstractApproxSpace
from scimba_torch.physical_models.elliptic_pde.laplacians import (
    Laplacian1DDirichletStrongForm,
    Laplacian1DNeumannStrongForm,
    Laplacian2DDirichletStrongForm,
)
from scimba_torch.physical_models.temporal_pde.abstract_temporal_pde import (
    GenericFirstOrderTemporalPDE,
)
from scimba_torch.utils.scimba_tensors import LabelTensor
from scimba_torch.utils.typing_protocols import VarArgCallable


[docs] class HeatEquation1DStrongForm(GenericFirstOrderTemporalPDE): """Implementation of a 1D heat equation with Neumann BCs in strong form. Args: space: The approximation space for the problem init: Callable for the initial condition f: Source term function (default is zero) g: Neumann boundary condition function (default is zero) **kwargs: Additional keyword arguments """ def __init__( self, space: AbstractApproxSpace, init: Callable, f: Callable | None = None, g: Callable | None = None, **kwargs, ): # Rémi says: below we initialize rhs and bc_rhs of the # space component to default values because it will not be used; # rhs and bc_rhs of the time-dependent PDE are used instead space_component = Laplacian1DNeumannStrongForm(space) super().__init__(space, space_component, init, f, g, **kwargs)
[docs] class HeatEquation1DDirichletStrongForm(GenericFirstOrderTemporalPDE): r"""Implementation of a 1D heat equation with Dirichlet BCs in strong form. Args: space: The approximation space for the problem init: Callable for the initial condition f: Source term function (default is zero) g: Dirichlet boundary condition function (default is zero) **kwargs: Additional keyword arguments """ def __init__( self, space: AbstractApproxSpace, init: Callable, f: Callable | None = None, g: Callable | None = None, **kwargs, ): # Rémi says: below we initialize rhs and bc_rhs of the # space component to default values because it will not be used; # rhs and bc_rhs of the time-dependent PDE are used instead space_component = Laplacian1DDirichletStrongForm(space) super().__init__(space, space_component, init, f, g, **kwargs)
[docs] class HeatEquation2DStrongForm(GenericFirstOrderTemporalPDE): r"""Implementation of a 2D Laplacian problem with Dirichlet BCs in strong form. Args: space: The approximation space for the problem init: Callable for the initial condition f: Source term function (default is zero) g: Dirichlet boundary condition function (default is zero) **kwargs: Additional keyword arguments """ def __init__( self, space: AbstractApproxSpace, init: Callable, f: Callable | None = None, g: Callable | None = None, **kwargs, ): # Rémi says: below we initialize rhs and bc_rhs of the # space component to default values because it will not be used; # rhs and bc_rhs of the time-dependent PDE are used instead space_component = Laplacian2DDirichletStrongForm(space) super().__init__(space, space_component, init, f, g, **kwargs)
[docs] class HeatEquation2DStrongFormImplicit(GenericFirstOrderTemporalPDE): r"""2D heat equation for implicit discrete_pinns. Args: space: the approx. space. init: the rhs of the initial condition. f: the rhs of the residual (default is zero). g: the rhs of the boundary condition (default is zero). **kwargs: Additional keyword arguments. """ dt: float alpha: float def __init__( self, space: AbstractApproxSpace, init: Callable, f: Callable | None = None, g: Callable | None = None, **kwargs, ): # Rémi says: below we initialize rhs and bc_rhs of the # space component to default values because it will not be used; # rhs and bc_rhs of the time-dependent PDE are used instead space_component = Laplacian2DDirichletStrongForm(space) super().__init__(space, space_component, init, f, g, **kwargs) self.dt = kwargs.get("dt", 1e-3) self.alpha = kwargs.get("alpha", 1.0)
[docs] def functional_operator( self, func: VarArgCallable, # t: LabelTensor, x: torch.Tensor, mu: torch.Tensor, theta: torch.Tensor, ) -> torch.Tensor: """Compute the functional operator. Args: func: Callable representing the function x: Spatial coordinate tensor mu: Parameter tensor theta: Additional parameters tensor Returns: Functional operator tensor """ # space operator # grad_func = torch.func.jacrev(func, 0) # space_op = -mu[0] * torch.func.jacrev(grad_func, 0)(x, mu, theta) # return func(x, mu, theta) - self.alpha * self.dt * space_op[0, 0] space_op = self.space_component.functional_operator(func, x, mu, theta) return func(x, mu, theta) - self.alpha * self.dt * space_op
# Dirichlet conditions
[docs] def functional_operator_bc( self, func: VarArgCallable, t: LabelTensor, x: torch.Tensor, n: torch.Tensor, mu: torch.Tensor, theta: torch.Tensor, ) -> torch.Tensor: """Compute the functional operator for boundary conditions. Args: func: Callable representing the function t: Temporal coordinate tensor x: Spatial coordinate tensor n: Normal vector tensor mu: Parameter tensor theta: Additional parameters tensor Returns: Functional operator tensor for boundary conditions """ return func(x, mu, theta)
[docs] def functional_operator_ic( self, func: VarArgCallable, x: torch.Tensor, mu: torch.Tensor, theta: torch.Tensor, ) -> torch.Tensor: """Compute the functional operator for initial conditions. Args: func: Callable representing the function x: Spatial coordinate tensor mu: Parameter tensor theta: Additional parameters tensor Returns: Functional operator tensor for initial conditions """ return func(x, mu, theta)