Skip to content

5. field

torchfsm.field.diffused_noise ¤

diffused_noise(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    diffusion_coef: float = 0.01,
    device: Optional[device] = None,
    dtype: Optional[dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[
                Union[float, Tuple[float, float]],
                Union[float, Tuple[float, float]],
            ],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]

Generate a diffused noise field. The noise is generated by integrating a Laplacian operator with a random initial condition. The diffusion coefficient controls the amount of diffusion applied to the noise.

Parameters:

Name Type Description Default
mesh Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]

The mesh to generate the noise on. If a sequence is provided, it should be in the form of [(x_min, x_max, n_points), ...].

required
diffusion_coef float

The diffusion coefficient. Default is 1.0.

0.01
device Optional[device]

The device to generate the noise on. Default is None.

None
dtype Optional[dtype]

The data type of the generated noise. Default is None.

None
batch_size int

The number of batches. Default is 1.

1
n_channel int

The number of channels. Default is 1.

1
normalize_mode Optional[Union[Literal['normal_distribution', '-1_1', '0_1'], Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]]]]

The normalization mode for the generated noise. See torchfsm.field.normalize for details. If None, no normalization is applied. Default is None.

None

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ...]: The generated diffused noise field.

Source code in torchfsm/field/_diffused_noise.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def diffused_noise(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    diffusion_coef: float = 0.01,
    device: Optional[torch.device] = None,
    dtype: Optional[torch.dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]:
    r"""
    Generate a diffused noise field.
        The noise is generated by integrating a Laplacian operator with a random initial condition.
        The diffusion coefficient controls the amount of diffusion applied to the noise.

    Args:
        mesh (Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]): The mesh to generate the noise on.
            If a sequence is provided, it should be in the form of [(x_min, x_max, n_points), ...].
        diffusion_coef (float): The diffusion coefficient. Default is 1.0.
        device (Optional[torch.device]): The device to generate the noise on. Default is None.
        dtype (Optional[torch.dtype]): The data type of the generated noise. Default is None.
        batch_size (int): The number of batches. Default is 1.
        n_channel (int): The number of channels. Default is 1.
        normalize_mode (Optional[Union[Literal["normal_distribution", "-1_1", "0_1"],Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],]]):
            The normalization mode for the generated noise. See `torchfsm.field.normalize` for details.
            If None, no normalization is applied. Default is None.

    Returns:
        SpatialTensor["B C H ...]: The generated diffused noise field.
    """
    if device is None and (isinstance(mesh, FourierMesh) or isinstance(mesh, MeshGrid)):
        device = mesh.device
    if dtype is None and (isinstance(mesh, FourierMesh) or isinstance(mesh, MeshGrid)):
        dtype = mesh.dtype
    u_0 = torch.randn(
        *mesh_shape(mesh, batch_size=batch_size, n_channel=n_channel),
        device=device,
        dtype=dtype
    )
    diffusion = diffusion_coef * Laplacian()
    u_0 = diffusion.integrate(u_0, dt=1, step=1, mesh=mesh)
    del diffusion
    clean_up_memory()
    if normalize_mode is not None:
        u_0 = normalize(u_0, normalize_mode=normalize_mode)
    return u_0

torchfsm.field.random_diffused_noise ¤

random_diffused_noise(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    min_diffusion_coef: float = 0.001,
    max_diffusion_coef: float = 0.01,
    device: Optional[device] = None,
    dtype: Optional[dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[
                Union[float, Tuple[float, float]],
                Union[float, Tuple[float, float]],
            ],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]
Source code in torchfsm/field/_diffused_noise.py
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def random_diffused_noise(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    min_diffusion_coef: float = 0.001,
    max_diffusion_coef: float = 0.01,
    device: Optional[torch.device] = None,
    dtype: Optional[torch.dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]:
    mesh, device, dtype = _get_mesh_device_and_dtype(mesh, device, dtype)
    coef_shape = (batch_size, n_channel) + (1,) * mesh.n_dim
    if max_diffusion_coef >= min_diffusion_coef * 100:  # log uniform sampling
        log_min = torch.log(
            torch.tensor(min_diffusion_coef, device=device, dtype=dtype)
        )
        log_max = torch.log(
            torch.tensor(max_diffusion_coef, device=device, dtype=dtype)
        )
        diffusion_coef = torch.exp(
            torch.rand(coef_shape, device=device, dtype=dtype) * (log_max - log_min)
            + log_min
        )
    else:
        diffusion_coef = (
            torch.rand(coef_shape, device=device, dtype=dtype)
            * (max_diffusion_coef - min_diffusion_coef)
            + min_diffusion_coef
        )
    return diffused_noise(
        mesh=mesh,
        diffusion_coef=diffusion_coef,
        batch_size=batch_size,
        n_channel=n_channel,
        normalize_mode=normalize_mode,
    )

torchfsm.field.kolm_force ¤

kolm_force(
    x: Tensor,
    drag_coef: float = -0.1,
    k: float = 4.0,
    length_scale: float = 1.0,
) -> Operator

Generate cosine force field for 2d kolmogorov flow in vorticity form. It is defined as \(a \omega - k cos (k l x)\)

Parameters:

Name Type Description Default
x Tensor

The input tensor.

required
drag_coef float

The drag coefficient \(a\). Default is -0.1.

-0.1
k float

The wave number. Default is 4.0.

4.0
length_scale float

The length scale \(l\). Default is 1.0.

1.0
Source code in torchfsm/field/_kolm_force.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def kolm_force(
    x: torch.Tensor,
    drag_coef: float = -0.1,
    k: float = 4.0,
    length_scale: float = 1.0,
) -> Operator:
    r"""
    Generate cosine force field for 2d kolmogorov flow in vorticity form.
        It is defined as $a \omega - k cos (k l x)$

    Args:
        x (torch.Tensor): The input tensor.
        drag_coef (float): The drag coefficient $a$. Default is -0.1.
        k (float): The wave number. Default is 4.0.
        length_scale (float): The length scale $l$. Default is 1.0.
    """


    return drag_coef * ImplicitSource() - ExplicitSource(
        k * torch.cos(k * length_scale * x)
    )

torchfsm.field.wave_1d ¤

wave_1d(
    x: SpatialTensor["B C H ..."],
    min_k: int = 1,
    max_k: int = 5,
    min_amplitude: float = 0.5,
    max_amplitude: float = 1.0,
    n_polynomial: int = 5,
    zero_mean: bool = False,
    mean_shift_coef=0.3,
    batched: bool = False,
) -> SpatialTensor["B C H ..."]

Generate a 1D wave field with multiple harmonics.

Parameters:

Name Type Description Default
x SpatialTensor['B C H ...']

The input tensor.

required
min_k int

The minimum wave number. Default is 1.

1
max_k int

The maximum wave number. Default is 5.

5
min_amplitude float

The minimum amplitude. Default is 0.5.

0.5
max_amplitude float

The maximum amplitude. Default is 1.0.

1.0
n_polynomial int

The number of polynomial terms. Default is 5.

5
zero_mean bool

If True, the mean of the wave will be zero. Default is False.

False
mean_shift_coef float

The coefficient for mean shift. Default is 0.3.

0.3
batched bool

If True, the input tensor is batched. Default is False.

False

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ..."]: The generated wave field.

Source code in torchfsm/field/_wave_1d.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def wave_1d(
    x: SpatialTensor["B C H ..."],
    min_k: int = 1,
    max_k: int = 5,
    min_amplitude: float = 0.5,
    max_amplitude: float = 1.0,
    n_polynomial: int = 5,
    zero_mean: bool = False,
    mean_shift_coef=0.3,
    batched: bool = False,
) -> SpatialTensor["B C H ..."]:
    r"""
    Generate a 1D wave field with multiple harmonics.

    Args:
        x (SpatialTensor["B C H ..."]): The input tensor.
        min_k (int): The minimum wave number. Default is 1.
        max_k (int): The maximum wave number. Default is 5.
        min_amplitude (float): The minimum amplitude. Default is 0.5.
        max_amplitude (float): The maximum amplitude. Default is 1.0.
        n_polynomial (int): The number of polynomial terms. Default is 5.
        zero_mean (bool): If True, the mean of the wave will be zero. Default is False.
        mean_shift_coef (float): The coefficient for mean shift. Default is 0.3.
        batched (bool): If True, the input tensor is batched. Default is False.

    Returns:
        SpatialTensor["B C H ..."]: The generated wave field.
    """
    x_new = x / x.max() * torch.pi * 2
    y = torch.zeros_like(x)
    if not batched:
        x_new = x_new.unsqueeze(0)
        y = y.unsqueeze(0)
    batch = x_new.shape[0]
    shape = [batch, n_polynomial] + [1] * (x_new.dim() - 2)
    k = torch.randint(min_k, max_k + 1, shape, device=x.device, dtype=x.dtype)
    amplitude = (
        torch.rand(*shape, device=x.device, dtype=x.dtype)
        * (max_amplitude - min_amplitude)
        + min_amplitude
    )
    shift = torch.rand(*shape, device=x.device, dtype=x.dtype) * torch.pi * 2
    for i in range(n_polynomial):
        y += amplitude[:, i : i + 1, ...] * torch.sin(
            k[:, i : i + 1, ...] * (x_new + shift[:, i : i + 1, ...])
        )
    if not zero_mean:
        value_shift = torch.rand(
            [batch] + [1] * (x_new.dim() - 1), device=x.device, dtype=x.dtype
        )
        value_shift = (value_shift - 0.5) * 2 * (
            max_amplitude - min_amplitude
        ) * mean_shift_coef + min_amplitude
        y += value_shift
    if not batched:
        y = y.squeeze(0)
    return y

torchfsm.field.random_gaussian_blobs ¤

random_gaussian_blobs(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    position_range: Tuple[float, float] = (0.4, 0.6),
    variance_range: Tuple[float, float] = (0.005, 0.01),
    batch_size: int = 1,
    n_channel: int = 1,
    device: Optional[device] = None,
) -> SpatialTensor["B C H ..."]

Generate random Gaussian blobs on the specified mesh.

Parameters:

Name Type Description Default
mesh Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]

The mesh on which to generate the Gaussian blob.

required
position_range Tuple[float, float]

The range of positions for the Gaussian blob. Default is (0.4, 0.6).

(0.4, 0.6)
variance_range Tuple[float, float]

The range of variances for the Gaussian blob. Default is (0.005, 0.01).

(0.005, 0.01)
batch_size int

The number of batches. Default is 1.

1
n_channel int

The number of channels. Default is 1.

1
device Optional[device]

The device on which to create the tensor. Default is None.

None

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ..."]: The generated Gaussian blob field.

Source code in torchfsm/field/_gaussian_blobs.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def random_gaussian_blobs(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    position_range: Tuple[float, float] = (0.4, 0.6),
    variance_range: Tuple[float, float] = (0.005, 0.01),
    batch_size: int = 1,
    n_channel: int = 1,
    device: Optional[torch.device] = None
) -> SpatialTensor["B C H ..."]:
    r"""
    Generate random Gaussian blobs on the specified mesh.

    Args:
        mesh (Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]):
            The mesh on which to generate the Gaussian blob.
        position_range (Tuple[float, float]): The range of positions for the Gaussian blob.
            Default is (0.4, 0.6).
        variance_range (Tuple[float, float]): The range of variances for the Gaussian blob.
            Default is (0.005, 0.01).
        batch_size (int): The number of batches. Default is 1.
        n_channel (int): The number of channels. Default is 1.
        device (Optional[torch.device]): The device on which to create the tensor. Default is None. 

    Returns:
        SpatialTensor["B C H ..."]: The generated Gaussian blob field.
    """
    if not isinstance(mesh, MeshGrid):
        mesh = MeshGrid(mesh.mesh_info if isinstance(mesh, FourierMesh) else mesh,device=device)
    if device is not None:
        if mesh.device != device:
            raise ValueError(f"Mesh device {mesh.device} does not match the specified device {device}.")
    mesh_grid= mesh.bc_mesh_grid()
    mesh_grid = [mesh_grid] if isinstance(mesh_grid, Tensor) else mesh_grid
    locations=torch.cat(mesh_grid,dim=1).squeeze(0)
    n_dim = locations.ndim-1 
    position = torch.empty(n_dim).uniform_(position_range[0],position_range[1]).to(mesh.device)
    variance = torch.empty(n_dim).uniform_(variance_range[0], variance_range[1]).to(mesh.device)
    locations = torch.stack([locations]*batch_size*n_channel, dim=0)
    position = position.unsqueeze(0).repeat(batch_size*n_channel, 1)
    variance = variance.unsqueeze(0).repeat(batch_size*n_channel, 1)
    blob=torch.exp(-0.5*_batched_mahalanobis_distance(locations, position, variance))
    return blob.view(batch_size, n_channel, *blob.shape[1:])

torchfsm.field.truncated_fourier_series ¤

truncated_fourier_series(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    freq_threshold: int = 5,
    device: Optional[device] = None,
    dtype: Optional[dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[
                Union[float, Tuple[float, float]],
                Union[float, Tuple[float, float]],
            ],
        ]
    ] = None,
    normalized_freq: bool = True,
    noise_type: Literal["normal", "uniform"] = "normal",
) -> SpatialTensor["B C H ..."]

Generate a truncated Fourier series noise field on a given mesh.

Parameters:

Name Type Description Default
mesh Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]

