Skip to content

Commit 4ca8445

Browse files
committed
🎨 nt2py new funcs + housekeeping
1 parent 92f9a3c commit 4ca8445

File tree

3 files changed

+102
-61
lines changed

3 files changed

+102
-61
lines changed

src/pic/pgen/inputs/langmuir.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[simulation]
2-
title = "Reconnection"
2+
title = "Langmuir"
33
runtime = 100.0
44

55
[domain]

src/pic/pgen/magnetosphere.hpp

+3-7
Original file line numberDiff line numberDiff line change
@@ -154,23 +154,19 @@ namespace ntt {
154154
struct InjectionShell : public SpatialDistribution<D, S> {
155155
explicit InjectionShell(const SimulationParams& params, Meshblock<D, S>& mblock)
156156
: SpatialDistribution<D, S>(params, mblock) {
157-
inj_rmax = params.get<real_t>("problem", "inj_rmax", 1.5 * mblock.metric.x1_min);
158-
inj_thmin = params.get<real_t>("problem", "inj_thmin", 0.0);
157+
inj_rmax = params.get<real_t>("problem", "inj_rmax", 1.5 * mblock.metric.x1_min);
159158
const int buff_cells = 10;
160159
coord_t<D> xcu { ZERO }, xph { ZERO };
161160
xcu[0] = (real_t)buff_cells;
162161
mblock.metric.x_Code2Sph(xcu, xph);
163162
inj_rmin = xph[0];
164163
}
165164
Inline real_t operator()(const coord_t<D>& x_ph) const {
166-
return ((x_ph[0] <= inj_rmax) && (x_ph[0] > inj_rmin)
167-
&& ((x_ph[1] > inj_thmin) && (x_ph[1] <= constant::PI - inj_thmin)))
168-
? ONE
169-
: ZERO;
165+
return ((x_ph[0] <= inj_rmax) && (x_ph[0] > inj_rmin)) ? ONE : ZERO;
170166
}
171167

172168
private:
173-
real_t inj_rmax, inj_rmin, inj_thmin;
169+
real_t inj_rmax, inj_rmin;
174170
};
175171

176172
template <Dimension D, SimulationEngine S>

vis/nt2.py

+98-53
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ def EdgeToCenter(arr):
129129
dask_arrays.append(array[ngh:-ngh, ngh:-ngh])
130130

131131
k_ = reduce(
132-
lambda x, y: x.replace(*y),
132+
lambda x, y: x.replace(*y)
133+
if "_" not in x
134+
else "_".join([x.split("_")[0].replace(*y)] + x.split("_")[1:]),
133135
[k, *list(CoordinateDict[metric].items())],
134136
)
135137
k_ = reduce(
@@ -152,71 +154,114 @@ def EdgeToCenter(arr):
152154
return ds
153155

154156

155-
def plotAndSave(st, plot_, fpath_, dpi_):
156-
import matplotlib.pyplot as plt
157+
def makeMovie(**ffmpeg_kwargs):
158+
"""
159+
Create a movie from frames using the `ffmpeg` command-line tool.
157160
158-
try:
159-
plot_(st)
160-
plt.savefig(f"{fpath_}/{st:05d}.png", dpi=dpi_, bbox_inches="tight")
161-
plt.close()
161+
Parameters
162+
----------
163+
ffmpeg_kwargs : dict
164+
Keyword arguments for the `ffmpeg` command-line tool.
165+
166+
Returns
167+
-------
168+
bool
169+
True if the movie was created successfully, False otherwise.
170+
171+
Notes
172+
-----
173+
This function uses the `subprocess` module to execute the `ffmpeg` command-line
174+
tool with the given arguments.
175+
176+
Examples
177+
--------
178+
>>> makeMovie(ffmpeg="/path/to/ffmpeg", framerate="30", start="0", input="step_", number=3,
179+
extension="png", compression="1", output="anim.mov")
180+
"""
181+
import subprocess
182+
183+
command = [
184+
ffmpeg_kwargs.get("ffmpeg", "ffmpeg"),
185+
"-nostdin",
186+
"-framerate",
187+
ffmpeg_kwargs.get("framerate", "30"),
188+
"-start_number",
189+
ffmpeg_kwargs.get("start", "0"),
190+
"-i",
191+
ffmpeg_kwargs.get("input", "step_")
192+
+ f"%0{ffmpeg_kwargs.get('number', 3)}d.{ffmpeg_kwargs.get('extension', 'png')}",
193+
"-c:v",
194+
"libx264",
195+
"-crf",
196+
ffmpeg_kwargs.get("compression", "1"),
197+
"-filter_complex",
198+
"[0:v]format=yuv420p,pad=ceil(iw/2)*2:ceil(ih/2)*2",
199+
ffmpeg_kwargs.get("output", "anim.mov"),
200+
]
201+
command = [str(c) for c in command]
202+
result = subprocess.run(command, capture_output=True, text=True)
203+
204+
if result.returncode == 0:
205+
print("ffmpeg -- [OK]")
162206
return True
163-
except:
207+
else:
208+
print("ffmpeg -- [not OK]", result.returncode, result.stdout, result.stderr)
164209
return False
165210

166211

167-
def makeMovie(plot, steps, fpath, dpi=300, num_cpus=mp.cpu_count()):
212+
def makeFrames(plot, ds, steps, fpath, num_cpus=mp.cpu_count()):
168213
"""
169-
Generates a movie by applying the `plot` function to each step in `steps`, and saving the resulting figure at the specified `fpath` with the specified `dpi`.
214+
Create plot frames from a set of timesteps of the same dataset.
170215
171216
Parameters
172217
----------
173218
plot : function
174-
A function that accepts a single argument 'st' and returns a figure.
175-
steps : list
176-
A list of steps that the `plot` function will be applied to.
219+
A function that generates and saves the plot. The function must take a time index
220+
as its first argument, followed by the data source, and the path for png files.
221+
ds : object
222+
The data source used to generate the plots.
223+
steps : array_like, optional
224+
The time indices to use for generating the movie. If None, use all
225+
time indices in `ds` (defined by the `t` key).
177226
fpath : str
178-
The file path where the figures will be saved.
179-
dpi : int, optional
180-
The dpi of the saved figures. Defaults to 300.
227+
The file path to save the frames.
181228
num_cpus : int, optional
182-
The number of CPUs to use for parallel processing. Defaults to the number of CPUs on the current machine.
229+
The number of CPUs to use for parallel processing. If None, use all available CPUs.
183230
184231
Returns
185232
-------
186-
None
233+
list
234+
A list of results returned by the `plot` function, one for each time index.
235+
236+
Raises
237+
------
238+
ValueError
239+
If `plot` is not a callable function.
240+
241+
Notes
242+
-----
243+
This function uses the `multiprocessing` module to parallelize the generation
244+
of the plots, and `tqdm` module to display a progress bar.
245+
246+
Examples
247+
--------
248+
>>> makeFrames(plot_func, data_source, range(100), 'output/', num_cpus=16)
187249
"""
188-
import tqdm
189-
from functools import partial
190-
191-
# from multiprocessing import Pool
192-
import os
193-
from multiprocessing import get_context
194-
195-
print(f"Processing on {num_cpus} CPUs\n")
196-
print(f"1. Writing frames to `{fpath}/%05d.png`.")
197-
198-
if not os.path.exists(fpath):
199-
os.makedirs(fpath)
200-
201-
# with Pool(num_cpus) as p:
202-
with get_context("spawn").Pool(num_cpus) as pool:
203-
with tqdm.tqdm(total=len(steps)) as pbar:
204-
for _ in pool.imap_unordered(
205-
partial(plotAndSave, plot_=plot, fpath_=fpath, dpi_=dpi), steps
206-
):
207-
pbar.update()
208-
209-
# result = list(
210-
# tqdm.tqdm(
211-
# p.imap_unordered(
212-
# partial(plotAndSave, plot_=plot, fpath_=fpath, dpi_=dpi),
213-
# steps,
214-
# ),
215-
# total=len(steps),
216-
# )
217-
# )
218-
# retur
219-
# retur
220-
# retur
221-
# return resultn resultn resultn result
222-
# return result
250+
251+
from tqdm import tqdm
252+
import numpy as np
253+
import multiprocessing as mp
254+
255+
if num_cpus is None:
256+
num_cpus = mp.cpu_count()
257+
258+
pool = mp.Pool(num_cpus)
259+
260+
if steps is None:
261+
steps = np.arange(len(ds.t))
262+
else:
263+
steps = np.array(steps)
264+
265+
tasks = [[ti, ds, fpath] for ti in steps]
266+
results = [pool.apply_async(plot, t) for t in tasks]
267+
return [result.get() for result in tqdm(results)]

0 commit comments

Comments
 (0)