Skip to content

Commit 0a87a1c

Browse files
author
Samuel Burbulla
committed
Wrap FNO from NVIDIA Modulus.
1 parent f07fe22 commit 0a87a1c

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
`continuiti.operators.modulus`
3+
4+
Operators from NVIDIA Modulus wrapped in continuiti.
5+
"""
6+
7+
# Test if we can import NVIDIA Modulus
8+
try:
9+
import modulus # noqa: F40
10+
except ImportError:
11+
raise ImportError("NVIDIA Modulus not found!")
12+
13+
from .fno import FNO
14+
15+
__all__ = [
16+
"FNO",
17+
]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""
2+
`continuiti.operators.modulus.fno`
3+
4+
The Fourier Neural Operator from NVIDIA Modulus wrapped in continuiti.
5+
"""
6+
7+
import torch
8+
from typing import Optional
9+
from continuiti.operators import Operator, OperatorShapes
10+
from modulus.models.fno import FNO as FNOModulus
11+
12+
13+
class FNO(Operator):
14+
r"""FNO architecture from NVIDIA Modulus.
15+
16+
The `in_channels` and `out_channels` arguments are determined by the
17+
`shapes` argument. The `dimension` is set to the dimension of the input
18+
coordinates, assuming that the grid dimension is the same as the coordinate
19+
dimension of `x`.
20+
21+
All other keyword arguments are passed to the Fourier Neural Operator, please refer
22+
to the documentation of the `modulus.model.fno.FNO` class for more information.
23+
24+
Args:
25+
shapes: Shapes of the input and output data.
26+
device: Device.
27+
**kwargs: Additional arguments for the Fourier layers.
28+
"""
29+
30+
def __init__(
31+
self,
32+
shapes: OperatorShapes,
33+
device: Optional[torch.device] = None,
34+
dimension: Optional[int] = None,
35+
**kwargs,
36+
):
37+
super().__init__(shapes, device)
38+
39+
if dimension is None:
40+
# Per default, use coordinate dimension
41+
dimension = shapes.x.dim
42+
43+
self.fno = FNOModulus(
44+
in_channels=shapes.u.dim,
45+
out_channels=shapes.v.dim,
46+
dimension=dimension,
47+
**kwargs,
48+
)
49+
self.fno.to(device)
50+
51+
def forward(
52+
self, x: torch.Tensor, u: torch.Tensor, y: torch.Tensor
53+
) -> torch.Tensor:
54+
r"""Forward pass of the Fourier Neural Operator.
55+
56+
Args:
57+
x: Ignored.
58+
u: Input function values of shape (batch_size, u_dim, num_sensors...).
59+
y: Ignored.
60+
"""
61+
return self.fno(u)

tests/operators/modulus/__init__.py

Whitespace-only changes.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import pytest
2+
from continuiti.benchmarks.sine import SineBenchmark
3+
from continuiti.trainer import Trainer
4+
from continuiti.operators.modulus import FNO
5+
from continuiti.operators.losses import MSELoss
6+
7+
8+
@pytest.mark.slow
9+
def test_modulus_fno():
10+
try:
11+
import modulus # noqa: F401
12+
except ImportError:
13+
pytest.skip("NVIDIA Modulus not found!")
14+
15+
# Data set
16+
benchmark = SineBenchmark(n_train=1)
17+
dataset = benchmark.train_dataset
18+
19+
# Operator
20+
# Configured like the default continuiti `FourierNeuralOperator`
21+
# with depth=3 and width=3 as in `test_fno.py`.
22+
operator = FNO(
23+
dataset.shapes,
24+
decoder_layers=1,
25+
decoder_layer_size=1,
26+
decoder_activation_fn="identity",
27+
num_fno_layers=3, # "depth" in FourierNeuralOperator
28+
latent_channels=3, # "width" in FourierNeuralOperator
29+
num_fno_modes=dataset.shapes.u.size[0] // 2 + 1,
30+
padding=0,
31+
coord_features=False,
32+
)
33+
34+
# Train
35+
Trainer(operator, device="cpu").fit(dataset, tol=1e-12, epochs=10_000)
36+
37+
# Check solution
38+
x, u, y, v = dataset.x, dataset.u, dataset.y, dataset.v
39+
assert MSELoss()(operator, x, u, y, v) < 1e-12
40+
41+
42+
# SineBenchmark(n_train=1024, n_sensors=128, n_evaluations=128), epochs=100
43+
44+
# NVIDIA Modulus FNO
45+
# Parameters: 3560 Device: cpu
46+
# Epoch 100/100 Step 32/32 [====================] 6ms/step [0:19min<0:00min] - loss/train = 6.3876e-05
47+
48+
# continuiti FNO
49+
# Parameters: 3556 Device: cpu
50+
# Epoch 100/100 Step 32/32 [====================] 3ms/step [0:10min<0:00min] - loss/train = 1.4440e-04
51+
52+
# -> continuiti FNO is 2x faster than NVIDIA Modulus FNO
53+
# -> NVIDIA Modulus FNO can not handle different number of sensors and evaluations

0 commit comments

Comments
 (0)