PhysicsNeMo Sym#

Symbolic PDE residual computation for physics-informed training.

PDE Base Class#

class physicsnemo.sym.eq.pde.PDE[source]#

Bases: object

Base class for all partial differential equations.

Subclasses must populate self.equations (a dict[str, sympy.Expr]) and set self.dim.

Examples

Define a 2-D advection-diffusion equation:

>>> from sympy import Symbol, Function, Number
>>> from physicsnemo.sym.eq.pde import PDE
>>>
>>> class AdvectionDiffusion(PDE):
...     def __init__(self, D=0.1):
...         self.dim = 2
...         x, y = Symbol("x"), Symbol("y")
...         T = Function("T")(x, y)
...         u = Function("u")(x, y)
...         v = Function("v")(x, y)
...         self.equations = {
...             "advection_diffusion": (
...                 u * T.diff(x) + v * T.diff(y)
...                 - D * (T.diff(x, 2) + T.diff(y, 2))
...             ),
...         }
...
>>> pde = AdvectionDiffusion(D=0.01)
>>> pde.pprint()
advection_diffusion: ...
make_computations(
detach_names: list[str] | None = None,
) list[Computation][source]#

Convert each equation into a Computation.

Returns:

One computation per equation in self.equations.

Return type:

list[Computation]

pprint(print_latex: bool = False) None[source]#

Pretty-print the equations.

subs(x, y)[source]#

Substitute x with y in all equations (calls SymPy subs).

PhysicsInformer#

class physicsnemo.sym.eq.phy_informer.PhysicsInformer(
required_outputs: List[str],
equations: PDE,
grad_method: str,
fd_dx: float | List[float] = 0.001,
bounds: List[float] | None = None,
compute_connectivity: bool = True,
detach_names: List[str] | None = None,
device: str | None = None,
)[source]#

Bases: object

Compute the residual of a PDE using automatic spatial derivative computation.

Given a PDE and a list of required_outputs, this class builds a computational graph that automatically computes spatial derivatives and evaluates equation residuals.

Parameters:
  • required_outputs (list[str]) – Equation names to compute (e.g. ["continuity", "momentum_x"]).

  • equations (PDE) – The PDE whose equations dict defines the symbolic residuals.

  • grad_method (str) – One of "autodiff", "meshless_finite_difference", "finite_difference", "spectral", "least_squares".

  • fd_dx (float or list[float]) – Grid spacing for FD / meshless FD methods.

  • bounds (list[float]) – Domain lengths for spectral method.

  • compute_connectivity (bool) – If True and using "least_squares", build the connectivity tensor on the fly from "nodes" and "edges" in the input dict.

  • detach_names (list[str] or None) – Names of variables (and their derivatives) whose tensors will be detached from the computational graph before the compiled PDE equations are evaluated. When a name appears in this list, its value is passed through torch.Tensor.detach() inside the SympyToTorch forward call, so no gradient flows through it during back-propagation. This is useful for inverse problems: for example, when inverting for viscosity nu the flow-field variables and their spatial derivatives (["u", "u__x", "u__x__x", ...]) should be detached so that the physics loss updates only the inversion network for nu while the flow network is trained solely on data-fitting loss.

  • device (str or torch.device or None) – Target device.

Examples

>>> import torch
>>> from sympy import Symbol, Function, Number
>>> from physicsnemo.sym.eq.pde import PDE
>>> from physicsnemo.sym.eq.phy_informer import PhysicsInformer
>>>
>>> class Diffusion(PDE):
...     def __init__(self, D=0.1):
...         self.dim = 2
...         x, y = Symbol("x"), Symbol("y")
...         u = Function("u")(x, y)
...         self.equations = {
...             "diffusion": -D * (u.diff(x, 2) + u.diff(y, 2)),
...         }
...
>>> pde = Diffusion(D=0.01)
>>> pi = PhysicsInformer(
...     required_outputs=["diffusion"],
...     equations=pde,
...     grad_method="finite_difference",
...     fd_dx=0.01,
... )
>>> field = torch.rand(1, 1, 32, 32)
>>> result = pi.forward({"u": field})
>>> result["diffusion"].shape
torch.Size([1, 1, 32, 32])
property required_inputs: list[str]#

Return the list of tensor names the forward call expects.

Gradient Calculators#

GradientCalculator is the user-facing dispatcher that PhysicsInformer uses internally based on the grad_method argument. The individual per-method Gradients* modules are exposed for advanced users that need to compute spatial derivatives outside of the PhysicsInformer pipeline.