The mesh on which to generate the noise.

required
freq_threshold int

The frequency threshold for truncation.

5
device Optional[device]

The device on which to create the tensor.

None
dtype Optional[dtype]

The data type of the tensor.

None
batch_size int

The number of batches.

1
n_channel int

The number of channels.

1
normalize_mode Optional[Union[Literal['normal_distribution', '-1_1', '0_1'], Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]]]]

The normalization mode for the generated noise. See torchfsm.field.normalize for details. If None, no normalization is applied. Default is None.

None
normalized_freq bool

If True, wheather to set the frequency threshold as a normalized value. If the domain length is 1, setting this to True or False will not make a difference.

True
noise_type Literal['normal', 'uniform']

The type of noise to generate in the Fourier domain.

'normal'

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ..."]: The generated noise field.

Source code in torchfsm/field/_truncated_fourier.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def truncated_fourier_series(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    freq_threshold: int = 5,
    device: Optional[torch.device] = None,
    dtype: Optional[torch.dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],
        ]
    ] = None,
    normalized_freq: bool = True,
    noise_type: Literal["normal", "uniform"] = "normal",
) -> SpatialTensor["B C H ..."]:
    r"""
    Generate a truncated Fourier series noise field on a given mesh.

    Args:
        mesh (Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]): The mesh on which to generate the noise.
        freq_threshold (int): The frequency threshold for truncation.
        device (Optional[torch.device]): The device on which to create the tensor.
        dtype (Optional[torch.dtype]): The data type of the tensor.
        batch_size (int): The number of batches.
        n_channel (int): The number of channels.
        normalize_mode (Optional[Union[Literal["normal_distribution", "-1_1", "0_1"],Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],]]):
            The normalization mode for the generated noise. See `torchfsm.field.normalize` for details.
            If None, no normalization is applied. Default is None.
        normalized_freq (bool): If True, wheather to set the frequency threshold as a normalized value.
            If the domain length is 1, setting this to True or False will not make a difference.
        noise_type (Literal["normal","uniform"]): The type of noise to generate in the Fourier domain.

    Returns:
        SpatialTensor["B C H ..."]: The generated noise field.
    """
    mesh, device, dtype = _get_mesh_device_and_dtype(mesh, device, dtype)

    if normalized_freq:
        filter = mesh.normalized_low_pass_filter(freq_threshold)
        # mesh.normalized_low_pass_filter.cache_clear()
    else:
        filter = mesh.abs_low_pass_filter(freq_threshold)
        # mesh.abs_low_pass_filter.cache_clear()

    return truncated_fourier_series_custom_filter(
        mesh=mesh,
        low_pass_filter=filter,
        device=device,
        dtype=dtype,
        batch_size=batch_size,
        n_channel=n_channel,
        normalize_mode=normalize_mode,
        noise_type=noise_type,
    )

