Skip to content

Commit 4a098ad

Browse files
Fixed Rayleigh number definition in RBC (#522)
1 parent 1f04016 commit 4a098ad

File tree

4 files changed

+91
-28
lines changed

4 files changed

+91
-28
lines changed

pySDC/helpers/spectral_helper.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,7 @@ def __init__(self, comm=None, useGPU=False, debug=False):
882882
self.BCs = None
883883

884884
self.fft_cache = {}
885+
self.fft_dealias_shape_cache = {}
885886

886887
@property
887888
def u_init(self):
@@ -1470,7 +1471,9 @@ def _transform_dct(self, u, axes, padding=None, **kwargs):
14701471

14711472
if padding is not None:
14721473
shape = list(v.shape)
1473-
if self.comm:
1474+
if ('forward', *padding) in self.fft_dealias_shape_cache.keys():
1475+
shape[0] = self.fft_dealias_shape_cache[('forward', *padding)]
1476+
elif self.comm:
14741477
send_buf = np.array(v.shape[0])
14751478
recv_buf = np.array(v.shape[0])
14761479
self.comm.Allreduce(send_buf, recv_buf)
@@ -1645,7 +1648,9 @@ def _transform_idct(self, u, axes, padding=None, **kwargs):
16451648
if padding is not None:
16461649
if padding[axis] != 1:
16471650
shape = list(v.shape)
1648-
if self.comm:
1651+
if ('backward', *padding) in self.fft_dealias_shape_cache.keys():
1652+
shape[0] = self.fft_dealias_shape_cache[('backward', *padding)]
1653+
elif self.comm:
16491654
send_buf = np.array(v.shape[0])
16501655
recv_buf = np.array(v.shape[0])
16511656
self.comm.Allreduce(send_buf, recv_buf)
@@ -1754,8 +1759,6 @@ def get_aligned(self, u, axis_in, axis_out, fft=None, forward=False, **kwargs):
17541759
if self.comm.size == 1:
17551760
return u.copy()
17561761

1757-
fft = self.get_fft(**kwargs) if fft is None else fft
1758-
17591762
global_fft = self.get_fft(**kwargs)
17601763
axisA = [me.axisA for me in global_fft.transfer]
17611764
axisB = [me.axisB for me in global_fft.transfer]
@@ -1793,6 +1796,8 @@ def get_aligned(self, u, axis_in, axis_out, fft=None, forward=False, **kwargs):
17931796
else: # go the potentially slower route of not reusing transfer classes
17941797
from mpi4py_fft import newDistArray
17951798

1799+
fft = self.get_fft(**kwargs) if fft is None else fft
1800+
17961801
_in = newDistArray(fft, forward).redistribute(axis_in)
17971802
_in[...] = u
17981803

pySDC/implementations/problem_classes/RayleighBenard.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pySDC.core.convergence_controller import ConvergenceController
77
from pySDC.core.hooks import Hooks
88
from pySDC.implementations.convergence_controller_classes.check_convergence import CheckConvergence
9+
from pySDC.core.problem import WorkCounter
910

1011

1112
class RayleighBenard(GenericSpectralLinear):
@@ -20,7 +21,7 @@ class RayleighBenard(GenericSpectralLinear):
2021
v_t - nu (v_xx + v_zz) + p_z - T = -uv_x - vv_z
2122
2223
with u the horizontal velocity, v the vertical velocity (in z-direction), T the temperature, p the pressure, indices
23-
denoting derivatives, kappa=(Rayleigh * Prandl)**(-1/2) and nu = (Rayleigh / Prandl)**(-1/2). Everything on the left
24+
denoting derivatives, kappa=(Rayleigh * Prandtl)**(-1/2) and nu = (Rayleigh / Prandtl)**(-1/2). Everything on the left
2425
hand side, that is the viscous part, the pressure gradient and the buoyancy due to temperature are treated
2526
implicitly, while the non-linear convection part on the right hand side is integrated explicitly.
2627
@@ -36,7 +37,7 @@ class RayleighBenard(GenericSpectralLinear):
3637
facilitate the Dirichlet BCs.
3738
3839
Parameters:
39-
Prandl (float): Prandl number
40+
Prandtl (float): Prandtl number
4041
Rayleigh (float): Rayleigh number
4142
nx (int): Horizontal resolution
4243
nz (int): Vertical resolution
@@ -50,26 +51,28 @@ class RayleighBenard(GenericSpectralLinear):
5051

5152
def __init__(
5253
self,
53-
Prandl=1,
54+
Prandtl=1,
5455
Rayleigh=2e6,
5556
nx=256,
5657
nz=64,
5758
BCs=None,
5859
dealiasing=3 / 2,
5960
comm=None,
61+
Lx=8,
6062
**kwargs,
6163
):
6264
"""
6365
Constructor. `kwargs` are forwarded to parent class constructor.
6466
6567
Args:
66-
Prandl (float): Prandtl number
68+
Prandtl (float): Prandtl number
6769
Rayleigh (float): Rayleigh number
6870
nx (int): Resolution in x-direction
6971
nz (int): Resolution in z direction
7072
BCs (dict): Vertical boundary conditions
7173
dealiasing (float): Dealiasing for evaluating the non-linear part in real space
7274
comm (mpi4py.Intracomm): Space communicator
75+
Lx (float): Horizontal length of the domain
7376
"""
7477
BCs = {} if BCs is None else BCs
7578
BCs = {
@@ -90,18 +93,19 @@ def __init__(
9093
except ModuleNotFoundError:
9194
pass
9295
self._makeAttributeAndRegister(
93-
'Prandl',
96+
'Prandtl',
9497
'Rayleigh',
9598
'nx',
9699
'nz',
97100
'BCs',
98101
'dealiasing',
99102
'comm',
103+
'Lx',
100104
localVars=locals(),
101105
readOnly=True,
102106
)
103107

104-
bases = [{'base': 'fft', 'N': nx, 'x0': 0, 'x1': 8}, {'base': 'ultraspherical', 'N': nz}]
108+
bases = [{'base': 'fft', 'N': nx, 'x0': 0, 'x1': self.Lx}, {'base': 'ultraspherical', 'N': nz}]
105109
components = ['u', 'v', 'T', 'p']
106110
super().__init__(bases, components, comm=comm, **kwargs)
107111

@@ -127,15 +131,17 @@ def __init__(
127131
self.Dz = S1 @ Dz
128132
self.Dzz = S2 @ Dzz
129133

130-
kappa = (Rayleigh * Prandl) ** (-1 / 2.0)
131-
nu = (Rayleigh / Prandl) ** (-1 / 2.0)
134+
# compute rescaled Rayleigh number to extract viscosity and thermal diffusivity
135+
Ra = Rayleigh / (max([abs(BCs['T_top'] - BCs['T_bottom']), np.finfo(float).eps]) * self.axes[1].L ** 3)
136+
self.kappa = (Ra * Prandtl) ** (-1 / 2.0)
137+
self.nu = (Ra / Prandtl) ** (-1 / 2.0)
132138

133139
# construct operators
134140
L_lhs = {
135141
'p': {'u': U01 @ Dx, 'v': Dz}, # divergence free constraint
136-
'u': {'p': U02 @ Dx, 'u': -nu * (U02 @ Dxx + Dzz)},
137-
'v': {'p': U12 @ Dz, 'v': -nu * (U02 @ Dxx + Dzz), 'T': -U02 @ Id},
138-
'T': {'T': -kappa * (U02 @ Dxx + Dzz)},
142+
'u': {'p': U02 @ Dx, 'u': -self.nu * (U02 @ Dxx + Dzz)},
143+
'v': {'p': U12 @ Dz, 'v': -self.nu * (U02 @ Dxx + Dzz), 'T': -U02 @ Id},
144+
'T': {'T': -self.kappa * (U02 @ Dxx + Dzz)},
139145
}
140146
self.setup_L(L_lhs)
141147

@@ -175,6 +181,8 @@ def __init__(
175181
)
176182
self.setup_BCs()
177183

184+
self.work_counters['rhs'] = WorkCounter()
185+
178186
def eval_f(self, u, *args, **kwargs):
179187
f = self.f_init
180188

@@ -225,6 +233,7 @@ def eval_f(self, u, *args, **kwargs):
225233
else:
226234
f.expl[:] = self.itransform(self.transform(fexpl_pad, padding=padding)).real
227235

236+
self.work_counters['rhs']()
228237
return f
229238

230239
def u_exact(self, t=0, noise_level=1e-3, seed=99):

pySDC/implementations/problem_classes/generic_spectral.py

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,26 @@ class attributes of the spectral helper. This class will automatically switch th
2828
Pr (sparse matrix): Right preconditioner
2929
"""
3030

31-
@classmethod
32-
def setup_GPU(cls):
31+
def setup_GPU(self):
3332
"""switch to GPU modules"""
3433
import cupy as cp
3534
from pySDC.implementations.datatype_classes.cupy_mesh import cupy_mesh, imex_cupy_mesh
3635
from pySDC.implementations.datatype_classes.mesh import mesh, imex_mesh
3736

38-
cls.dtype_u = cupy_mesh
37+
self.dtype_u = cupy_mesh
3938

4039
GPU_versions = {
4140
mesh: cupy_mesh,
4241
imex_mesh: imex_cupy_mesh,
4342
}
4443

45-
cls.dtype_f = GPU_versions[cls.dtype_f]
44+
self.dtype_f = GPU_versions[self.dtype_f]
45+
46+
if self.comm is not None:
47+
from pySDC.helpers.NCCL_communicator import NCCLComm
48+
49+
if not isinstance(self.comm, NCCLComm):
50+
self.__dict__['comm'] = NCCLComm(self.comm)
4651

4752
def __init__(
4853
self,
@@ -94,6 +99,9 @@ def __init__(
9499

95100
if useGPU:
96101
self.setup_GPU()
102+
if self.solver_args is not None:
103+
if 'rtol' in self.solver_args.keys():
104+
self.solver_args['tol'] = self.solver_args.pop('rtol')
97105

98106
for base in bases:
99107
self.spectral.add_axis(**base)
@@ -218,14 +226,21 @@ def solve_system(self, rhs, dt, u0=None, *args, skip_itransform=False, **kwargs)
218226

219227
if self.spectral_space:
220228
rhs_hat = rhs.copy()
229+
if u0 is not None:
230+
u0_hat = self.Pr.T @ u0.copy().flatten()
221231
else:
222232
rhs_hat = self.spectral.transform(rhs)
233+
if u0 is not None:
234+
u0_hat = self.Pr.T @ self.spectral.transform(u0).flatten()
235+
236+
if self.useGPU:
237+
self.xp.cuda.Device().synchronize()
223238

224239
rhs_hat = (self.M @ rhs_hat.flatten()).reshape(rhs_hat.shape)
225240
rhs_hat = self.spectral.put_BCs_in_rhs_hat(rhs_hat)
226241
rhs_hat = self.Pl @ rhs_hat.flatten()
227242

228-
if dt not in self.cached_factorizations.keys():
243+
if dt not in self.cached_factorizations.keys() or not self.solver_type.lower() == 'cached_direct':
229244
A = self.M + dt * self.L
230245
A = self.Pl @ self.spectral.put_BCs_in_matrix(A) @ self.Pr
231246

@@ -255,25 +270,58 @@ def solve_system(self, rhs, dt, u0=None, *args, skip_itransform=False, **kwargs)
255270

256271
elif self.solver_type.lower() == 'direct':
257272
_sol_hat = sp.linalg.spsolve(A, rhs_hat)
273+
elif self.solver_type.lower() == 'lsqr':
274+
lsqr = sp.linalg.lsqr(
275+
A,
276+
rhs_hat,
277+
x0=u0_hat,
278+
**self.solver_args,
279+
)
280+
_sol_hat = lsqr[0]
258281
elif self.solver_type.lower() == 'gmres':
259282
_sol_hat, _ = sp.linalg.gmres(
260283
A,
261284
rhs_hat,
262-
x0=u0.flatten(),
285+
x0=u0_hat,
263286
**self.solver_args,
264287
callback=self.work_counters[self.solver_type],
265-
callback_type='legacy',
288+
callback_type='pr_norm',
289+
)
290+
elif self.solver_type.lower() == 'gmres+ilu':
291+
linalg = self.spectral.linalg
292+
293+
if dt not in self.cached_factorizations.keys():
294+
if len(self.cached_factorizations) >= self.max_cached_factorizations:
295+
to_evict = list(self.cached_factorizations.keys())[0]
296+
self.cached_factorizations.pop(to_evict)
297+
self.logger.debug(f'Evicted matrix factorization for {to_evict=:.6f} from cache')
298+
iLU = linalg.spilu(A, drop_tol=dt * 1e-4, fill_factor=100)
299+
self.cached_factorizations[dt] = linalg.LinearOperator(A.shape, iLU.solve)
300+
self.logger.debug(f'Cached matrix factorization for {dt=:.6f}')
301+
self.work_counters['factorizations']()
302+
303+
_sol_hat, _ = linalg.gmres(
304+
A,
305+
rhs_hat,
306+
x0=u0_hat,
307+
**self.solver_args,
308+
callback=self.work_counters[self.solver_type],
309+
callback_type='pr_norm',
310+
M=self.cached_factorizations[dt],
266311
)
267312
elif self.solver_type.lower() == 'cg':
268313
_sol_hat, _ = sp.linalg.cg(
269-
A, rhs_hat, x0=u0.flatten(), **self.solver_args, callback=self.work_counters[self.solver_type]
314+
A, rhs_hat, x0=u0_hat, **self.solver_args, callback=self.work_counters[self.solver_type]
270315
)
271316
else:
272-
raise NotImplementedError(f'Solver {self.solver_type:!} not implemented in {type(self).__name__}!')
317+
raise NotImplementedError(f'Solver {self.solver_type=} not implemented in {type(self).__name__}!')
273318

274319
sol_hat = self.spectral.u_init_forward
275320
sol_hat[...] = (self.Pr @ _sol_hat).reshape(sol_hat.shape)
276321

322+
if self.useGPU:
323+
self.xp.cuda.Device().synchronize()
324+
277325
if self.spectral_space:
278326
return sol_hat
279327
else:
@@ -319,7 +367,6 @@ def compute_residual_DAE(self, stage=''):
319367
res[m] += L.tau[m]
320368
# use abs function from data type here
321369
res_norm.append(abs(res[m]))
322-
# print(m, [abs(me) for me in res[m]], [abs(me) for me in L.u[0] - L.u[m + 1]])
323370

324371
# find maximal residual over the nodes
325372
if L.params.residual_type == 'full_abs':

pySDC/tests/test_problems/test_RayleighBenard.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ def test_eval_f(nx, nz, direction, spectral_space):
1515
X, Z = P.X, P.Z
1616
cos, sin = np.cos, np.sin
1717

18-
kappa = (P.Rayleigh * P.Prandl) ** (-1 / 2)
19-
nu = (P.Rayleigh / P.Prandl) ** (-1 / 2)
18+
kappa = P.kappa
19+
nu = P.nu
2020

2121
if direction == 'x':
2222
y = sin(X * np.pi)
@@ -181,7 +181,9 @@ def test_Poisson_problems(nx, component):
181181
'T_top': 0,
182182
'T_bottom': 0,
183183
}
184-
P = RayleighBenard(nx=nx, nz=6, BCs=BCs, Rayleigh=1.0)
184+
P = RayleighBenard(
185+
nx=nx, nz=6, BCs=BCs, Rayleigh=(max([abs(BCs['T_top'] - BCs['T_bottom']), np.finfo(float).eps]) * 2**3)
186+
)
185187
rhs = P.u_init
186188

187189
idx = P.index(f'{component}')

0 commit comments

Comments
 (0)