Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ physicsnemo/core/ @coreyjadams @CharlelieLrt @ktangsali
# Reusable neural network building blocks
physicsnemo/nn/ @loliverhennigh

# Symbolic PDE residual computation (PhysicsInformer)
physicsnemo/sym/ @ktangsali

# Functional operations (KNN, radius search, SDF)
physicsnemo/nn/functional/ @loliverhennigh
physicsnemo/nn/functional/knn/ @coreyjadams @peterdsharpe @loliverhennigh
Expand Down
14 changes: 13 additions & 1 deletion .importlinter
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ containers=
layers =
experimental
active_learning : diffusion
models : datapipes : metrics : domain_parallel : optim
models : datapipes : metrics : domain_parallel : optim : sym
mesh
nn
utils
Expand Down Expand Up @@ -104,6 +104,18 @@ layers =
noise_schedulers
utils

[importlinter:contract:physicsnemo-sym]
name = Control Internal Dependencies in PhysicsNeMo sym
type = layers
containers=
physicsnemo.sym
layers =
eq
graph
computation
utils
constants

[importlinter:contract:physicsnemo-external-imports]
name = Prevent Non-listed external imports in physicsnemo
type = forbidden_import
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`uniform_grid_gradient`, `rectilinear_grid_gradient`,
`spectral_grid_gradient`, `meshless_fd_derivatives`, `mesh_lsq_gradient`,
and `mesh_green_gauss_gradient`.
- Adds `physicsnemo.sym` module for symbolic PDE residual computation
(`PhysicsInformer`). Users define PDEs via SymPy and select a gradient method
(`autodiff`, `finite_difference`, `spectral`, `meshless_finite_difference`,
`least_squares`); spatial derivatives are computed automatically using the
`nn.functional.derivatives` functionals.