torchfsm.field.random_truncated_fourier_series ¤

random_truncated_fourier_series(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    min_freq: int = 2,
    max_freq: int = 5,
    device: Optional[device] = None,
    dtype: Optional[dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[
                Union[float, Tuple[float, float]],
                Union[float, Tuple[float, float]],
            ],
        ]
    ] = None,
    normalized_freq: bool = True,
    noise_type: Literal["normal", "uniform"] = "normal",
) -> SpatialTensor["B C H ..."]

Generate a truncated Fourier series noise field on a given mesh.

Parameters:

Name Type Description Default
mesh Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]

The mesh on which to generate the noise.

required
min_freq int

The minimum frequency threshold for truncation.

2
max_freq int

The maximum frequency threshold for truncation.

5
device Optional[device]

The device on which to create the tensor.

None
dtype Optional[dtype]

The data type of the tensor.

None
batch_size int

The number of batches.

1
n_channel int

The number of channels.

1
normalize_mode Optional[Union[Literal['normal_distribution', '-1_1', '0_1'], Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]]]]

The normalization mode for the generated noise. See torchfsm.field.normalize for details. If None, no normalization is applied. Default is None.

None
normalized_freq bool

If True, wheather to set the frequency threshold as a normalized value. If the domain length is 1, setting this to True or False will not make a difference.

