Skip to content

Commit 01ca8a5

Browse files
Added scripts for generating plots for chapters 7 and 8 in the thesis (#526)
1 parent c751afd commit 01ca8a5

File tree

13 files changed

+2095
-181
lines changed

13 files changed

+2095
-181
lines changed

pySDC/implementations/problem_classes/GrayScott_MPIFFT.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class grayscott_imex_diffusion(IMEX_Laplacian_MPIFFT):
1919
\frac{\partial u}{\partial t} = D_u \Delta u - u v^2 + A (1 - u),
2020
2121
.. math::
22-
\frac{\partial v}{\partial t} = D_v \Delta v + u v^2 - B u
22+
\frac{\partial v}{\partial t} = D_v \Delta v + u v^2 - B v
2323
2424
in :math:`x \in \Omega:=[-L/2, L/2]^N` with :math:`N=2,3`. Spatial discretization is done by using
2525
Fast Fourier transformation for solving the linear parts provided by ``mpi4py-fft`` [2]_, see also
@@ -222,7 +222,7 @@ def u_exact(self, t, seed=10700000):
222222

223223
for _ in range(-self.num_blobs):
224224
x0 = rng.random(size=self.ndim) * self.L[0] - self.L[0] / 2
225-
l = rng.random(size=self.ndim) * self.L[0] / self.nvars[0] * 30
225+
l = rng.random(size=self.ndim) * self.L[0] / self.nvars[0] * 80
226226

227227
masks = [xp.logical_and(self.X[i] > x0[i], self.X[i] < x0[i] + l[i]) for i in range(self.ndim)]
228228
mask = masks[0]
@@ -236,33 +236,54 @@ def u_exact(self, t, seed=10700000):
236236
"""
237237
Blobs as in https://www.chebfun.org/examples/pde/GrayScott.html
238238
"""
239-
assert self.ndim == 2, 'The initial conditions are 2D for now..'
240-
241239
inc = self.L[0] / (self.num_blobs + 1)
242240

243241
for i in range(1, self.num_blobs + 1):
244242
for j in range(1, self.num_blobs + 1):
245-
signs = (-1) ** rng.integers(low=0, high=2, size=2)
246-
247-
# This assumes that the box is [-L/2, L/2]^2
248-
_u[...] += -xp.exp(
249-
-80.0
250-
* (
251-
(self.X[0] + self.x0 + inc * i + signs[0] * 0.05) ** 2
252-
+ (self.X[1] + self.x0 + inc * j + signs[1] * 0.02) ** 2
243+
signs = (-1) ** rng.integers(low=0, high=2, size=self.ndim)
244+
245+
if self.ndim == 2:
246+
# This assumes that the box is [-L/2, L/2]^2
247+
_u[...] += -xp.exp(
248+
-80.0
249+
* (
250+
(self.X[0] + self.x0 + inc * i + signs[0] * 0.05) ** 2
251+
+ (self.X[1] + self.x0 + inc * j + signs[1] * 0.02) ** 2
252+
)
253+
)
254+
_v[...] += xp.exp(
255+
-80.0
256+
* (
257+
(self.X[0] + self.x0 + inc * i - signs[0] * 0.05) ** 2
258+
+ (self.X[1] + self.x0 + inc * j - signs[1] * 0.02) ** 2
259+
)
260+
)
261+
elif self.ndim == 3:
262+
z_pos = self.x0 + rng.random() * self.L[2]
263+
# This assumes that the box is [-L/2, L/2]^3
264+
_u[...] += -xp.exp(
265+
-80.0
266+
* (
267+
(self.X[0] + self.x0 + inc * i + signs[0] * 0.05) ** 2
268+
+ (self.X[1] + self.x0 + inc * j + signs[1] * 0.02) ** 2
269+
+ (self.X[2] - z_pos + signs[2] * 0.035) ** 2
270+
)
253271
)
254-
)
255-
_v[...] += xp.exp(
256-
-80.0
257-
* (
258-
(self.X[0] + self.x0 + inc * i - signs[0] * 0.05) ** 2
259-
+ (self.X[1] + self.x0 + inc * j - signs[1] * 0.02) ** 2
272+
_v[...] += xp.exp(
273+
-80.0
274+
* (
275+
(self.X[0] + self.x0 + inc * i - signs[0] * 0.05) ** 2
276+
+ (self.X[1] + self.x0 + inc * j - signs[1] * 0.02) ** 2
277+
+ (self.X[2] - z_pos - signs[2] * 0.035) ** 2
278+
)
260279
)
261-
)
280+
else:
281+
raise NotImplementedError
262282

263283
_u += 1
264284
else:
265-
raise NotImplementedError
285+
_u[...] = rng.random(_u.shape)
286+
_v[...] = rng.random(_v.shape)
266287

267288
u = self.u_init
268289
if self.spectral:

pySDC/implementations/problem_classes/generic_MPIFFT_Laplacian.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def __init__(
8585
collapse=True,
8686
backend=self.fft_backend,
8787
comm_backend=self.fft_comm_backend,
88+
grid=(-1,),
8889
)
8990

9091
# get test data to figure out type and dimensions

pySDC/projects/GPU/README.rst

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,84 @@ For instance, use
4545

4646
.. code-block:: bash
4747
48-
srun -n 4 python work_precision.py --config=GS_USkate --procs=1/1/4 --useGPU=True --mode=run
49-
mpirun -np 8 python work_precision.py --config=GS_USkate --procs=1/1/4 --useGPU=True --mode=plot
50-
python work_precision.py --config=GS_USkate --procs=1/1/4 --useGPU=True --mode=video
48+
srun -n 4 python run_experiment.pyy --config=GS_USkate --procs=1/1/4 --useGPU=True --mode=run
49+
mpirun -np 8 python run_experiment.py --config=GS_USkate --procs=1/1/4 --useGPU=True --mode=plot
50+
python run_experiment.py --config=GS_USkate --procs=1/1/4 --useGPU=True --mode=video
5151
5252
to first run the problem, then make plots and then make a video for Gray-Scott with the U-Skate configuration (see arXiv:1501.01990).
5353

5454
To do a parallel scaling test, you can go to JUWELS Booster and use, for instance,
5555

5656
.. code-block:: bash
57-
python analysis_scripts/parallel_scaling.py --mode=run --scaling=strong --space_time=True --XPU=GPU --problem=GS
58-
srun python analysis_scripts/parallel_scaling.py --mode=plot --scaling=strong --space_time=True --XPU=GPU --problem=GS
57+
58+
python analysis_scripts/parallel_scaling.py --mode=run --space_time=True --XPU=GPU --problem=GS3D
59+
python analysis_scripts/parallel_scaling.py --mode=plot --space_time=True --XPU=GPU --problem=GS3D
5960
6061
This will generate jobscripts and submit the jobs. Notice that you have to wait for the jobs to complete before you can plot them.
6162

6263
To learn more about the options for the scripts, run them with `--help`.
64+
65+
Reproducing plots in Thomas Baumann's thesis
66+
--------------------------------------------
67+
Keep in mind that the results of the experiments are specific to the hardware that was used in the experiments.
68+
To record the data for space-time parallel scaling experiments with Gray-Scott and RBC, run the following commands on the specified machines within the directory that contains this README.
69+
70+
.. code-block:: bash
71+
72+
# run on JUWELS
73+
python analysis_scripts/parallel_scaling.py --mode=run --problem=GS3D --XPU=CPU --space_time=False
74+
python analysis_scripts/parallel_scaling.py --mode=run --problem=GS3D --XPU=CPU --space_time=True
75+
76+
# run on JUWELS booster
77+
python analysis_scripts/parallel_scaling.py --mode=run --problem=GS3D --XPU=GPU --space_time=False
78+
python analysis_scripts/parallel_scaling.py --mode=run --problem=GS3D --XPU=GPU --space_time=True
79+
80+
# run on JURECA DC
81+
python analysis_scripts/parallel_scaling.py --mode=run --problem=RBC --XPU=CPU --space_time=False
82+
python analysis_scripts/parallel_scaling.py --mode=run --problem=RBC --XPU=CPU --space_time=True
83+
84+
# run on JUWELS booster
85+
python analysis_scripts/parallel_scaling.py --mode=run --problem=RBC --XPU=GPU --space_time=False
86+
python analysis_scripts/parallel_scaling.py --mode=run --problem=RBC --XPU=GPU --space_time=True
87+
88+
These commands will submit a bunch of jobscripts with the individual runs.
89+
Keep in mind that these are specific to a compute project and some paths are account-specific.
90+
Most likely, you will have to change options at the top of the file `./etc/generate_jobscript.py` before you can run anything.
91+
Also, notice that you may not be allowed to request all resources needed for the largest Gray-Scott GPU run during normal operation of JUWELS booster.
92+
93+
After all jobs have run to completion, you have recorded all scaling data and may plot the results with the following command:
94+
95+
.. code-block:: bash
96+
97+
python paper_plots.py --target=thesis
98+
99+
In order to run the production runs, modify the `path` class attribute of `LargeSim` in `analysis_scripts/large_simulations.py`.
100+
Then use the following commands on the specified machines:
101+
102+
.. code-block:: bash
103+
104+
# run on JUWELS booster
105+
python analysis_scripts/large_simulations.py --mode=run --problem=GS --XPU=GPU
106+
107+
# run on JURECA DC
108+
python analysis_scripts/large_simulations.py --mode=run --problem=RBC --XPU=CPU
109+
110+
Plotting the results of the Gray-Scott simulation requires a lot of memory and will take very long.
111+
Modify the paths in `analysis_scripts/plot_large_simulations.py` and then run:
112+
113+
.. code-block:: bash
114+
115+
python analysis_scripts/3d_plot_GS_large.py --base_path=<path>
116+
python analysis_scripts/plot_large_simulations.py --problem=GS
117+
118+
Plotting the results of the Rayleigh-Benard production run is more easy.
119+
After modifying the paths as earlier, run the following commands:
120+
121+
.. code-block:: bash
122+
123+
python analysis_scripts/large_simulations.py --mode=plot --problem=RBC --XPU=CPU
124+
python analysis_scripts/large_simulations.py --mode=video --problem=RBC --XPU=CPU
125+
python analysis_scripts/plot_large_simulations.py --problem=RBC
126+
127+
Run scripts with `--help` to learn more about parameters.
128+
Keep in mind that not all features are supported with all problems.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import numpy as np
2+
import pickle
3+
import pyvista as pv
4+
import subprocess
5+
import gc
6+
from mpi4py import MPI
7+
from tqdm import tqdm
8+
9+
10+
class Grid:
11+
def __init__(self, grid, label, zoom_range):
12+
self.grid = grid
13+
self.label = label
14+
self.zoom_range = zoom_range
15+
16+
def get_camera_path(self):
17+
import os
18+
19+
os.makedirs('etc/cameras', exist_ok=True)
20+
return f'etc/cameras/cam{self.label}_{len(self.grid.x)}.pickle'
21+
22+
def get_camera_pos(self):
23+
with open(self.get_camera_path(), 'rb') as file:
24+
return pickle.load(file)
25+
26+
def set_camera_pos(self, pos):
27+
with open(self.get_camera_path(), 'wb') as file:
28+
pickle.dump(pos, file)
29+
30+
def get_zoom(self, frame):
31+
return (self.zoom_range[1] - self.zoom_range[0]) * frame / 100 + self.zoom_range[0]
32+
33+
34+
def plot(
35+
n_time,
36+
n_space,
37+
useGPU,
38+
n_frames,
39+
base_path,
40+
space_range,
41+
res,
42+
start_frame=0,
43+
plot_resolution=2048,
44+
n_samples=1024,
45+
zoom=1e-2,
46+
): # pragma: no cover
47+
comm = MPI.COMM_WORLD
48+
49+
space_range = tqdm(space_range)
50+
space_range.set_description('load files')
51+
52+
for frame in range(start_frame, n_frames, comm.size):
53+
i = frame + comm.rank
54+
55+
v = None
56+
gc.collect()
57+
for procs in space_range:
58+
gc.collect()
59+
60+
path = f'{base_path}/GrayScottLarge-res_{res}-useGPU_{useGPU}-procs_1_{n_time}_{n_space}-0-{procs}-solution_{i:06d}.pickle'
61+
with open(path, 'rb') as file:
62+
_data = pickle.load(file)
63+
64+
if v is None:
65+
shape = _data['shape']
66+
v = pv.ImageData(dimensions=shape, spacing=tuple([1 / me for me in shape]))
67+
v['values'] = np.zeros(np.prod(shape))
68+
69+
local_slice_flat = slice(np.prod(_data['v'].shape) * procs, np.prod(_data['v'].shape) * (procs + 1))
70+
v['values'][local_slice_flat] = _data['v'].flatten()
71+
72+
# sampled = Grid(pv.ImageData(dimensions=(n_samples,) * 3, spacing=(1 / n_samples,) * 3), '', [1.0, 1.0])
73+
zoomed = Grid(
74+
pv.ImageData(dimensions=(n_samples,) * 3, spacing=(zoom / n_samples,) * 3, origin=[0.8, 0.47, 0.03]),
75+
'_zoomed',
76+
[1.0, 1.2],
77+
)
78+
79+
for grid in [zoomed]:
80+
81+
p = pv.Plotter(off_screen=True)
82+
contours = grid.grid.sample(v, progress_bar=True, categorical=True).contour(
83+
isosurfaces=[0.3], method='flying_edges', progress_bar=True
84+
)
85+
86+
p.add_mesh(contours, opacity=0.5, cmap=['teal'])
87+
p.remove_scalar_bar()
88+
p.camera.azimuth += 15
89+
90+
p.camera.Elevation(0.9)
91+
plotting_path = './simulation_plots/'
92+
93+
path = f'{plotting_path}/GS_large_{i:06d}{grid.label}.png'
94+
95+
if frame == 0:
96+
grid.set_camera_pos(p.camera.position)
97+
98+
p.camera.position = tuple(pos * grid.get_zoom(frame) for pos in grid.get_camera_pos())
99+
100+
p.screenshot(path, window_size=(plot_resolution,) * 2)
101+
print(f'Saved {path}', flush=True)
102+
103+
104+
def video(view=None): # pragma: no cover
105+
path = f'simulation_plots/GS_large_%06d{view}.png'
106+
path_target = f'videos/GS_large{view}.mp4'
107+
108+
cmd = f'ffmpeg -i {path} -pix_fmt yuv420p -r 9 -s 2048:1536 -y {path_target}'.split()
109+
110+
subprocess.run(cmd)
111+
112+
113+
if __name__ == '__main__':
114+
import argparse
115+
116+
parser = argparse.ArgumentParser()
117+
parser.add_argument('--XPU', type=str, choices=['CPU', 'GPU'], default='GPU')
118+
parser.add_argument('--mode', type=str, choices=['plot', 'video'], default='plot')
119+
parser.add_argument('--nframes', type=int, default=100)
120+
parser.add_argument('--start', type=int, default=0)
121+
parser.add_argument('--base_path', type=str, default='/p/scratch/ccstma/baumann7/large_runs/data/')
122+
parser.add_argument('--space_range', type=int, default=None)
123+
parser.add_argument('--zoom', type=float, default=9e-2)
124+
parser.add_argument('--n_samples', type=int, default=1024)
125+
args = parser.parse_args()
126+
127+
from pySDC.projects.GPU.analysis_scripts.large_simulations import GSLarge
128+
129+
sim = GSLarge()
130+
131+
if args.XPU == 'CPU':
132+
sim.setup_CPU_params()
133+
elif args.XPU == 'GPU':
134+
sim.setup_GPU_params()
135+
else:
136+
raise NotImplementedError()
137+
138+
space_range = range(sim.params['procs'][2] if args.space_range is None else args.space_range)
139+
140+
if args.mode == 'plot':
141+
pv.global_theme.allow_empty_mesh = True
142+
try:
143+
pv.start_xvfb(window_size=(4096, 4096))
144+
except OSError:
145+
pass
146+
plot(
147+
n_time=sim.params['procs'][1],
148+
n_space=sim.params['procs'][2],
149+
useGPU=sim.params['useGPU'],
150+
base_path=args.base_path,
151+
space_range=space_range,
152+
n_frames=args.nframes,
153+
res=sim.params['res'],
154+
start_frame=args.start,
155+
n_samples=args.n_samples,
156+
zoom=args.zoom,
157+
)
158+
elif args.mode == 'video':
159+
for view in ['', '_zoomed']:
160+
video(view)
161+
else:
162+
raise NotImplementedError()

0 commit comments

Comments
 (0)