Skip to content

Commit e1f48c6

Browse files
committed
Merge branch 'topic/default/util-phys-fields' into 'branch/default'
Better util.phys_fields See merge request fluiddyn/fluidsim!443
2 parents 448fbd9 + 40e69b2 commit e1f48c6

File tree

4 files changed

+86
-45
lines changed

4 files changed

+86
-45
lines changed

fluidsim/base/output/model.xmf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515
</Geometry>
1616
<Attribute Name="vx" AttributeType="Scalar" Center="Cell">
1717
<DataItem Dimensions="16 16 16" NumberType="Float" Format="HDF">
18-
state_phys_t=000.000_it=0.hd5:/state_phys/vx
18+
state_phys_t000.000_it0.hd5:/state_phys/vx
1919
</DataItem>
2020
</Attribute>
2121
<Attribute Name="vy" AttributeType="Scalar" Center="Cell">
2222
<DataItem Dimensions="16 16 16" NumberType="Float" Format="HDF">
23-
state_phys_t=000.000_it=0.hd5:/state_phys/vy
23+
state_phys_t000.000_it0.hd5:/state_phys/vy
2424
</DataItem>
2525
</Attribute>
2626
<Attribute Name="vz" AttributeType="Scalar" Center="Cell">
2727
<DataItem Dimensions="16 16 16" NumberType="Float" Format="HDF">
28-
state_phys_t=000.000_it=0.hd5:/state_phys/vz
28+
state_phys_t000.000_it0.hd5:/state_phys/vz
2929
</DataItem>
3030
</Attribute>
3131
</Grid>