True
noise_type Literal['normal', 'uniform']

The type of noise to generate in the Fourier domain.

'normal'

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ..."]: The generated noise field.

Source code in torchfsm/field/_truncated_fourier.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
def random_truncated_fourier_series(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    min_freq: int = 2,
    max_freq: int = 5,
    device: Optional[torch.device] = None,
    dtype: Optional[torch.dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],
        ]
    ] = None,
    normalized_freq: bool = True,
    noise_type: Literal["normal", "uniform"] = "normal",
) -> SpatialTensor["B C H ..."]:
    r"""
    Generate a truncated Fourier series noise field on a given mesh.

    Args:
        mesh (Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]): The mesh on which to generate the noise.
        min_freq (int): The minimum frequency threshold for truncation.
        max_freq (int): The maximum frequency threshold for truncation.
        device (Optional[torch.device]): The device on which to create the tensor.
        dtype (Optional[torch.dtype]): The data type of the tensor.
        batch_size (int): The number of batches.
        n_channel (int): The number of channels.
        normalize_mode (Optional[Union[Literal["normal_distribution", "-1_1", "0_1"],Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],]]):
            The normalization mode for the generated noise. See `torchfsm.field.normalize` for details.
            If None, no normalization is applied. Default is None.
        normalized_freq (bool): If True, wheather to set the frequency threshold as a normalized value.
            If the domain length is 1, setting this to True or False will not make a difference.
        noise_type (Literal["normal","uniform"]): The type of noise to generate in the Fourier domain.

    Returns:
        SpatialTensor["B C H ..."]: The generated noise field.
    """
    mesh, device, dtype = _get_mesh_device_and_dtype(mesh, device, dtype)

    if normalized_freq:
        filter = torch.cat(
            [
                mesh.normalized_low_pass_filter(random.randint(min_freq, max_freq + 1))
                for _ in range(batch_size)
            ],
            dim=0,
        )
        # mesh.normalized_low_pass_filter.cache_clear()
    else:
        filter = torch.cat(
            [
                mesh.abs_low_pass_filter(random.randint(min_freq, max_freq + 1))
                for _ in range(batch_size)
            ],
            dim=0,
        )
        # mesh.abs_low_pass_filter.cache_clear()

    return truncated_fourier_series_custom_filter(
        mesh=mesh,
        low_pass_filter=filter,
        device=device,
        dtype=dtype,
        batch_size=batch_size,
        n_channel=n_channel,
        normalize_mode=normalize_mode,
        noise_type=noise_type,
    )

