Skip to content

Commit a29fae3

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

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+
14+
class FNO(Operator):
15+
r"""FNO architecture from NVIDIA Modulus.
16+
17+
The `in_channels` and `out_channels` arguments are determined by the
18+
`shapes` argument. The `dimension` is set to the dimension of the input
19+
coordinates, assuming that the grid dimension is the same as the coordinate
20+
dimension of `x`.
21+
22+
All other keyword arguments are passed to the Fourier Neural Operator, please refer
23+
to the documentation of the `modulus.model.fno.FNO` class for more information.
24+
25+
Args:
26+
shapes: Shapes of the input and output data.
27+
device: Device.
28+
**kwargs: Additional arguments for the Fourier layers.
29+
"""
30+
31+
def __init__(
32+
self,
33+
shapes: OperatorShapes,
34+
device: Optional[torch.device] = None,
35+
dimension: Optional[int] = None,
36+
**kwargs,
37+
):
38+
super().__init__(shapes, device)
39+
40+
if dimension is None:
41+
# Per default, use coordinate dimension
42+
dimension = shapes.x.dim
43+
44+
self.fno = FNOModulus(
45+
in_channels = shapes.u.dim,
46+
out_channels = shapes.v.dim,
47+
dimension = dimension,
48+
**kwargs
49+
)
50+
self.fno.to(device)
51+
52+
53+
def forward(self, x: torch.Tensor, u: torch.Tensor, y: torch.Tensor) -> 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)