-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Fixes #1320 # Description The iMOD5 projectfile contains an "EXTRA" section, with files to copy. These files usually contain settings and lookup tables for MetaSWAP, which have no direct relation to cell data. This PR adds CopyFiles package to copy these files. # Checklist - [x] Links to correct issue - [x] Update changelog, if changes affect users - [x] PR title starts with ``Issue #nr``, e.g. ``Issue #737`` - [x] Unit tests were added - [ ] **If feature added**: Added/extended example
- Loading branch information
1 parent
38cad96
commit 29886af
Showing
4 changed files
with
127 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from pathlib import Path | ||
from shutil import copy2 | ||
from typing import cast | ||
|
||
import numpy as np | ||
import xarray as xr | ||
|
||
from imod.logging import logger | ||
from imod.logging.loglevel import LogLevel | ||
from imod.msw.pkgbase import MetaSwapPackage | ||
from imod.typing import Imod5DataDict | ||
|
||
_LOG_MESSAGE_TEMPLATE = """\ | ||
Will not copy files {filtered}, these will be generated by iMOD Python | ||
instead.""" | ||
|
||
|
||
class FileCopier(MetaSwapPackage): | ||
def __init__(self, paths: list[str]): | ||
super().__init__() | ||
paths_da = xr.DataArray( | ||
paths, coords={"file_nr": np.arange(len(paths))}, dims=("file_nr",) | ||
) | ||
self.dataset["paths"] = paths_da | ||
|
||
@classmethod | ||
def from_imod5_data(cls, imod5_data: Imod5DataDict): | ||
paths = cast(list[list[str]], imod5_data["extra"]["paths"]) | ||
paths_unpacked = {Path(p[0]) for p in paths} | ||
files_to_filter = ( | ||
"mete_grid.inp", | ||
"para_sim.inp", | ||
"svat2precgrid.inp", | ||
"svat2etrefgrid.inp", | ||
) | ||
paths_included = [ | ||
str(p) for p in paths_unpacked if p.name.lower() not in files_to_filter | ||
] | ||
paths_excluded = {str(p) for p in paths_unpacked} - set(paths_included) | ||
if paths_excluded: | ||
log_message = _LOG_MESSAGE_TEMPLATE.format(filtered=paths_excluded) | ||
logger.log( | ||
loglevel=LogLevel.INFO, | ||
message=log_message, | ||
) | ||
return cls(paths_included) | ||
|
||
def write(self, directory: str | Path, *_): | ||
directory = Path(directory) | ||
|
||
src_paths = [Path(p) for p in self.dataset["paths"].to_numpy()] | ||
dst_paths = [directory / p.name for p in src_paths] | ||
|
||
for src_path, dst_path in zip(src_paths, dst_paths): | ||
copy2(src_path, dst_path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
from pytest_cases import parametrize_with_cases | ||
|
||
from imod.msw.copy_files import FileCopier | ||
|
||
|
||
def write_test_files(directory, filenames): | ||
paths = [directory / filename for filename in filenames] | ||
for p in paths: | ||
with open(p, mode="w") as f: | ||
f.write("test") | ||
return paths | ||
|
||
|
||
def case_simple_files(tmp_path_factory): | ||
directory = tmp_path_factory.mktemp("simple_files") | ||
filenames = [ | ||
"a.inp", | ||
"b.inp", | ||
"c.inp", | ||
] | ||
return write_test_files(directory, filenames) | ||
|
||
|
||
def case_imod5_extra_files(tmp_path_factory): | ||
directory = tmp_path_factory.mktemp("imod5_extra_files") | ||
filenames = [ | ||
"a.inp", | ||
"b.inp", | ||
"c.inp", | ||
"mete_grid.inp", | ||
"para_sim.inp", | ||
"svat2precgrid.inp", | ||
"svat2etrefgrid.inp", | ||
] | ||
return write_test_files(directory, filenames) | ||
|
||
|
||
@parametrize_with_cases("src_files", cases=".") | ||
def test_copyfile_init(src_files): | ||
# Act | ||
copyfiles = FileCopier(src_files) | ||
# Arrange | ||
assert "paths" in copyfiles.dataset.keys() | ||
assert len(copyfiles.dataset["paths"]) == len(src_files) | ||
|
||
|
||
@parametrize_with_cases("src_files", cases=".") | ||
def test_copyfile_write(src_files, tmp_path): | ||
# Arrange | ||
expected_filenames = {f.name for f in src_files} | ||
# Act | ||
copyfiles = FileCopier(src_files) | ||
copyfiles.write(tmp_path) | ||
# Assert | ||
actual_filepaths = tmp_path.glob("*.inp") | ||
actual_filenames = {f.name for f in actual_filepaths} | ||
diff = expected_filenames ^ actual_filenames | ||
assert len(diff) == 0 | ||
|
||
|
||
@parametrize_with_cases("src_files", cases=".") | ||
def test_from_imod5_data(src_files): | ||
# Arrange | ||
imod5_ls = [[p] for p in src_files] | ||
imod5_data = {"extra": {"paths": imod5_ls}} | ||
# Act | ||
copyfiles = FileCopier.from_imod5_data(imod5_data) | ||
# Assert | ||
len(copyfiles.dataset["paths"]) == 3 |