torchfsm.field.truncated_fourier_series_custom_filter ¤

truncated_fourier_series_custom_filter(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    low_pass_filter: SpatialTensor["B C H ..."],
    device: Optional[device] = None,
    dtype: Optional[dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[
                Union[float, Tuple[float, float]],
                Union[float, Tuple[float, float]],
            ],
        ]
    ] = None,
    noise_type: Literal["normal", "uniform"] = "normal",
) -> SpatialTensor["B C H ..."]

Generate a truncated Fourier series noise field on a given mesh.

Parameters:

Name Type Description Default
mesh Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]

The mesh on which to generate the noise.

required
low_pass_filter SpatialTensor['B C H ...']

The custom low-pass filter to apply to the Fourier noise.

required
device Optional[device]

The device on which to create the tensor.

None
dtype Optional[dtype]

The data type of the tensor.

None
batch_size int

The number of batches.

1
n_channel int

The number of channels.

1
normalize_mode Optional[Union[Literal['normal_distribution', '-1_1', '0_1'], Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]]]]

The normalization mode for the generated noise. See torchfsm.field.normalize for details. If None, no normalization is applied. Default is None.

None
noise_type Literal['normal', 'uniform']

The type of noise to generate in the Fourier domain.

'normal'

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ..."]: The generated noise field.

