Skip to content

Commit 26beced

Browse files
authored
Merge pull request #91 from StagPython/h5path
Simplify logic around legacy vs hdf5 paths
2 parents cc648c6 + d3e5444 commit 26beced

6 files changed

Lines changed: 102 additions & 122 deletions

File tree

src/stagpy/_caching.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
if typing.TYPE_CHECKING:
1414
from collections.abc import Mapping
15+
from pathlib import Path
1516

1617
from .datatypes import Field
1718
from .stagyydata import StagyyData
@@ -89,15 +90,14 @@ class StepSnapInfo:
8990

9091
@dataclass(frozen=True)
9192
class StepSnapH5(StepSnap):
92-
sdat: StagyyData
93+
timeh5: Path
9394

9495
@cached_property
9596
def _info(self) -> StepSnapInfo:
96-
assert self.sdat.hdf5 is not None
9797
isnap = -1
9898
step_to_snap = {}
9999
snap_to_step = {}
100-
for isnap, istep in parsers.h5.extras.isnap_istep(self.sdat.hdf5):
100+
for isnap, istep in parsers.h5.extras.isnap_istep(self.timeh5):
101101
step_to_snap[istep] = isnap
102102
snap_to_step[isnap] = istep
103103
return StepSnapInfo(
@@ -131,7 +131,7 @@ def _snap_to_step(self) -> dict[int, int | None]:
131131
@cached_property
132132
def isnap_max(self) -> int:
133133
imax = -1
134-
out_stem = re.escape(self.sdat.par.legacy_output("_").name[:-1])
134+
out_stem = re.escape(self.sdat.par.legacy_output("").name[:-1])
135135
rgx = re.compile(f"^{out_stem}_([a-zA-Z]+)([0-9]{{5}})$")
136136
fstems = set(fstem for fstem in phyvars.FIELD_FILES)
137137
for fname in self.sdat._files:

src/stagpy/parfile.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def from_main_par(parfile: Path, read_parameters_dat: bool = True) -> StagyyPar:
6868
par_main = StagyyPar(nml=par_dflt.nml, root=par_main.root)
6969

7070
if read_parameters_dat:
71-
outfile = par_main.legacy_output("_parameters.dat")
71+
outfile = par_main.legacy_output("parameters.dat")
7272
if outfile.is_file():
7373
par_main._update(StagyyPar._from_file(outfile))
7474
outfile = par_main.h5_output("parameters.dat")
@@ -80,9 +80,11 @@ def get(self, section: str, option: str, default: T) -> T:
8080
sec = self.nml.get(section, {})
8181
return sec.get(option, default)
8282

83-
def legacy_output(self, suffix: str) -> Path:
83+
def legacy_output(self, suffix: str, isnap: int | None = None) -> Path:
84+
if isnap is not None:
85+
suffix += f"{isnap:05d}"
8486
stem = self.get("ioin", "output_file_stem", "output")
85-
return self.root / (stem + suffix)
87+
return self.root / f"{stem}_{suffix}"
8688

8789
def h5_output(self, filename: str) -> Path:
8890
h5folder = self.get("ioin", "hdf5_output_folder", "+hdf5")

src/stagpy/parsers/h5/extras.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
from pathlib import Path
1010

1111

12-
def isnap_istep(h5folder: Path) -> Iterator[tuple[int, int]]:
13-
"""Iterate through (isnap, istep) recorded in h5folder/'time_botT.h5'.
12+
def isnap_istep(timeh5: Path) -> Iterator[tuple[int, int]]:
13+
"""Iterate through (isnap, istep) recorded in 'time_botT.h5'.
1414
1515
Args:
16-
h5folder: directory of HDF5 output files.
16+
timeh5: path of the time h5 file.
1717
1818
Yields:
1919
tuple (isnap, istep).
2020
"""
21-
with h5py.File(h5folder / "time_botT.h5", "r") as h5f:
21+
with h5py.File(timeh5, "r") as h5f:
2222
for name, dset in h5f.items():
2323
isnap = int(name[-5:])
2424
if len(dset) == 3:

src/stagpy/stagyydata.py

Lines changed: 51 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,10 @@ class Refstate:
7878
@cached_property
7979
def _data(self) -> tuple[list[list[DataFrame]], list[DataFrame]]:
8080
"""Read reference state profile."""
81-
reffile = self.sdat.filename("refstat.dat")
82-
if self.sdat.hdf5 and not reffile.is_file():
83-
# check legacy folder as well
84-
reffile = self.sdat.filename("refstat.dat", force_legacy=True)
85-
data = parsers.txt.refstate(reffile)
81+
reffile = self.sdat._find_file("refstat.dat")
82+
data = None
83+
if reffile is not None:
84+
data = parsers.txt.refstate(reffile)
8685
if data is None:
8786
raise error.NoRefstateError(self.sdat)
8887
return data
@@ -147,15 +146,14 @@ def _cached_extra(self) -> dict[str, dt.Tseries]:
147146

148147
@cached_property
149148
def _data(self) -> DataFrame | None:
150-
timefile = self.sdat.filename("TimeSeries.h5")
149+
timefile: Path | None
150+
timefile = self.sdat.par.h5_output("TimeSeries.h5")
151151
data = parsers.h5.tseries.tseries(timefile)
152152
if data is not None:
153153
return data
154-
timefile = self.sdat.filename("time.dat")
155-
if self.sdat.hdf5 and not timefile.is_file():
156-
# check legacy folder as well
157-
timefile = self.sdat.filename("time.dat", force_legacy=True)
158-
data = parsers.txt.tseries(timefile)
154+
timefile = self.sdat._find_file("time.dat")
155+
if timefile is not None:
156+
data = parsers.txt.tseries(timefile)
159157
return data
160158

161159
@property
@@ -671,12 +669,6 @@ def parpath(self) -> Path:
671669
return parpath
672670
return parpath / "par"
673671

674-
@cached_property
675-
def hdf5(self) -> Path | None:
676-
"""Path of output hdf5 folder if relevant, None otherwise."""
677-
h5xmf = self.par.h5_output("Data.xmf")
678-
return h5xmf.parent if h5xmf.is_file() else None
679-
680672
@cached_property
681673
def steps(self) -> Steps:
682674
"""Collection of time steps."""
@@ -698,32 +690,32 @@ def refstate(self) -> Refstate:
698690
return Refstate(self)
699691

700692
@cached_property
701-
def _dataxmf(self) -> FieldXmf:
702-
assert self.hdf5 is not None
703-
return FieldXmf(
704-
path=self.hdf5 / "Data.xmf",
705-
)
693+
def _dataxmf(self) -> FieldXmf | None:
694+
path = self.par.h5_output("Data.xmf")
695+
if path.is_file():
696+
return FieldXmf(path=path)
697+
return None
706698

707699
@cached_property
708-
def _topxmf(self) -> FieldXmf:
709-
assert self.hdf5 is not None
710-
return FieldXmf(
711-
path=self.hdf5 / "DataSurface.xmf",
712-
)
700+
def _topxmf(self) -> FieldXmf | None:
701+
path = self.par.h5_output("DataSurface.xmf")
702+
if path.is_file():
703+
return FieldXmf(path=path)
704+
return None
713705

714706
@cached_property
715-
def _botxmf(self) -> FieldXmf:
716-
assert self.hdf5 is not None
717-
return FieldXmf(
718-
path=self.hdf5 / "DataBottom.xmf",
719-
)
707+
def _botxmf(self) -> FieldXmf | None:
708+
path = self.par.h5_output("DataBottom.xmf")
709+
if path.is_file():
710+
return FieldXmf(path=path)
711+
return None
720712

721713
@cached_property
722-
def _traxmf(self) -> TracersXmf:
723-
assert self.hdf5 is not None
724-
return TracersXmf(
725-
path=self.hdf5 / "DataTracers.xmf",
726-
)
714+
def _traxmf(self) -> TracersXmf | None:
715+
path = self.par.h5_output("DataTracers.xmf")
716+
if path.is_file():
717+
return TracersXmf(path=path)
718+
return None
727719

728720
@cached_property
729721
def par(self) -> StagyyPar:
@@ -732,15 +724,15 @@ def par(self) -> StagyyPar:
732724

733725
@cached_property
734726
def _rprof_and_times(self) -> tuple[dict[int, DataFrame], DataFrame | None]:
735-
rproffile = self.filename("rprof.h5")
727+
rproffile: Path | None
728+
rproffile = self.par.h5_output("rprof.h5")
736729
data = parsers.h5.rprof.rprof(rproffile)
737730
if data[1] is not None:
738731
return data
739-
rproffile = self.filename("rprof.dat")
740-
if self.hdf5 and not rproffile.is_file():
741-
# check legacy folder as well
742-
rproffile = self.filename("rprof.dat", force_legacy=True)
743-
return parsers.txt.rprof(rproffile)
732+
rproffile = self._find_file("rprof.dat")
733+
if rproffile is not None:
734+
return parsers.txt.rprof(rproffile)
735+
return {}, None
744736

745737
@property
746738
def rtimes(self) -> DataFrame | None:
@@ -750,7 +742,7 @@ def rtimes(self) -> DataFrame | None:
750742
@cached_property
751743
def _files(self) -> set[Path]:
752744
"""Set of found binary files output by StagYY."""
753-
out_dir = self.par.legacy_output("_").parent
745+
out_dir = self.par.legacy_output("").parent
754746
if out_dir.is_dir():
755747
return set(out_dir.iterdir())
756748
return set()
@@ -767,32 +759,19 @@ def set_nfields_max(self, nfields: int | None) -> None:
767759
raise error.InvalidNfieldsError(nfields)
768760
self._field_cache.resize(nfields)
769761

770-
def filename(
771-
self,
772-
fname: str,
773-
timestep: int | None = None,
774-
suffix: str = "",
775-
force_legacy: bool = False,
776-
) -> Path:
777-
"""Return name of StagYY output file.
778-
779-
Args:
780-
fname: name stem.
781-
timestep: snapshot number if relevant.
782-
suffix: optional suffix of file name.
783-
force_legacy: force returning the legacy output path.
762+
def _find_file(self, fname: str) -> Path | None:
763+
"""Return path of StagYY output file if found.
784764
785-
Returns:
786-
the path of the output file constructed with the provided segments.
765+
This searches in the legacy folder first, and then the hdf5
766+
output folder.
787767
"""
788-
if timestep is not None:
789-
fname += f"{timestep:05d}"
790-
fname += suffix
791-
if not force_legacy and self.hdf5:
792-
fpath = self.par.h5_output(fname)
793-
else:
794-
fpath = self.par.legacy_output(f"_{fname}")
795-
return fpath
768+
fpath = self.par.legacy_output(fname)
769+
if fpath.is_file():
770+
return fpath
771+
fpath = self.par.h5_output(fname)
772+
if fpath.is_file():
773+
return fpath
774+
return None
796775

797776
def _binfiles_set(self, isnap: int) -> set[Path]:
798777
"""Set of existing binary files at a given snap.
@@ -804,8 +783,7 @@ def _binfiles_set(self, isnap: int) -> set[Path]:
804783
the set of output files available for this snapshot number.
805784
"""
806785
possible_files = set(
807-
self.filename(fstem, isnap, force_legacy=True)
808-
for fstem in phyvars.FIELD_FILES
786+
self.par.legacy_output(fstem, isnap) for fstem in phyvars.FIELD_FILES
809787
)
810788
return possible_files & self._files
811789

@@ -815,6 +793,7 @@ def _field_cache(self) -> FieldCache:
815793

816794
@cached_property
817795
def _step_snap(self) -> StepSnap:
818-
if self.hdf5 is not None:
819-
return StepSnapH5(sdat=self)
796+
timeh5 = self.par.h5_output("time_botT.h5")
797+
if timeh5.is_file():
798+
return StepSnapH5(timeh5=timeh5)
820799
return StepSnapLegacy(sdat=self)

src/stagpy/step.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def _maybe_header(self) -> dict[str, Any] | None:
5050
header = None
5151
if binfiles:
5252
header = parsers.bin.field.header(binfiles.pop())
53-
elif sdat.hdf5:
53+
elif sdat._dataxmf is not None:
5454
header = parsers.h5.field.read_geom(sdat._dataxmf, self.step.isnap)
5555
return header if header else None
5656

@@ -352,32 +352,32 @@ def _get_raw_data(self, name: str) -> tuple[list[str], Any]:
352352
parsed_data = None
353353
if self.step.isnap is None:
354354
return list_fvar, None
355-
fieldfile = self.step.sdat.filename(
356-
filestem, self.step.isnap, force_legacy=True
357-
)
358-
if not fieldfile.is_file():
359-
fieldfile = self.step.sdat.filename(filestem, self.step.isnap)
355+
fieldfile = self.step.sdat.par.legacy_output(filestem, self.step.isnap)
360356
if fieldfile.is_file():
361357
parsed_data = parsers.bin.field.field(fieldfile)
362-
elif self.step.sdat.hdf5 and self.filesh5:
363-
# files in which the requested data can be found
364-
files = [
365-
(stem, fvars) for stem, fvars in self.filesh5.items() if name in fvars
366-
]
367-
for filestem, list_fvar in files:
368-
sdat = self.step.sdat
369-
if filestem in phyvars.SFIELD_FILES_H5:
370-
xmff = sdat._botxmf if name.endswith("bot") else sdat._topxmf
371-
header = self.step.geom._maybe_header
372-
assert header is not None
373-
else:
374-
xmff = sdat._dataxmf
375-
header = None
376-
parsed_data = parsers.h5.field.field(
377-
xmff, filestem, self.step.isnap, header
378-
)
379-
if parsed_data is not None:
380-
break
358+
return list_fvar, parsed_data
359+
if not self.filesh5:
360+
return list_fvar, parsed_data
361+
# files in which the requested data can be found
362+
files = [(stem, fvars) for stem, fvars in self.filesh5.items() if name in fvars]
363+
sdat = self.step.sdat
364+
if filestem in phyvars.SFIELD_FILES_H5:
365+
xmff = sdat._botxmf if name.endswith("bot") else sdat._topxmf
366+
else:
367+
xmff = sdat._dataxmf
368+
if xmff is None:
369+
return list_fvar, parsed_data
370+
for filestem, list_fvar in files:
371+
if filestem in phyvars.SFIELD_FILES_H5:
372+
header = self.step.geom._maybe_header
373+
assert header is not None
374+
else:
375+
header = None
376+
parsed_data = parsers.h5.field.field(
377+
xmff, filestem, self.step.isnap, header
378+
)
379+
if parsed_data is not None:
380+
break
381381
return list_fvar, parsed_data
382382

383383

@@ -403,12 +403,13 @@ def __getitem__(self, name: str) -> list[NDArray[np.floating]] | None:
403403
return self._data[name]
404404
if self.step.isnap is None:
405405
return None
406+
sdat = self.step.sdat
406407
data = parsers.bin.tracers.tracers(
407-
self.step.sdat.filename("tra", timestep=self.step.isnap, force_legacy=True)
408+
sdat.par.legacy_output("tra", self.step.isnap)
408409
)
409-
if data is None and self.step.sdat.hdf5:
410+
if data is None and sdat._traxmf is not None:
410411
self._data[name] = parsers.h5.tracers.tracers( # type: ignore
411-
self.step.sdat._traxmf,
412+
sdat._traxmf,
412413
name,
413414
self.step.isnap,
414415
)

0 commit comments

Comments
 (0)