Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
fbd391e
Start working on opening idfs by time
JoerivanEngelen Oct 1, 2026
0bc5815
Start making test and make sure it works
JoerivanEngelen Oct 1, 2026
0320704
Add chunks assertion
JoerivanEngelen Oct 1, 2026
27d3c7d
Improve docstring load_as_components
JoerivanEngelen Oct 5, 2026
14d4188
Update faulty docstrings
JoerivanEngelen Oct 5, 2026
ac78125
Finish unittests for open_by_time
JoerivanEngelen Oct 5, 2026
b001c45
Put path handling logic in separate helper function and use in more p…
JoerivanEngelen Oct 6, 2026
0a80190
Add docstring
JoerivanEngelen Oct 6, 2026
025a73b
Chunk idf data by time
JoerivanEngelen Oct 6, 2026
0dfa390
Format
JoerivanEngelen Oct 6, 2026
3636410
Add extra assert statments to verify shape and chunksizes
JoerivanEngelen Oct 6, 2026
b9b4f31
Format
JoerivanEngelen Oct 6, 2026
6c22705
Rename function to idf.open_chunked_by_time
JoerivanEngelen Oct 6, 2026
2635c25
Include mypy typing for idf.py
JoerivanEngelen Oct 6, 2026
987ff65
Finish faulty renaming of function
JoerivanEngelen Oct 7, 2026
4b66026
Force to string for regex pattern
JoerivanEngelen Oct 7, 2026
59b45c3
Add user acceptance test
JoerivanEngelen Oct 7, 2026
d354f01
Update changelog
JoerivanEngelen Oct 7, 2026
e38d98b
Call compute and remove values check that would take long and is not …
JoerivanEngelen Oct 7, 2026
e232bc9
Enforce numpy array in delayed task
JoerivanEngelen Oct 7, 2026
9ece88e
Add test
JoerivanEngelen Oct 7, 2026
7f8f7c6
Add option to idf.open "chunk_by" to chunk by time or idf. idf is the…
JoerivanEngelen Oct 12, 2026
1a85198
Reduce test duplication, and add test for idf.open option
JoerivanEngelen Oct 12, 2026
275a462
Clarify chunking behavior with better names for argument and values
JoerivanEngelen Oct 12, 2026
02218e0
Rename open_chunked_by_time to open_chunked_per_timestep
JoerivanEngelen Oct 12, 2026
d73af2f
Clarify private functions as well
JoerivanEngelen Oct 12, 2026
df29547
Format
JoerivanEngelen Oct 12, 2026
804e31a
Merge branch 'master' into issue_#1144_rechunk_projectfile_data
JoerivanEngelen Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/api/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ Added
the final package has fully empty layers trimmed off afterwards, so this
does not affect computed values. Set to False to keep the previous
full-layer-coordinate behaviour.
- Added option ``chunk_layout`` to :func:`imod.idf.open` to control how the data is
loaded in chunks, which can improve performance and memory usage for large datasets.
The default option ``per_idf`` keeps the old behavior, by chunking per IDF file.
The alternative option ``per_timestep`` chunks per timestep.
- Added :class:`imod.mf6.AdvectionUTVD` to the transport packages. This is the
most accurate advection scheme in most cases, and is recommended for when
sharp fronts need to be preserved or when computing on an unstructured grid.
Expand Down Expand Up @@ -76,6 +80,9 @@ Changed
(:class:`imod.mf6.River`, :class:`imod.mf6.GeneralHeadBoundary`,
:class:`imod.mf6.Drainage`, :class:`imod.mf6.Recharge`) will also be masked
where constant head cells are placed.
- :func:`imod.formats.prj.open_projectfile_data` now chunks the data by time
only instead of by both time and layer. This should enhance performance when
working with large datasets.

[1.1.0] - 2026-08-03
--------------------
Expand Down
69 changes: 64 additions & 5 deletions imod/formats/array_io/reading.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,33 @@ def _dask(path, attrs=None, pattern=None, _read=None, header=None):
return x, attrs


def _load(paths, use_cftime, _read, headers):
"""Combine a list of paths to IDFs to a single xarray.DataArray"""
def load_as_components(paths, use_cftime, _read, headers):
"""
Combine a list of paths to IDFs to components that can be used to construct
a single xarray.DataArray.

Parameters
----------
paths : list[str]
List of file paths to IDF files.
use_cftime : bool
Whether to use cftime for time coordinates.
_read : Callable
Function to read individual IDF files.
headers : list[dict]
List of headers corresponding to each IDF file.

Returns
-------
dask.array
Dask array containing the combined data from all IDF files.
dict
Dictionary of coordinates for the DataArray.
list[str]
List of dimension names for the DataArray.
str
Name of the DataArray.
"""
# this function also works for single IDFs
names = [h["name"] for h in headers]
_all_equal(names, "names")
Expand Down Expand Up @@ -320,8 +345,14 @@ def _load(paths, use_cftime, _read, headers):
nested_dict.set_nested(groupby, groupbykeys, da)
dask_arrays = nested_dict.sorted_nested_dict(groupby)
dask_array = _ndconcat(dask_arrays, ndim)
return dask_array, coords, dims, names[0]


out = xr.DataArray(dask_array, coords, dims, name=names[0])
def _load(paths, use_cftime, _read, headers):
dask_array, coords, dims, name = load_as_components(
paths, use_cftime, _read, headers
)
out = xr.DataArray(dask_array, coords, dims, name=name)

first_attrs = headers[0]

Expand All @@ -333,7 +364,27 @@ def _load(paths, use_cftime, _read, headers):
return out


def _open(path, use_cftime, pattern, header, _read):
def handle_path(
path: pathlib.Path | str | list[pathlib.Path | str],
) -> list[pathlib.Path]:
"""
Handle a path input and return a list of pathlib.Path objects.

Parameters
----------
path : pathlib.Path, str, or list of pathlib.Path
The input path(s) to handle.

Returns
-------
list[pathlib.Path]
List of pathlib.Path objects corresponding to the input path(s).

Raises
------
FileNotFoundError
If no files matching the input path(s) are found.
"""
if isinstance(path, pathlib.Path):
path = str(path)

Expand All @@ -342,8 +393,16 @@ def _open(path, use_cftime, pattern, header, _read):
else:
paths = [pathlib.Path(p) for p in glob.glob(path)]

headers = [header(p, pattern) for p in paths]
n = len(paths)
if n == 0:
raise FileNotFoundError(f"Could not find any files matching {path}")

return paths


def _open(path, use_cftime, pattern, header, _read):
paths = handle_path(path)

headers = [header(p, pattern) for p in paths]

return _load(paths, use_cftime, _read, headers)
Loading
Loading