Source code in torchfsm/field/_truncated_fourier.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def truncated_fourier_series_custom_filter(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    low_pass_filter: SpatialTensor["B C H ..."],
    device: Optional[torch.device] = None,
    dtype: Optional[torch.dtype] = None,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],
        ]
    ] = None,
    noise_type: Literal["normal", "uniform"] = "normal",
) -> SpatialTensor["B C H ..."]:
    r"""
    Generate a truncated Fourier series noise field on a given mesh.

    Args:
        mesh (Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]): The mesh on which to generate the noise.
        low_pass_filter (SpatialTensor["B C H ..."]): The custom low-pass filter to apply to the Fourier noise.
        device (Optional[torch.device]): The device on which to create the tensor.
        dtype (Optional[torch.dtype]): The data type of the tensor.
        batch_size (int): The number of batches.
        n_channel (int): The number of channels.
        normalize_mode (Optional[Union[Literal["normal_distribution", "-1_1", "0_1"],Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],]]):
            The normalization mode for the generated noise. See `torchfsm.field.normalize` for details.
            If None, no normalization is applied. Default is None.
        noise_type (Literal["normal","uniform"]): The type of noise to generate in the Fourier domain.

    Returns:
        SpatialTensor["B C H ..."]: The generated noise field.
    """
    mesh, device, dtype = _get_mesh_device_and_dtype(mesh, device, dtype)
    if noise_type == "uniform":
        fourier_noise = torch.rand(
            *mesh_shape(mesh, batch_size=batch_size, n_channel=n_channel),
            device=device,
            dtype=dtype,
        )
    elif noise_type == "normal":
        fourier_noise = torch.randn(
            *mesh_shape(mesh, batch_size=batch_size, n_channel=n_channel),
            device=device,
            dtype=dtype,
        )
    else:
        raise ValueError(
            f"Unsupported noise_type: {noise_type}. Supported types are 'normal' and 'uniform'."
        )
    fourier_noise = mesh.fft(fourier_noise)
    fourier_noise = fourier_noise * low_pass_filter
    fourier_noise = mesh.ifft(fourier_noise).real
    if normalize_mode is not None:
        fourier_noise = normalize(fourier_noise, normalize_mode=normalize_mode)
    return fourier_noise

torchfsm.field.functional_energy_spectrum ¤

functional_energy_spectrum(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    spectrum_func: Callable[[Tensor], Tensor],
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[
                Union[float, Tuple[float, float]],
                Union[float, Tuple[float, float]],
            ],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]

Generate an field \(\mathbf{u}\) based on a given energy spectrum function \(E(k)\) which statisfies$ rac{1}{2}\oiint_{A(K)}\hat{\mathbf{u}}(\mathbf{k})\hat{\mathbf{u}}^*(\mathbf{k})dA(k)=E(k)$ where \(\hat{\mathbf{u}}\) is the Fourier transform of \(\mathbf{u}\) and \(\mathbf{u}^*\) is the corresponding complex conjugate. How it works: If \(\hat{\mathbf{u}}\) is independent of the direction of \(\mathbf{k}\), i.e., \(\hat{\mathbf{u}}(\mathbf{k})=\hat{\mathbf{u}}(k)\), then the above equation can be simplified as$ rac{4\pi k^2}{2}|\hat{\mathbf{u}}(k)|^2=E(k)$. Therefore, we can derive that \(|\hat{\mathbf{u}}(k)|=\sqrt{ rac{E(k)}{2\pi k^2}}\). You can use torchfsm.utils.collect_energy_spectrum to verify the energy spectrum of the generated field.

Parameters:

Name Type Description Default
mesh Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]

The mesh or grid on which to generate the initial field.

required
spectrum_func Callable[[Tensor], Tensor]

A function that takes a tensor of wave numbers (SpatialTensor["B C H ..."]) and returns the corresponding energy spectrum values, e.g., lambda k: 0.327k*(-5/3).

required
batch_size int

The number of batches. Default is 1.

1
n_channel int