### Changed

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ RUN if [ "$TARGETPLATFORM" = "linux/amd64" ] && [ "$NATTEN_AMD64_WHEEL" != "unkn
RUN uv pip install --no-build-isolation "torch_sparse"

# All pyproject extras (no dev); installs physicsnemo non-editable
RUN cd /physicsnemo && uv pip install ".[cu13,utils-extras,mesh-extras,datapipes-extras,gnns]"
RUN cd /physicsnemo && uv pip install ".[cu13,utils-extras,mesh-extras,datapipes-extras,gnns,sym]"

# Cleanup builder stage
RUN rm -rf /physicsnemo/
Expand Down
17 changes: 17 additions & 0 deletions docs/api/physicsnemo.sym.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PhysicsNeMo Sym
===============

Symbolic PDE residual computation for physics-informed training.

.. autoclass:: physicsnemo.sym.eq.pde.PDE
:members:
:show-inheritance:

.. autoclass:: physicsnemo.sym.eq.phy_informer.PhysicsInformer
:members:
:show-inheritance:

.. autoclass:: physicsnemo.sym.eq.gradients.GradientCalculator
:members:

.. autofunction:: physicsnemo.sym.eq.gradients.compute_connectivity_tensor
50 changes: 50 additions & 0 deletions physicsnemo/sym/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# SPDX-FileCopyrightText: Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES.
# SPDX-FileCopyrightText: All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""PhysicsNeMo Sym: symbolic PDE residual computation.
Comment thread
ktangsali marked this conversation as resolved.
Example
-------
>>> from sympy import Symbol, Function
>>> from physicsnemo.sym.eq.pde import PDE
>>> from physicsnemo.sym.eq.phy_informer import PhysicsInformer
>>>
>>> class Poisson(PDE):
... def __init__(self):
... self.dim = 2
... x, y = Symbol("x"), Symbol("y")
... u = Function("u")(x, y)
... self.equations = {"poisson": u.diff(x, 2) + u.diff(y, 2)}
...
>>> pde = Poisson()
>>> pi = PhysicsInformer(["poisson"], pde, grad_method="autodiff")
>>> sorted(pi.required_inputs)
['coordinates', 'u']
"""

from physicsnemo.sym.eq.gradients import (
GradientCalculator,
compute_connectivity_tensor,
)
from physicsnemo.sym.eq.pde import PDE
from physicsnemo.sym.eq.phy_informer import PhysicsInformer

__all__ = [
"GradientCalculator",
"PDE",
"PhysicsInformer",
"compute_connectivity_tensor",
]
101 changes: 101 additions & 0 deletions physicsnemo/sym/computation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# SPDX-FileCopyrightText: Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES.
# SPDX-FileCopyrightText: All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Lightweight replacement for Sym's Node — wraps a callable with string-based I/O metadata."""

from __future__ import annotations

from physicsnemo.sym.constants import diff_str


class Computation:
Comment thread
ktangsali marked this conversation as resolved.
"""A named unit in the computational graph.

Parameters
----------
inputs : list[str]
Non-derivative input names (e.g. ``["u", "v", "p"]``).
outputs : list[str]
Output names produced by this computation.
evaluate : callable
A callable (typically ``torch.nn.Module``) mapping
``Dict[str, Tensor] → Dict[str, Tensor]``.
name : str
Human-readable label for debugging.
"""

def __init__(
self,
inputs: list[str],
outputs: list[str],
evaluate,
name: str = "Computation",
):
if isinstance(inputs, str):
inputs = [inputs]
all_inputs = [str(x) for x in inputs]
self._inputs = [x for x in all_inputs if diff_str not in x]
self._derivatives = [x for x in all_inputs if diff_str in x]
self._outputs = [str(x) for x in outputs]
self.evaluate = evaluate
self._name = name

@classmethod
def from_sympy(cls, eq, out_name, freeze_terms=None, detach_names=None):
"""Build a Computation from a SymPy expression."""
from physicsnemo.sym.utils.sympy.torch_printer import (
SympyToTorch,
_subs_derivatives,
)

if freeze_terms is None:
freeze_terms = []
if detach_names is None:
detach_names = []

sub_eq = _subs_derivatives(eq)
evaluate = SympyToTorch(sub_eq, out_name, freeze_terms, detach_names)
inputs = list(evaluate.keys)
outputs = [out_name]
return cls(inputs, outputs, evaluate, name="Sympy Computation: " + out_name)

@property
def name(self) -> str:
"""Human-readable label for this computation."""
return self._name

@property
def outputs(self) -> list[str]:
"""Output names produced by this computation."""
return self._outputs

@property
def inputs(self) -> list[str]:
"""Non-derivative input names."""
return self._inputs

@property
def derivatives(self) -> list[str]:
"""Derivative input names (contain ``__``)."""
return self._derivatives

def __str__(self) -> str:
return (
f"computation: {self.name}\n"
f" inputs: {self.inputs}\n"
f" derivatives: {self.derivatives}\n"
f" outputs: {self.outputs}"
)
29 changes: 29 additions & 0 deletions physicsnemo/sym/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES.
# SPDX-FileCopyrightText: All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np
import torch

diff_str: str = "__"
Comment thread
ktangsali marked this conversation as resolved.


def diff(y: str, x: str, degree: int = 1) -> str:
"""Build a derivative name string: ``diff('u', 'x')`` → ``'u__x'``."""
return diff_str.join([y] + degree * [x])


tf_dt = torch.float32
np_dt = np.float32
29 changes: 29 additions & 0 deletions physicsnemo/sym/eq/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES.
# SPDX-FileCopyrightText: All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from physicsnemo.sym.eq.gradients import (
GradientCalculator,
compute_connectivity_tensor,
)
from physicsnemo.sym.eq.pde import PDE
from physicsnemo.sym.eq.phy_informer import PhysicsInformer

__all__ = [
"GradientCalculator",
"PDE",
"PhysicsInformer",
"compute_connectivity_tensor",
]
Loading
Loading