class physicsnemo.sym.eq.gradients.GradientCalculator(device=None)[source]#

Factory for spatial gradient modules.

Parameters:

device (str or torch.device or None) – Target device for the created gradient modules.

Examples

>>> import torch
>>> from physicsnemo.sym.eq.gradients import GradientCalculator
>>> calc = GradientCalculator(device="cpu")
>>> module = calc.get_gradient_module("autodiff", invar="u", dim=2, order=1)
compute_gradients(
input_dict,
method_name=None,
invar=None,
**kwargs,
)[source]#

Compute gradients in one shot (convenience wrapper).

get_gradient_module(
method_name: str,
invar: str,
**kwargs,
)[source]#

Return a gradient torch.nn.Module for the given method and variable.

class physicsnemo.sym.eq.gradients.GradientsAutoDiff(
invar: str,
dim: int = 3,
order: int = 1,
return_mixed_derivs: bool = False,
)[source]#

Bases: Module

Compute spatial derivatives via torch.autograd.grad.

Parameters:
  • invar (str) – Name of the variable to differentiate (e.g. "u").

  • dim (int) – Spatial dimensionality (1, 2, or 3).

  • order (int) – Derivative order (1 or 2).

  • return_mixed_derivs (bool) – If True and order=2, include cross-derivatives like u__x__y.

forward(
input_dict: Dict[str, Tensor],
) Dict[str, Tensor][source]#

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

class physicsnemo.sym.eq.gradients.GradientsFiniteDifference(
invar: str,
dx: float | List[float],
dim: int = 3,
order: int = 1,
return_mixed_derivs: bool = False,
)[source]#

Bases: Module

Compute spatial derivatives on uniform grids via UniformGridGradient.

Parameters:
  • invar (str) – Name of the variable to differentiate (e.g. "u").

  • dx (float or list[float]) – Uniform grid spacing per axis.

  • dim (int) – Spatial dimensionality (1, 2, or 3).

  • order (int) – Derivative order (1 or 2).

  • return_mixed_derivs (bool) – If True and order=2, include cross-derivatives like u__x__y.

forward(
input_dict: Dict[str, Tensor],
) Dict[str, Tensor][source]#

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

class physicsnemo.sym.eq.gradients.GradientsMeshlessFiniteDifference(
invar: str,
dx: float | List[float],
dim: int = 3,
order: int = 1,
return_mixed_derivs: bool = False,
)[source]#

Bases: Module

Compute spatial derivatives using meshless central differences.

Expects stencil values in the input dict keyed as u>>x::1, u>>x::-1, etc.

forward(
input_dict: Dict[str, Tensor],
) Dict[str, Tensor][source]#

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

class physicsnemo.sym.eq.gradients.GradientsSpectral(
invar: str,
ell: float | List[float],
dim: int = 3,
order: int = 1,
return_mixed_derivs: bool = False,
)[source]#

Bases: Module

Compute spatial derivatives via SpectralGridGradient.

forward(
input_dict: Dict[str, Tensor],
) Dict[str, Tensor][source]#

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

class physicsnemo.sym.eq.gradients.GradientsLeastSquares(
invar: str,
dim: int = 3,
order: int = 1,
return_mixed_derivs: bool = False,
)[source]#

Bases: Module

Compute spatial derivatives using least-squares gradient reconstruction.

Uses MeshLSQGradient for first-order gradients and composes calls for second-order (same approach as the original physicsnemo-sym implementation).

forward(
input_dict: Dict[str, Tensor],
) Dict[str, Tensor][source]#

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

physicsnemo.sym.eq.gradients.compute_connectivity_tensor(
nodes: Tensor,
edges: Tensor,
max_neighbors: int | None = None,
) tuple[Tensor, Tensor, Tensor][source]#

Build CSR adjacency from node/edge lists.

Uses vectorized PyTorch ops (argsort + bincount) via the physicsnemo.mesh.neighbors utilities.

Parameters:
  • nodes (torch.Tensor) – Node IDs with shape (N, 1).

  • edges (torch.Tensor) – Edge pairs with shape (M, 2).

  • max_neighbors (int or None) – Pad neighbor matrix to this width. If None, uses the maximum found.

Returns:

(offsets, indices, neighbor_matrix) — CSR representation plus a padded (N, max_neighbors) neighbor matrix for batched computation.

Return type:

tuple[torch.Tensor, torch.Tensor, torch.Tensor]