The number of channels. Note that if multiple channels are used, each channel will be treated as a component of the vector field ans the energy is equally distributed among all channels. Default is 1.

1
normalize_mode Optional[Union[Literal['normal_distribution', '-1_1', '0_1'], Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]]]]

The normalization mode for the generated noise. See torchfsm.field.normalize for details. Note that the normalization will change the energy spectrum of the final generated field. If None, no normalization is applied. Default is None.

None

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ..."]: The generated initial field with shape (batch_size, n_channel, H, W, D, ...). The device and dtype are inherited from the input mesh.

Source code in torchfsm/field/_energy_spectrum.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def functional_energy_spectrum(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    spectrum_func: Callable[[torch.Tensor], torch.Tensor],
    batch_size: int=1,
    n_channel: int=1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]:
    """
    Generate an field $\mathbf{u}$ based on a given energy spectrum function $E(k)$ which statisfies$\frac{1}{2}\oiint_{A(K)}\hat{\mathbf{u}}(\mathbf{k})\hat{\mathbf{u}}^*(\mathbf{k})dA(k)=E(k)$
    where $\hat{\mathbf{u}}$ is the Fourier transform of $\mathbf{u}$ and $\mathbf{u}^*$ is the corresponding complex conjugate.
    How it works:
    If $\hat{\mathbf{u}}$ is independent of the direction of $\mathbf{k}$, i.e., $\hat{\mathbf{u}}(\mathbf{k})=\hat{\mathbf{u}}(k)$, then the above equation can be simplified as$\frac{4\pi k^2}{2}|\hat{\mathbf{u}}(k)|^2=E(k)$.
    Therefore, we can derive that $|\hat{\mathbf{u}}(k)|=\sqrt{\frac{E(k)}{2\pi k^2}}$.
    You can use `torchfsm.utils.collect_energy_spectrum` to verify the energy spectrum of the generated field.

    Args:
        mesh (Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]): The mesh or grid on which to generate the initial field.
        spectrum_func (Callable[[torch.Tensor], torch.Tensor]): A function that takes a tensor of wave numbers (SpatialTensor["B C H ..."]) and returns the corresponding energy spectrum values, e.g., lambda k: 0.327*k**(-5/3).
        batch_size (int): The number of batches. Default is 1.
        n_channel (int): The number of channels. Note that if multiple channels are used, each channel will be treated as a component of the vector field ans the energy is equally distributed among all channels. Default is 1.
        normalize_mode (Optional[Union[Literal["normal_distribution", "-1_1", "0_1"],Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],]]): 
            The normalization mode for the generated noise. See `torchfsm.field.normalize` for details.
            Note that the normalization will change the energy spectrum of the final generated field.
            If None, no normalization is applied. Default is None.

    Returns:
        SpatialTensor["B C H ..."]: The generated initial field with shape (batch_size, n_channel, H, W, D, ...). The device and dtype are inherited from the input mesh.

    """
    f_mesh = FourierMesh(mesh) if not isinstance(mesh, FourierMesh) else mesh
    k_vec = f_mesh.bf_vector * (2 * torch.pi)
    norm_k = torch.norm(k_vec, dim=1, keepdim=True)
    norm_k = torch.repeat_interleave(norm_k, batch_size, dim=0)
    norm_k = torch.repeat_interleave(norm_k, n_channel, dim=1)
    spectral_magnitude = torch.nan_to_num(
        spectrum_func(norm_k) / n_channel / (2 * torch.pi * norm_k**2),
        nan=0.0,
        posinf=0.0,
        neginf=0.0,
    )
    spectral_magnitude = random_hermitian_field(torch.sqrt(spectral_magnitude))
    u_0 = f_mesh.ifft(spectral_magnitude).real.view(
        batch_size, n_channel, *k_vec.shape[2:]
    )
    if normalize_mode is not None:
        u_0 = normalize(u_0, normalize_mode=normalize_mode)
    return u_0

torchfsm.field.random_power_law_energy_spectrum ¤

random_power_law_energy_spectrum(
    mesh: Union[
        Sequence[tuple[float, float, int]],
        MeshGrid,
        FourierMesh,
    ],
    min_power: float = -5.0,
    max_power: float = -2.0,
    batch_size: int = 1,
    n_channel: int = 1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[
                Union[float, Tuple[float, float]],
                Union[float, Tuple[float, float]],
            ],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]