fluidsim/util/phys_fields.py

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -147,27 +147,46 @@ def save_file(
147147

148148
def compute_file_name(time, str_width, ext, it=None):
149149
"""Compute the file name from time and co"""
150-
str_it = "" if it is None else f"_{it=}"
150+
str_it = "" if it is None else f"_it{it}"
151151
return f"state_phys_t{time:0{str_width}.3f}{str_it}.{ext}"
152152

153153

154-
def time_from_path(path):
154+
# Module-level variable, compiled on first use
155+
_TIME_PATTERN = None
156+
157+
158+
def time_from_path(path, exact=False):
155159
"""Regular expression search to extract time from filename."""
160+
161+
if exact:
162+
with h5py.File(path, "r") as file:
163+
time = file.attrs["time"]
164+
return time
165+
166+
global _TIME_PATTERN
167+
if _TIME_PATTERN is None:
168+
_TIME_PATTERN = re.compile(
169+
r"""
170+
(?!t) # text after t but exclude it
171+
[0-9]+ # a couple of digits
172+
\. # the decimal point
173+
[0-9]+ # a couple of digits
174+
""",
175+
re.VERBOSE,
176+
)
177+
156178
filename = os.path.basename(path)
157-
pattern = r"""
158-
(?!t) # text after t but exclude it
159-
[0-9]+ # a couple of digits
160-
\. # the decimal point
161-
[0-9]+ # a couple of digits
162-
"""
163-
match = re.search(pattern, filename, re.VERBOSE)
179+
match = _TIME_PATTERN.search(filename)
164180
time = float(match.group(0))
165181
return time
166182

167183

168184
def name_file_from_time_approx(path_dir, t_approx=None):
169185
"""Return the file name whose time is the closest to the given time.
170186
187+
Warning: for parallel runs and if ``t_approx is not None``, it is safer
188+
to only call this function by one process.
189+
171190
Parameters
172191
----------
173192
@@ -178,11 +197,8 @@ def name_file_from_time_approx(path_dir, t_approx=None):
178197
t_approx : number or "last" (optional)
179198
180199
Approximate time of the file to be loaded.
181-
182-
.. todo::
183-
184-
Can be elegantly implemented using regex as done in
185-
``fluidsim.base.output.phys_fields.time_from_path``
200+
If "last", use the last time.
201+
If None, just return the last file name (sorted in alphabetic order).
186202
187203
"""
188204
if not isinstance(path_dir, Path):
@@ -198,20 +214,13 @@ def name_file_from_time_approx(path_dir, t_approx=None):
198214
# should be the last one but not 100% sure
199215
return path_files[-1].name
200216

201-
name_files = [path.name for path in path_files]
202-
if "state_phys_t=" in name_files[0]:
203-
ind_start_time = len("state_phys_t=")
204-
else:
205-
ind_start_time = len("state_phys_t")
206-
207-
times = np.empty([nb_files])
208-
for ii, name in enumerate(name_files):
209-
tmp = ".".join(name[ind_start_time:].split(".")[:2])
210-
if "_" in tmp:
211-
tmp = tmp[: tmp.index("_")]
212-
times[ii] = float(tmp)
217+
# the time are read from the files if at least one of the name contains "_it"
218+
exact = any("_it" in path.name for path in path_files)
219+
times = [time_from_path(path, exact=exact) for path in path_files]
220+
213221
if t_approx == "last":
214-
t_approx = times.max()
215-
i_file = abs(times - t_approx).argmin()
216-
name_file = path_files[i_file].name
217-
return name_file
222+
path_file = max(zip(times, path_files))[1]
223+
else:
224+
i_file = abs(np.array(times) - t_approx).argmin()
225+
path_file = path_files[i_file]
226+
return path_file.name

fluidsim/util/test_phys_fields.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1+
import pytest
2+
13
import numpy as np
24

35
import h5py
46

57
from fluidsim.util.phys_fields import (
68
name_file_from_time_approx,
79
compute_file_name,
10+
time_from_path,
811
)
912

13+
from fluiddyn.util import mpi
14+
15+
16+
dt = 0.0004
17+
times = dt * np.arange(11)
1018

11-
def test_name_file_from_time_approx(tmp_path):
12-
dt = 0.0004
13-
times = dt * np.arange(11)
1419

15-
path_dir = tmp_path / "run_dir"
16-
path_dir.mkdir()
20+
@pytest.fixture(scope="module")
21+
def path_dir_with_files(tmp_path_factory):
22+
path_dir = tmp_path_factory.mktemp("run_dir")
1723

1824
str_width, ext = 7, "h5"
1925

@@ -27,9 +33,29 @@ def test_name_file_from_time_approx(tmp_path):
2733
file.attrs["time"] = time
2834
file.attrs["it"] = it
2935

30-
# print(path_file)
36+
return path_dir
37+
38+
39+
def test_time_from_path(path_dir_with_files):
40+
paths = sorted(path_dir_with_files.glob("*.h5"))
41+
for it, path in enumerate(paths):
42+
t_from_path = time_from_path(path)
43+
assert t_from_path == round(it * dt, 3)
44+
t_exact_from_path = time_from_path(path, exact=True)
45+
assert t_exact_from_path == it * dt
46+
47+
48+
def test_name_file_from_time_approx(path_dir_with_files):
49+
if mpi.rank > 0:
50+
return
51+
52+
path_dir = path_dir_with_files
3153

3254
name_file_last = name_file_from_time_approx(path_dir)
33-
assert name_file_last == path_file.name
55+
assert name_file_last == "state_phys_t000.004_it10.h5"
56+
57+
name_file = name_file_from_time_approx(path_dir, t_approx=0.0034)
58+
assert name_file == "state_phys_t000.003_it8.h5", name_file
3459

35-
# TODO: add assert statements to get bugs!
60+
name_file_last = name_file_from_time_approx(path_dir, t_approx="last")
61+
assert name_file_last == "state_phys_t000.004_it10.h5"

fluidsim/util/util.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,16 @@ def _path_file_from_time_approx(thing, t_approx):
279279
if thing is not None and Path(thing).is_file():
280280
path_file = Path(thing)
281281
else:
282-
path_dir = pathdir_from_namedir(thing)
282+
path_dir = Path(pathdir_from_namedir(thing))
283283
# choose the file with the time closer to t_approx
284-
name_file = name_file_from_time_approx(path_dir, t_approx)
285-
path_file = Path(path_dir) / name_file
284+
if mpi.rank == 0:
285+
name_file = name_file_from_time_approx(path_dir, t_approx)
286+
else:
287+
name_file = None
288+
if mpi.nb_proc > 1:
289+
name_file = mpi.comm.bcast(name_file, root=0)
290+
291+
path_file = path_dir / name_file
286292
return path_file
287293

288294

@@ -593,7 +599,7 @@ def times_start_last_from_path(path):
593599
if last_line.startswith("save state_phys"):
594600
name_file = last_line.split()[-1]
595601
name_file, ext = os.path.splitext(name_file)
596-
word = name_file.split("_it=")[0].split("state_phys_t")[-1]
602+
word = name_file.split("_it")[0].split("state_phys_t")[-1]
597603
t_last = float(word.replace(ext, ""))
598604
else:
599605
words = line_it.split()

0 commit comments

Comments
 (0)