Skip to content

Commit 2f03693

Browse files
committed
Checkpointing
1 parent 08ad421 commit 2f03693

File tree

4 files changed

+146
-81
lines changed

4 files changed

+146
-81
lines changed

drdmannturb/fluctuation_generation/fluctuation_field_generator.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import torch
1313

1414
from ..common import CPU_Unpickler
15+
from ..parameters import LowFreqParameters
1516
from ..spectra_fitting import CalibrationProblem
1617
from .covariance_kernels import MannCovariance, VonKarmanCovariance
1718
from .gaussian_random_fields import VectorGaussianRandomField
@@ -79,6 +80,7 @@ def __init__(
7980
length_scale: Optional[float] = None,
8081
time_scale: Optional[float] = None,
8182
energy_spectrum_scale: Optional[float] = None,
83+
lowfreq_params: Optional[LowFreqParameters] = None,
8284
path_to_parameters: Optional[Union[str, PathLike]] = None,
8385
seed: int = None,
8486
blend_num=10,
@@ -89,7 +91,7 @@ def __init__(
8991
friction_velocity : float
9092
The reference wind friction velocity :math:`u_*`
9193
reference_height : float
92-
Reference height :math:`L`
94+
Reference height :math:`z_{\text{ref}}`
9395
grid_dimensions : np.ndarray
9496
Numpy array denoting the grid size; the real dimensions of the domain of interest.
9597
grid_levels : np.ndarray
@@ -162,17 +164,19 @@ def __init__(
162164

163165
# define margins and buffer
164166
time_buffer = 3 * Gamma * L
165-
spatial_margin = 1 * L
167+
spatial_margin = 1 * L # TODO: where is this used?
166168

167169
# Grid calculations
168170
def calc_grid_size(level):
169171
return 2**level + 1
170172

173+
# TODO: why is this needed?
171174
try:
172175
grid_levels = [level.GetInt() for level in grid_levels]
173176
except AttributeError:
174177
pass
175178

179+
# TODO: Same as above...
176180
grid_sizes = [calc_grid_size(level) for level in grid_levels]
177181
Nx, Ny, Nz = grid_sizes
178182
grid_spacing = [dim / size for dim, size in zip(grid_dimensions, grid_sizes)]
@@ -334,10 +338,17 @@ def _normalize_block(
334338
Raises
335339
------
336340
ValueError
341+
In the case that curr_block does not satisfy not np.any (ie, it is empty, or all zeros).
337342
"No fluctuation field has been generated, call the .generate() method first."
343+
344+
ValueError
345+
If windprofiletype is not one of "LOG" or "PL".
346+
347+
ValueError
348+
In the case that any of the parameters zref, uref, or z0 are not positive.
338349
"""
339350
if not np.any(curr_block):
340-
raise ValueError("No fluctuation field has been generated, call the .generate() method first.")
351+
raise ValueError("No fluctuation field has been generated. The .generate() method must be called first.")
341352

342353
if windprofiletype not in ["LOG", "PL"]:
343354
raise ValueError('windprofiletype must be either "LOG" or "PL"')

drdmannturb/fluctuation_generation/lowfreq_syed_mann.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
def _compute_kappa(k1: float, k2: float, psi: float) -> float:
12-
"""
12+
r"""
1313
Subroutine to compute the horizontal wavevector :math:`\kappa`, defined by
1414
1515
.. math::
@@ -36,7 +36,7 @@ def _compute_kappa(k1: float, k2: float, psi: float) -> float:
3636

3737

3838
def _compute_E(kappa: float, c: float, L2D: float, z_i: float) -> float:
39-
"""
39+
r"""
4040
Subroutine to compute the energy spectrum :math:`E(\kappa)` with the attenuation factor,
4141
defined by
4242
@@ -64,7 +64,7 @@ def _compute_E(kappa: float, c: float, L2D: float, z_i: float) -> float:
6464

6565

6666
def _estimate_c(sigma2: float, L2D: float, z_i: float) -> float:
67-
"""
67+
r"""
6868
Subroutine to estimate the scaling factor :math:`c` from the target variance :math:`\sigma^2`.
6969
7070
This is achieved by approximating the integral of :math:`E(\kappa)` from :math:`\kappa=0` to
@@ -105,8 +105,9 @@ def generate_2D_lowfreq(
105105
L2D: float,
106106
z_i: float,
107107
c: Optional[float] = None,
108+
seed: Optional[int] = None,
108109
) -> np.ndarray:
109-
"""
110+
r"""
110111
Generates the 2D low-frequency wind fluctuation component of the Syed-Mann (2024) 2D+3D model.
111112
112113
Parameters

drdmannturb/fluctuation_generation/sampling_methods.py

+37-64
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"""
44

55
import os
6-
from math import *
76

87
import numpy as np
98
import scipy.fftpack as fft
@@ -16,14 +15,16 @@
1615

1716

1817
class Sampling_method_base:
19-
"""Meta class for different sampling methods. Each of these requires a ``RandomField`` object, which is a subclass of :py:class:``GaussianRandomField``."""
18+
"""Meta class for different sampling methods. Each of these requires a ``RandomField`` object, which is
19+
a subclass of :py:class:``GaussianRandomField``."""
2020

2121
def __init__(self, RandomField):
2222
"""
2323
Parameters
2424
----------
2525
RandomField : GaussianRandomField
26-
The random field from which to sample from. This object also determines all of the physical quantities and domain partitioning.
26+
The random field from which to sample from. This object also determines all of the physical quantities
27+
and domain partitioning.
2728
"""
2829
self.L, self.Nd, self.ndim = (
2930
RandomField.L,
@@ -34,30 +35,25 @@ def __init__(self, RandomField):
3435

3536

3637
class Sampling_method_freq(Sampling_method_base):
37-
"""Sampling method specifically in the frequency domain. This metaclass involves a single precomputation of the covariance spectrum of the underlying ``GaussianRandomField``. Refer to specific subclasses for details on what each of these entails, but generally, the approximate square-root of each associated spectral tensor is computed and transformed into the frequency domain.
38+
"""Sampling method specifically in the frequency domain. This metaclass involves a single precomputation of the
39+
covariance spectrum of the underlying ``GaussianRandomField``. Refer to specific subclasses for details on what
40+
each of these entails, but generally, the approximate square-root of each associated spectral tensor is computed
41+
and transformed into the frequency domain.
3842
3943
The norm of the transform is defined as the square-root of the length-scale.
4044
"""
4145

4246
def __init__(self, RandomField):
4347
super().__init__(RandomField)
4448
L, Nd, d = self.L, self.Nd, self.ndim
45-
self.Frequencies = [
46-
(2 * pi / L[j]) * (Nd[j] * fft.fftfreq(Nd[j])) for j in range(d)
47-
]
49+
self.Frequencies = [(2 * np.pi / L[j]) * (Nd[j] * fft.fftfreq(Nd[j])) for j in range(d)]
4850
self.TransformNorm = np.sqrt(L.prod())
4951
self.Spectrum = RandomField.Covariance.precompute_Spectrum(self.Frequencies)
5052

5153

52-
#######################################################################################################
53-
# Fourier Transform (FFTW)
54-
#######################################################################################################
55-
### - Only stationary covariance
56-
### - Uses the Fastest Fourier Transform in the West
57-
58-
5954
class Sampling_FFTW(Sampling_method_freq):
60-
"""Sampling with FFTW. Two stencils for the forward and inverse FFTs are generated using the following FFTW flags: ``"FFTW_MEASURE", "FFTW_DESTROY_INPUT", "FFTW_UNALIGNED"``.
55+
"""Sampling with FFTW. Two stencils for the forward and inverse FFTs are generated using the following FFTW flags:
56+
``"FFTW_MEASURE", "FFTW_DESTROY_INPUT", "FFTW_UNALIGNED"``.
6157
6258
Due to properties of the FFT, only stationary covariances are admissible.
6359
"""
@@ -74,12 +70,8 @@ def __init__(self, RandomField):
7470
flags = ("FFTW_MEASURE", "FFTW_DESTROY_INPUT", "FFTW_UNALIGNED")
7571
self.fft_x = pyfftw.empty_aligned(shpR, dtype="float64")
7672
self.fft_y = pyfftw.empty_aligned(shpC, dtype="complex128")
77-
self.fft_plan = pyfftw.FFTW(
78-
self.fft_x, self.fft_y, axes=axes, direction="FFTW_FORWARD", flags=flags
79-
)
80-
self.ifft_plan = pyfftw.FFTW(
81-
self.fft_y, self.fft_x, axes=axes, direction="FFTW_BACKWARD", flags=flags
82-
)
73+
self.fft_plan = pyfftw.FFTW(self.fft_x, self.fft_y, axes=axes, direction="FFTW_FORWARD", flags=flags)
74+
self.ifft_plan = pyfftw.FFTW(self.fft_y, self.fft_x, axes=axes, direction="FFTW_BACKWARD", flags=flags)
8375
self.Spectrum_half = self.Spectrum[..., : shpC[-1]] * np.sqrt(self.Nd.prod())
8476

8577
def __call__(self, noise):
@@ -90,27 +82,25 @@ def __call__(self, noise):
9082
return self.fft_x[self.DomainSlice] / self.TransformNorm
9183

9284

93-
#######################################################################################################
94-
# Vector Field Fourier Transform (VF_FFTW)
95-
#######################################################################################################
96-
### - Random vector fields
97-
### - Only stationary covariance
98-
### - Uses the Fastest Fourier Transform in the West
85+
class Sampling_VF_FFTW(Sampling_method_freq):
86+
"""Random vector fields using
9987
88+
FFTW applied to a vector field. This should be used in conjunction with :py:class:`VectorGaussianRandomField`.
89+
This sampling method is also multi-threaded across 4 threads, or else the maximum allowed by the environment. As in
90+
:py:class:`Sampling_FFTW`, the following FFTW flags are used: ``"FFTW_MEASURE", "FFTW_DESTROY_INPUT",
91+
"FFTW_UNALIGNED"``.
10092
101-
class Sampling_VF_FFTW(Sampling_method_freq):
102-
"""FFTW applied to a vector field. This should be used in conjunction with :py:class:`VectorGaussianRandomField`. This sampling method is also multi-threaded across 4 threads, or else the maximum allowed by the environment. As in :py:class:`Sampling_FFTW`, the following FFTW flags are used: ``"FFTW_MEASURE", "FFTW_DESTROY_INPUT", "FFTW_UNALIGNED"``."""
93+
Due to properties of the FFT, only stationary covariances are admissible.
94+
"""
10395

10496
def __init__(self, RandomField):
10597
super().__init__(RandomField)
10698

10799
import pyfftw
108100

109-
n_cpu = 4
110-
try:
111-
n_cpu = int(os.environ["OMP_NUM_THREADS"])
112-
except:
113-
pass
101+
# WARN: User might have OMP_NUM_THREADS set to something invalid here
102+
n_cpu = int(os.environ.get("OMP_NUM_THREADS", 4))
103+
114104
shpR = RandomField.ext_grid_shape
115105
shpC = shpR.copy()
116106
shpC[-1] = int(shpC[-1] // 2) + 1
@@ -135,9 +125,7 @@ def __init__(self, RandomField):
135125
threads=n_cpu,
136126
)
137127
self.Spectrum_half = self.Spectrum[..., : shpC[-1]] * np.sqrt(self.Nd.prod())
138-
self.hat_noise = np.stack(
139-
[np.zeros(shpC, dtype="complex64") for _ in range(3)], axis=-1
140-
)
128+
self.hat_noise = np.stack([np.zeros(shpC, dtype="complex64") for _ in range(3)], axis=-1)
141129
self.shpC = shpC
142130

143131
def __call__(self, noise):
@@ -146,25 +134,18 @@ def __call__(self, noise):
146134
self.fft_x[:] = noise[..., i]
147135
self.fft_plan()
148136
self.hat_noise[..., i] = self.fft_y[:]
149-
self.hat_noise = np.einsum(
150-
"kl...,...l->...k", self.Spectrum_half, self.hat_noise
151-
)
137+
self.hat_noise = np.einsum("kl...,...l->...k", self.Spectrum_half, self.hat_noise)
152138
for i in range(noise.shape[-1]):
153139
self.fft_y[:] = self.hat_noise[..., i]
154140
self.ifft_plan()
155141
tmp[..., i] = self.fft_x[:]
156142
return tmp[self.DomainSlice] / self.TransformNorm
157143

158144

159-
#######################################################################################################
160-
# Fourier Transform
161-
#######################################################################################################
162-
### - Only stationary covariance
163-
### - Uses sscipy.fftpack (slower solution)
164-
165-
166145
class Sampling_FFT(Sampling_method_freq):
167-
"""Sampling using ``scipy.fftpack``, which is considerably slower than with FFTW but is a simpler interface."""
146+
"""Sampling using ``scipy.fftpack``, which is considerably slower than with FFTW but is a simpler interface.
147+
148+
Due to properties of the FFT, only stationary covariances are admissible."""
168149

169150
def __init__(self, RandomField):
170151
super().__init__(RandomField)
@@ -176,15 +157,11 @@ def __call__(self, noise):
176157
return y.real[self.DomainSlice] / self.TransformNorm
177158

178159

179-
#######################################################################################################
180-
# Sine Transform
181-
#######################################################################################################
182-
### - Only stationary covariance
183-
### - Uses sscipy.fftpack (non the fastest solution)
184-
185-
186160
class Sampling_DST(Sampling_method_freq):
187-
"""Sampling using the discrete sine transform from ``scipy.fftpack``, with all other operations being identical as other sampling methods."""
161+
"""Sampling using the discrete sine transform from ``scipy.fftpack``, with all other operations being identical as
162+
other sampling methods. Should only be used for stationary covariances
163+
164+
Due to properties of the FFT, only stationary covariances are admissible."""
188165

189166
def __init__(self, RandomField):
190167
super().__init__(RandomField)
@@ -196,15 +173,11 @@ def __call__(self, noise):
196173
return y[self.DomainSlice] / self.TransformNorm
197174

198175

199-
#######################################################################################################
200-
# Cosine Transform
201-
#######################################################################################################
202-
### - Only stationary covariance
203-
### - Uses sscipy.fftpack (non the fastest solution)
204-
205-
206176
class Sampling_DCT(Sampling_method_freq):
207-
"""Sampling using the discrete cosine transform from ``scipy.fftpack``, with all other operations being identical as other sampling methods."""
177+
"""Sampling using the discrete cosine transform from ``scipy.fftpack``, with all other operations being identical
178+
to other sampling methods.
179+
180+
Due to properties of the FFT, only stationary covariances are admissible."""
208181

209182
def __init__(self, RandomField):
210183
super().__init__(RandomField)

0 commit comments

Comments
 (0)