Generate a random field with a power-law energy spectrum on a given mesh, i.e., \(E(k)=k^p\)

Parameters:

Name Type Description Default
mesh Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]

The mesh or grid on which to generate the initial field.

required
min_power float

The minimum power-law exponent.

-5.0
max_power float

The maximum power-law exponent.

-2.0
batch_size int

The number of batches. Default is 1.

1
n_channel int

The number of channels. Default is 1.

1
normalize_mode Optional[Union[Literal['normal_distribution', '-1_1', '0_1'], Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]]]]

The normalization mode for the generated noise. See torchfsm.field.normalize for details. Note that the normalization will change the energy spectrum of the final generated field. If None, no normalization is applied. Default is None.

None

Returns:

Type Description
SpatialTensor['B C H ...']

SpatialTensor["B C H ..."]: The generated initial field with shape (batch_size, n_channel, H, W, D, ...). The device and dtype are inherited from the input mesh.

Source code in torchfsm/field/_energy_spectrum.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def random_power_law_energy_spectrum(
    mesh: Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh],
    min_power: float=-5.0,
    max_power: float=-2.0,
    batch_size: int=1,
    n_channel: int=1,
    normalize_mode: Optional[
        Union[
            Literal["normal_distribution", "-1_1", "0_1"],
            Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],
        ]
    ] = None,
) -> SpatialTensor["B C H ..."]:
    """
    Generate a random field with a power-law energy spectrum on a given mesh, i.e., $E(k)=k^p$

    Args:
        mesh (Union[Sequence[tuple[float, float, int]], MeshGrid, FourierMesh]): The mesh or grid on which to generate the initial field.
        min_power (float): The minimum power-law exponent.
        max_power (float): The maximum power-law exponent.
        batch_size (int): The number of batches. Default is 1.
        n_channel (int): The number of channels. Default is 1.
        normalize_mode (Optional[Union[Literal["normal_distribution", "-1_1", "0_1"],Tuple[Union[float, Tuple[float, float]], Union[float, Tuple[float, float]]],]]): 
            The normalization mode for the generated noise. See `torchfsm.field.normalize` for details.
            Note that the normalization will change the energy spectrum of the final generated field.
            If None, no normalization is applied. Default is None.

    Returns:
        SpatialTensor["B C H ..."]: The generated initial field with shape (batch_size, n_channel, H, W, D, ...). The device and dtype are inherited from the input mesh.

    """
    mesh=FourierMesh(mesh) if not isinstance(mesh,FourierMesh) else mesh
    powers=torch.rand(batch_size,1,*tuple([1,]*mesh.n_dim),device=mesh.device)*(max_power-min_power)+min_power
    return functional_energy_spectrum(
        mesh=mesh,
        spectrum_func=lambda k: torch.pow(k,powers),
        batch_size=batch_size,
        n_channel=n_channel,
        normalize_mode=normalize_mode,
    )

torchfsm.field.random_hermitian_field ¤

random_hermitian_field(
    magnitude: SpatialTensor["B C H ..."],
)

Generate a random Hermitian field in Fourier space with a given magnitude.

Parameters:

Name Type Description Default
magnitude SpatialTensor['B C H ...']

The desired magnitude of the Fourier field

required

Returns:

Type Description

SpatialTensor["B C H ..."]: A random Hermitian field in Fourier space with the specified magnitude.

Source code in torchfsm/field/_energy_spectrum.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def random_hermitian_field(magnitude: SpatialTensor["B C H ..."]):
    """
    Generate a random Hermitian field in Fourier space with a given magnitude.

    Args:
        magnitude (SpatialTensor["B C H ..."]): The desired magnitude of the Fourier field

    Returns:
        SpatialTensor["B C H ..."]: A random Hermitian field in Fourier space with the specified magnitude.
    """

    real_field = torch.randn_like(magnitude)
    dims = tuple(2 + i for i in range(magnitude.ndim - 2))
    fft_field = torch.fft.fftn(real_field, dim=dims)
    current_magnitude = torch.abs(fft_field)
    current_magnitude = torch.where(current_magnitude > 1e-12, current_magnitude, 1.0)
    return magnitude * (fft_field / current_magnitude)