Source code for scimba_torch.plots.plots_nd

"""Plotting functions (generic in geometric dimension) for approximation spaces."""

# import warnings
from collections.abc import Sequence
from typing import Any, Callable

import numpy as np

from scimba_common.plots.plots_nd import (
    _plot_abstract_approx_space,
    _plot_abstract_approx_spaces,
)
from scimba_torch.approximation_space.abstract_space import AbstractApproxSpace
from scimba_torch.domain.mesh_based_domain.cuboid import Cuboid
from scimba_torch.domain.meshless_domain.base import SurfacicDomain, VolumetricDomain
from scimba_torch.plots._utils.eval_utilities import (
    eval_on_np_tensors,
    eval_scalar_func_on_np_tensors,
)


[docs] def plot_abstract_approx_space( space: AbstractApproxSpace, spatial_domain: VolumetricDomain | Cuboid, parameters_domain: Sequence[Sequence[float]] = [], time_domain: Sequence[float] = [], velocity_domain: SurfacicDomain | VolumetricDomain | None = None, **kwargs, ): """Plot an AbstractApproxSpace on its domain. Args: space: the space to be plot spatial_domain: the geometric domain on which space is defined parameters_domain: the domain of parameters of space, [] meaning no parameters, time_domain: the time domain of space, [] meaning space is time-independent, velocity_domain: the velocity domain of space, None meaning space has no velocity arguments, **kwargs: arbitrary keyword arguments Keyword Args: parameters_values: a (list of) point(s) in the parameters domain, or "mean" or "random", defaults to "mean", time_values: a (list of) time(s) in the time domain, or "initial" or "final", defaults to "final", velocity_values: a (list of) point(s) in the velocity domain, components: the list of components of the space to be plot, defaults to the list of all the components, loss: a GenericLosses object to be plot, residual: an AbstractPDE object with a residual attribute, derivatives: a list of strings representing the derivatives to be plot, for instance "uxx"; defaults to [], solution: a callable depending on the same args as space to be plot, error: plot the absolute error with respect to the given solution, cuts: for 2D geometric dim, a list of affine spaces of dimension 1, each given as a tuple of 1 point and a basis title: a str ...: see examples Implemented only for 1 and 2 dimensional spaces. Raises: ValueError: some input arguments are not correctly formated Examples: >>> import matplotlib.pyplot as plt >>> from scimba_torch.plots.plots_nd import plot_AbstractApproxSpace >>> ... >>> def exact_sol(x: LabelTensor, mu: LabelTensor): x1, x2 = x.get_components() mu1 = mu.get_components() return mu1 * torch.sin(2.0 * torch.pi * x1) * torch.sin(2.0 * torch.pi * x2) >>> plot_AbstractApproxSpace( pinns.space, #the approximation space domain_x, #the geometric domain [[1.0, 2.0]], #the parameters domain loss=pinns.losses, #the loss residual=pde, #the residual solution=exact_sol, #the reference solution error=exact_sol, #the ref. sol. to plot absolute error derivatives=["ux", "uy"], #a list of string for derivatives cuts=[ #a list of 2 1D cuts ([0.0, 0.0], [-0.5, 0.5]), ([0.0, 0.2], [0.0, 1.0]), ], draw_contours=True, #whether to draw level lines n_drawn_contours=20, #number of level lines title="Learned solution to 2D Laplacian in strong form with weak boundary conditions", ) >>> plt.show() """ if not isinstance(space, AbstractApproxSpace): raise ValueError("first argument (space) must be an AbstractApproxSpace") def eval_approx_space_function( t: np.ndarray, x: np.ndarray, v: np.ndarray, mu: np.ndarray, symb_dict: dict[str, list[str]], component: int | dict[int, int] = 0, **kwargs, ) -> dict[str, np.ndarray]: return eval_on_np_tensors(space, t, x, v, mu, symb_dict, component, **kwargs) def eval_scalar_func_of_space_function( func: Callable, t: np.ndarray, x: np.ndarray, v: np.ndarray, mu: np.ndarray, ) -> np.ndarray: return eval_scalar_func_on_np_tensors(func, space, t, x, v, mu) _plot_abstract_approx_space( eval_approx_space_function, eval_scalar_func_of_space_function, space.nb_unknowns, spatial_domain, parameters_domain, time_domain, velocity_domain, **kwargs, )
def _is_sequence_abstract_approx_space(l_spaces: Any) -> bool: return isinstance(l_spaces, Sequence) and all( isinstance(L, AbstractApproxSpace) for L in l_spaces )
[docs] def plot_abstract_approx_spaces( spaces: AbstractApproxSpace | Sequence[AbstractApproxSpace], spatial_domains: VolumetricDomain | Cuboid | Sequence[VolumetricDomain | Cuboid], parameters_domains: Sequence[Sequence[float]] | Sequence[Sequence[Sequence[float]]] = ([],), time_domains: Sequence[float] | Sequence[Sequence[float]] = ([],), velocity_domains: SurfacicDomain | None | Sequence[SurfacicDomain | None] = None, **kwargs, ) -> None: """Plot a sequence of AbstractApproxSpaces on their domains. Args: spaces: the (sequence of) space(s) to be plot spatial_domains: the (sequence of) geometric domain(s) on which spaces are defined parameters_domains: the (sequence of) domain(s) of parameters of space(s), ([],) meaning no parameters, time_domains: the (sequence of) time domain(s) of space(s), ([],) meaning spaces is time-independent, velocity_domains: the the (sequence of) velocity domain(s) of space(s), None meaning space has no velocity arguments, **kwargs: arbitrary keyword arguments Keyword Args: title: the main title of the figure titles: a sequence of titles (1 for each approximation space) ...: same keyword arguments as in plot_AbstractApproxSpace, are to be given as sequences of n values, where n is the number of spaces; sequences of n same values can be shortcut by the value Implemented only for 1 and 2 dimensional spaces Raises: ValueError: some input arguments are not correctly formated Examples: >>> import matplotlib.pyplot as plt >>> from scimba_torch.plots.plots_nd import plot_AbstractApproxSpaces >>> ... >>> def exact_sol(x: LabelTensor, mu: LabelTensor): x1, x2 = x.get_components() mu1 = mu.get_components() return mu1 * torch.sin(2.0 * torch.pi * x1) * torch.sin(2.0 * torch.pi * x2) >>> plot_AbstractApproxSpaces( (pinns.space, pinns2.space, pinns3.space,),# a sequence of AbstractSpace domain_x, # shortcut for (domain_x,domain_x,domain_x,) ((1.0, 1.0 + 1e-5),), # the same parameters domain for the 3 spaces loss=( pinns.losses, pinns2.losses, pinns3.losses, ), residual=( pinns.pde, pinns2.pde, pinns3.pde, ), error=exact_sol, draw_contours=True, n_drawn_contours=20, parameters_values="random", ) >>> plt.show() """ nspaces = (spaces,) if isinstance(spaces, AbstractApproxSpace) else spaces if not _is_sequence_abstract_approx_space(nspaces): raise ValueError( "first argument must be a AbstractApproxSpace or a Sequence of " "AbstractApproxSpaces" ) # nbspaces = len(nspaces) eval_approx_space_functions = [ lambda *args, space=space, **kwargs: eval_on_np_tensors(space, *args, **kwargs) for space in nspaces ] eval_scalar_func_of_space_functions = [ lambda func, *args, space=space: eval_scalar_func_on_np_tensors( func, space, *args ) for space in nspaces ] nnb_components = [space.nb_unknowns for space in nspaces] _plot_abstract_approx_spaces( eval_approx_space_functions, eval_scalar_func_of_space_functions, nnb_components, spatial_domains, parameters_domains, time_domains, velocity_domains, **kwargs, )