From 1dcec41bd60fb8327cb5b4a8c7a2c78ce2c4bba2 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 11:57:51 +0100 Subject: [PATCH 01/17] Move regrid utilities to ``common`` module --- examples/mf6/different_ways_to_regrid_models.py | 2 +- examples/user-guide/08-regridding.py | 4 ++-- imod/common/__init__.py | 1 + imod/{mf6 => common}/utilities/regrid.py | 0 imod/mf6/__init__.py | 1 - imod/mf6/chd.py | 2 +- imod/mf6/dis.py | 10 +++++----- imod/mf6/drn.py | 8 ++++---- imod/mf6/ghb.py | 2 +- imod/mf6/ic.py | 2 +- imod/mf6/model.py | 2 +- imod/mf6/model_gwf.py | 2 +- imod/mf6/npf.py | 8 ++++---- imod/mf6/package.py | 8 ++++---- imod/mf6/rch.py | 2 +- imod/mf6/riv.py | 6 +++--- imod/mf6/simulation.py | 2 +- imod/mf6/sto.py | 2 +- imod/mf6/utilities/imod5_converter.py | 2 +- imod/msw/idf_mapping.py | 2 +- imod/msw/meteo_mapping.py | 2 +- imod/msw/model.py | 2 +- imod/msw/output_control.py | 2 +- imod/msw/pkgbase.py | 6 +++--- imod/tests/test_mf6/test_mf6_hfb.py | 2 +- imod/tests/test_mf6/test_mf6_rch.py | 2 +- imod/tests/test_mf6/test_mf6_regrid_package.py | 2 +- imod/tests/test_mf6/test_mf6_regrid_scheme.py | 2 +- imod/tests/test_mf6/test_mf6_regrid_simulation.py | 2 +- .../test_mf6/test_mf6_unsupported_grid_operations.py | 2 +- .../tests/test_mf6/test_utilities/test_regrid_utils.py | 2 +- imod/tests/test_msw/test_annual_crop_factors.py | 2 +- imod/tests/test_msw/test_grid_data.py | 2 +- imod/tests/test_msw/test_idf_mapping.py | 2 +- imod/tests/test_msw/test_infiltration.py | 2 +- imod/tests/test_msw/test_initial_conditions.py | 2 +- imod/tests/test_msw/test_landuse_options.py | 2 +- imod/tests/test_msw/test_meteo_grid.py | 2 +- imod/tests/test_msw/test_model.py | 2 +- imod/tests/test_msw/test_ponding.py | 2 +- imod/tests/test_msw/test_scaling_factors.py | 2 +- 41 files changed, 57 insertions(+), 57 deletions(-) create mode 100644 imod/common/__init__.py rename imod/{mf6 => common}/utilities/regrid.py (100%) diff --git a/examples/mf6/different_ways_to_regrid_models.py b/examples/mf6/different_ways_to_regrid_models.py index d789766f8..d2c83e799 100644 --- a/examples/mf6/different_ways_to_regrid_models.py +++ b/examples/mf6/different_ways_to_regrid_models.py @@ -25,8 +25,8 @@ import xarray as xr from example_models import create_twri_simulation +from imod.common.utilities.regrid import RegridderType, RegridderWeightsCache from imod.mf6.regrid import NodePropertyFlowRegridMethod -from imod.mf6.utilities.regrid import RegridderType, RegridderWeightsCache # %% # Now we create the twri simulation itself. It yields a simulation of a flow problem, with a grid of 3 layers and 15 cells in both x and y directions. diff --git a/examples/user-guide/08-regridding.py b/examples/user-guide/08-regridding.py index ee81e832c..370552cc3 100644 --- a/examples/user-guide/08-regridding.py +++ b/examples/user-guide/08-regridding.py @@ -189,7 +189,7 @@ # Set up the input needed for custom regridding. Create a regridder # weight-cache. This object can (and should) be reused for all the packages that # undergo custom regridding at this stage. -from imod.mf6.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid import RegridderWeightsCache regrid_cache = RegridderWeightsCache() @@ -199,8 +199,8 @@ # Regrid the recharge package with a custom regridder. In this case we opt # for the centroid locator regridder. This regridder is similar to using a # "nearest neighbour" lookup. +from imod.common.utilities.regrid import RegridderType from imod.mf6.regrid import RechargeRegridMethod -from imod.mf6.utilities.regrid import RegridderType regridder_types = RechargeRegridMethod(rate=(RegridderType.CENTROIDLOCATOR,)) diff --git a/imod/common/__init__.py b/imod/common/__init__.py new file mode 100644 index 000000000..b8dcda5a6 --- /dev/null +++ b/imod/common/__init__.py @@ -0,0 +1 @@ +from imod.common.utilities.regrid import RegridderType, RegridderWeightsCache diff --git a/imod/mf6/utilities/regrid.py b/imod/common/utilities/regrid.py similarity index 100% rename from imod/mf6/utilities/regrid.py rename to imod/common/utilities/regrid.py diff --git a/imod/mf6/__init__.py b/imod/mf6/__init__.py index 9b3d1027f..8cd53bbfe 100644 --- a/imod/mf6/__init__.py +++ b/imod/mf6/__init__.py @@ -54,7 +54,6 @@ from imod.mf6.ssm import SourceSinkMixing from imod.mf6.sto import SpecificStorage, Storage, StorageCoefficient from imod.mf6.timedis import TimeDiscretization -from imod.mf6.utilities.regrid import RegridderType, RegridderWeightsCache from imod.mf6.uzf import UnsaturatedZoneFlow from imod.mf6.wel import LayeredWell, Well, WellDisStructured, WellDisVertices from imod.mf6.write_context import WriteContext diff --git a/imod/mf6/chd.py b/imod/mf6/chd.py index 96cb30f20..6213e4151 100644 --- a/imod/mf6/chd.py +++ b/imod/mf6/chd.py @@ -4,12 +4,12 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.logging import init_log_decorator from imod.logging.logging_decorators import standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization from imod.mf6.regrid.regrid_schemes import ConstantHeadRegridMethod -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA from imod.schemata import ( AllCoordsValueSchema, diff --git a/imod/mf6/dis.py b/imod/mf6/dis.py index 6d2894bfe..29067139b 100644 --- a/imod/mf6/dis.py +++ b/imod/mf6/dis.py @@ -7,17 +7,17 @@ import imod from imod.common.interfaces.imaskingsettings import IMaskingSettings from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import ( + RegridderWeightsCache, + _regrid_like, + _regrid_package_data, +) from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.disv import VerticesDiscretization from imod.mf6.package import Package from imod.mf6.regrid.regrid_schemes import DiscretizationRegridMethod, RegridMethodType from imod.mf6.utilities.grid import create_smallest_target_grid from imod.mf6.utilities.imod5_converter import convert_ibound_to_idomain -from imod.mf6.utilities.regrid import ( - RegridderWeightsCache, - _regrid_like, - _regrid_package_data, -) from imod.mf6.validation import DisBottomSchema from imod.schemata import ( ActiveCellsConnectedSchema, diff --git a/imod/mf6/drn.py b/imod/mf6/drn.py index 16b8f7884..d8ad9e4fc 100644 --- a/imod/mf6/drn.py +++ b/imod/mf6/drn.py @@ -5,16 +5,16 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import ( + RegridderWeightsCache, + _regrid_package_data, +) from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization from imod.mf6.disv import VerticesDiscretization from imod.mf6.npf import NodePropertyFlow from imod.mf6.regrid.regrid_schemes import DrainageRegridMethod -from imod.mf6.utilities.regrid import ( - RegridderWeightsCache, - _regrid_package_data, -) from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA from imod.prepare.cleanup import cleanup_drn from imod.prepare.topsystem.allocation import ALLOCATION_OPTION, allocate_drn_cells diff --git a/imod/mf6/ghb.py b/imod/mf6/ghb.py index 68867a6f9..ab3a387f3 100644 --- a/imod/mf6/ghb.py +++ b/imod/mf6/ghb.py @@ -5,6 +5,7 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization @@ -14,7 +15,6 @@ GeneralHeadBoundaryRegridMethod, RegridMethodType, ) -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA from imod.prepare.cleanup import cleanup_ghb from imod.prepare.topsystem.allocation import ALLOCATION_OPTION, allocate_ghb_cells diff --git a/imod/mf6/ic.py b/imod/mf6/ic.py index 12f21e883..63a00d317 100644 --- a/imod/mf6/ic.py +++ b/imod/mf6/ic.py @@ -5,12 +5,12 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.logging import init_log_decorator from imod.mf6.package import Package from imod.mf6.regrid.regrid_schemes import ( InitialConditionsRegridMethod, ) -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.mf6.validation import PKG_DIMS_SCHEMA from imod.schemata import ( AllCoordsValueSchema, diff --git a/imod/mf6/model.py b/imod/mf6/model.py index 5809b91b9..dadccace0 100644 --- a/imod/mf6/model.py +++ b/imod/mf6/model.py @@ -20,6 +20,7 @@ import imod from imod.common.interfaces.imodel import IModel from imod.common.statusinfo import NestedStatusInfo, StatusInfo, StatusInfoBase +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_like from imod.logging import LogLevel, logger, standard_log_decorator from imod.mf6.drn import Drainage from imod.mf6.ghb import GeneralHeadBoundary @@ -29,7 +30,6 @@ from imod.mf6.riv import River from imod.mf6.utilities.mask import _mask_all_packages from imod.mf6.utilities.mf6hfb import merge_hfb_packages -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_like from imod.mf6.validation import pkg_errors_to_status_info from imod.mf6.validation_context import ValidationContext from imod.mf6.wel import GridAgnosticWell diff --git a/imod/mf6/model_gwf.py b/imod/mf6/model_gwf.py index 93a7ac8bf..b4c8757fb 100644 --- a/imod/mf6/model_gwf.py +++ b/imod/mf6/model_gwf.py @@ -7,6 +7,7 @@ import cftime import numpy as np +from imod.common.utilities.regrid import RegridderWeightsCache from imod.logging import init_log_decorator from imod.logging.logging_decorators import standard_log_decorator from imod.mf6 import ConstantHead @@ -34,7 +35,6 @@ from imod.mf6.riv import River from imod.mf6.sto import StorageCoefficient from imod.mf6.utilities.chd_concat import concat_layered_chd_packages -from imod.mf6.utilities.regrid import RegridderWeightsCache from imod.mf6.wel import LayeredWell, Well from imod.prepare.topsystem.default_allocation_methods import ( SimulationAllocationOptions, diff --git a/imod/mf6/npf.py b/imod/mf6/npf.py index 4e9ab373d..615e6418c 100644 --- a/imod/mf6/npf.py +++ b/imod/mf6/npf.py @@ -6,16 +6,16 @@ import xarray as xr from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import ( + RegridderWeightsCache, + _regrid_package_data, +) from imod.logging import init_log_decorator from imod.mf6.package import Package from imod.mf6.regrid.regrid_schemes import ( NodePropertyFlowRegridMethod, ) from imod.mf6.utilities.imod5_converter import fill_missing_layers -from imod.mf6.utilities.regrid import ( - RegridderWeightsCache, - _regrid_package_data, -) from imod.mf6.validation import PKG_DIMS_SCHEMA from imod.schemata import ( AllCoordsValueSchema, diff --git a/imod/mf6/package.py b/imod/mf6/package.py index 1fd438a58..0cd62a958 100644 --- a/imod/mf6/package.py +++ b/imod/mf6/package.py @@ -15,6 +15,10 @@ import imod from imod.common.interfaces.ipackage import IPackage +from imod.common.utilities.regrid import ( + RegridderWeightsCache, + _regrid_like, +) from imod.logging import standard_log_decorator from imod.mf6.auxiliary_variables import ( get_variable_names, @@ -26,10 +30,6 @@ ) from imod.mf6.utilities.mask import mask_package from imod.mf6.utilities.package import _is_valid -from imod.mf6.utilities.regrid import ( - RegridderWeightsCache, - _regrid_like, -) from imod.mf6.utilities.schemata import filter_schemata_dict from imod.mf6.validation import validation_pkg_error_message from imod.mf6.write_context import WriteContext diff --git a/imod/mf6/rch.py b/imod/mf6/rch.py index 0d69c1444..de9eb157f 100644 --- a/imod/mf6/rch.py +++ b/imod/mf6/rch.py @@ -5,12 +5,12 @@ import xarray as xr from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.logging import init_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization from imod.mf6.regrid.regrid_schemes import RechargeRegridMethod from imod.mf6.utilities.imod5_converter import convert_unit_rch_rate -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA from imod.msw.utilities.imod5_converter import ( get_cell_area_from_imod5_data, diff --git a/imod/mf6/riv.py b/imod/mf6/riv.py index 9e11540f2..049872e34 100644 --- a/imod/mf6/riv.py +++ b/imod/mf6/riv.py @@ -6,6 +6,9 @@ from imod import logging from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import ( + RegridderWeightsCache, +) from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization @@ -14,9 +17,6 @@ from imod.mf6.npf import NodePropertyFlow from imod.mf6.regrid.regrid_schemes import RiverRegridMethod from imod.mf6.utilities.imod5_converter import regrid_imod5_pkg_data -from imod.mf6.utilities.regrid import ( - RegridderWeightsCache, -) from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA from imod.prepare.cleanup import AlignLevelsMode, align_interface_levels, cleanup_riv from imod.prepare.topsystem.allocation import ALLOCATION_OPTION, allocate_riv_cells diff --git a/imod/mf6/simulation.py b/imod/mf6/simulation.py index 32e53799c..4d843b301 100644 --- a/imod/mf6/simulation.py +++ b/imod/mf6/simulation.py @@ -24,6 +24,7 @@ from imod.common.interfaces.imodel import IModel from imod.common.interfaces.isimulation import ISimulation from imod.common.statusinfo import NestedStatusInfo +from imod.common.utilities.regrid import _regrid_like from imod.logging import LogLevel, logger, standard_log_decorator from imod.mf6.gwfgwf import GWFGWF from imod.mf6.gwfgwt import GWFGWT @@ -42,7 +43,6 @@ from imod.mf6.regrid.regrid_schemes import RegridMethodType from imod.mf6.ssm import SourceSinkMixing from imod.mf6.utilities.mask import _mask_all_models -from imod.mf6.utilities.regrid import _regrid_like from imod.mf6.validation_context import ValidationContext from imod.mf6.write_context import WriteContext from imod.prepare.topsystem.default_allocation_methods import ( diff --git a/imod/mf6/sto.py b/imod/mf6/sto.py index b8bcc9d16..049a098eb 100644 --- a/imod/mf6/sto.py +++ b/imod/mf6/sto.py @@ -5,13 +5,13 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.logging import init_log_decorator from imod.mf6.package import Package from imod.mf6.regrid.regrid_schemes import ( SpecificStorageRegridMethod, StorageCoefficientRegridMethod, ) -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.mf6.validation import PKG_DIMS_SCHEMA from imod.schemata import ( AllCoordsValueSchema, diff --git a/imod/mf6/utilities/imod5_converter.py b/imod/mf6/utilities/imod5_converter.py index a680312f2..a59e9cfbb 100644 --- a/imod/mf6/utilities/imod5_converter.py +++ b/imod/mf6/utilities/imod5_converter.py @@ -4,8 +4,8 @@ import pandas as pd import xarray as xr +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.mf6.package import Package -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_package_data from imod.typing import GridDataDict, Imod5DataDict from imod.typing.grid import full_like from imod.util.dims import drop_layer_dim_cap_data diff --git a/imod/msw/idf_mapping.py b/imod/msw/idf_mapping.py index b3e5e48a3..eefa438c9 100644 --- a/imod/msw/idf_mapping.py +++ b/imod/msw/idf_mapping.py @@ -5,7 +5,7 @@ import xarray as xr from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.mf6.utilities.regrid import RegridderWeightsCache, _regrid_array +from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_array from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.regrid.regrid_schemes import IdfMappingRegridMethod diff --git a/imod/msw/meteo_mapping.py b/imod/msw/meteo_mapping.py index a8bbd1bc2..f0125b33e 100644 --- a/imod/msw/meteo_mapping.py +++ b/imod/msw/meteo_mapping.py @@ -8,7 +8,7 @@ import xarray as xr import imod -from imod.mf6.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.utilities.common import find_in_file_list diff --git a/imod/msw/model.py b/imod/msw/model.py index 958996591..4ca3f521f 100644 --- a/imod/msw/model.py +++ b/imod/msw/model.py @@ -8,9 +8,9 @@ import numpy as np import xarray as xr +from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.dis import StructuredDiscretization from imod.mf6.mf6_wel_adapter import Mf6Wel -from imod.mf6.utilities.regrid import RegridderWeightsCache from imod.msw.copy_files import FileCopier from imod.msw.coupler_mapping import CouplerMapping from imod.msw.grid_data import GridData diff --git a/imod/msw/output_control.py b/imod/msw/output_control.py index 973882f62..023622570 100644 --- a/imod/msw/output_control.py +++ b/imod/msw/output_control.py @@ -4,7 +4,7 @@ import pandas as pd from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.mf6.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.timeutil import to_metaswap_timeformat diff --git a/imod/msw/pkgbase.py b/imod/msw/pkgbase.py index 08b306e6d..b9e142452 100644 --- a/imod/msw/pkgbase.py +++ b/imod/msw/pkgbase.py @@ -7,12 +7,12 @@ import pandas as pd import xarray as xr -from imod.mf6.dis import StructuredDiscretization -from imod.mf6.mf6_wel_adapter import Mf6Wel -from imod.mf6.utilities.regrid import ( +from imod.common.utilities.regrid import ( RegridderWeightsCache, _regrid_like, ) +from imod.mf6.dis import StructuredDiscretization +from imod.mf6.mf6_wel_adapter import Mf6Wel from imod.msw.fixed_format import format_fixed_width from imod.typing import IntArray from imod.typing.grid import GridDataArray, GridDataset diff --git a/imod/tests/test_mf6/test_mf6_hfb.py b/imod/tests/test_mf6/test_mf6_hfb.py index 188dac3b8..df98ac1b7 100644 --- a/imod/tests/test_mf6/test_mf6_hfb.py +++ b/imod/tests/test_mf6/test_mf6_hfb.py @@ -10,6 +10,7 @@ from pytest_cases import parametrize_with_cases from shapely import Polygon, get_coordinates, linestrings +from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6 import ( HorizontalFlowBarrierHydraulicCharacteristic, HorizontalFlowBarrierMultiplier, @@ -29,7 +30,6 @@ from imod.mf6.ims import SolutionPresetSimple from imod.mf6.npf import NodePropertyFlow from imod.mf6.simulation import Modflow6Simulation -from imod.mf6.utilities.regrid import RegridderWeightsCache from imod.prepare.hfb import ( linestring_to_square_zpolygons, linestring_to_trapezoid_zpolygons, diff --git a/imod/tests/test_mf6/test_mf6_rch.py b/imod/tests/test_mf6/test_mf6_rch.py index 2c02a2006..efb1cba5d 100644 --- a/imod/tests/test_mf6/test_mf6_rch.py +++ b/imod/tests/test_mf6/test_mf6_rch.py @@ -9,8 +9,8 @@ import xarray as xr import imod +from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.dis import StructuredDiscretization -from imod.mf6.utilities.regrid import RegridderWeightsCache from imod.mf6.write_context import WriteContext from imod.schemata import ValidationError from imod.typing.grid import is_planar_grid, is_transient_data_grid, nan_like diff --git a/imod/tests/test_mf6/test_mf6_regrid_package.py b/imod/tests/test_mf6/test_mf6_regrid_package.py index 79a190e71..481b099d8 100644 --- a/imod/tests/test_mf6/test_mf6_regrid_package.py +++ b/imod/tests/test_mf6/test_mf6_regrid_package.py @@ -8,8 +8,8 @@ import xugrid as xu import imod +from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.package import Package -from imod.mf6.utilities.regrid import RegridderWeightsCache from imod.tests.fixtures.mf6_small_models_fixture import ( grid_data_structured, grid_data_structured_layered, diff --git a/imod/tests/test_mf6/test_mf6_regrid_scheme.py b/imod/tests/test_mf6/test_mf6_regrid_scheme.py index 72c4aaa86..5a1150529 100644 --- a/imod/tests/test_mf6/test_mf6_regrid_scheme.py +++ b/imod/tests/test_mf6/test_mf6_regrid_scheme.py @@ -4,6 +4,7 @@ from pydantic import ValidationError from pytest_cases import parametrize, parametrize_with_cases +from imod.common.utilities.regrid import RegridderType from imod.mf6.regrid.regrid_schemes import ( ConstantHeadRegridMethod, DiscretizationRegridMethod, @@ -19,7 +20,6 @@ SpecificStorageRegridMethod, StorageCoefficientRegridMethod, ) -from imod.mf6.utilities.regrid import RegridderType ALL_REGRID_METHODS = [ ConstantHeadRegridMethod, diff --git a/imod/tests/test_mf6/test_mf6_regrid_simulation.py b/imod/tests/test_mf6/test_mf6_regrid_simulation.py index 6091a423d..dc36a5784 100644 --- a/imod/tests/test_mf6/test_mf6_regrid_simulation.py +++ b/imod/tests/test_mf6/test_mf6_regrid_simulation.py @@ -4,9 +4,9 @@ import pytest import imod +from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6 import VerticesDiscretization from imod.mf6.regrid.regrid_schemes import ConstantHeadRegridMethod -from imod.mf6.utilities.regrid import RegridderWeightsCache from imod.tests.fixtures.mf6_modelrun_fixture import assert_simulation_can_run from imod.tests.fixtures.mf6_small_models_fixture import ( grid_data_structured, diff --git a/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py b/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py index eff33b966..8de41a6ab 100644 --- a/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py +++ b/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py @@ -2,7 +2,7 @@ import pytest import xarray as xr -from imod.mf6.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid import RegridderWeightsCache from imod.typing.grid import zeros_like diff --git a/imod/tests/test_mf6/test_utilities/test_regrid_utils.py b/imod/tests/test_mf6/test_utilities/test_regrid_utils.py index a559fcfdd..ab5ffd177 100644 --- a/imod/tests/test_mf6/test_utilities/test_regrid_utils.py +++ b/imod/tests/test_mf6/test_utilities/test_regrid_utils.py @@ -4,8 +4,8 @@ import pytest from xugrid import OverlapRegridder +from imod.common.utilities.regrid import RegridderType, RegridderWeightsCache from imod.mf6 import Dispersion -from imod.mf6.utilities.regrid import RegridderType, RegridderWeightsCache def is_equal_regridder(instance_1, instance_2) -> bool: diff --git a/imod/tests/test_msw/test_annual_crop_factors.py b/imod/tests/test_msw/test_annual_crop_factors.py index ae4c34e74..45f2e9e12 100644 --- a/imod/tests/test_msw/test_annual_crop_factors.py +++ b/imod/tests/test_msw/test_annual_crop_factors.py @@ -1,7 +1,7 @@ import numpy as np import xarray as xr -from imod.mf6.utilities.regrid import ( +from imod.common.utilities.regrid import ( RegridderWeightsCache, ) from imod.msw import AnnualCropFactors diff --git a/imod/tests/test_msw/test_grid_data.py b/imod/tests/test_msw/test_grid_data.py index 0ec1b603d..c6526b3ef 100644 --- a/imod/tests/test_msw/test_grid_data.py +++ b/imod/tests/test_msw/test_grid_data.py @@ -11,8 +11,8 @@ from numpy.testing import assert_almost_equal, assert_equal from pytest_cases import case, parametrize_with_cases +from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.dis import StructuredDiscretization -from imod.mf6.utilities.regrid import RegridderWeightsCache from imod.msw import GridData from imod.msw.fixed_format import format_fixed_width from imod.util.spatial import get_total_grid_area diff --git a/imod/tests/test_msw/test_idf_mapping.py b/imod/tests/test_msw/test_idf_mapping.py index a8f519076..40f613cfa 100644 --- a/imod/tests/test_msw/test_idf_mapping.py +++ b/imod/tests/test_msw/test_idf_mapping.py @@ -1,6 +1,6 @@ import numpy as np -from imod.mf6.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw.idf_mapping import IdfMapping from imod.tests.fixtures.msw_regrid_fixture import get_3x3_area, get_5x5_new_grid diff --git a/imod/tests/test_msw/test_infiltration.py b/imod/tests/test_msw/test_infiltration.py index 47990e3a6..8d9921454 100644 --- a/imod/tests/test_msw/test_infiltration.py +++ b/imod/tests/test_msw/test_infiltration.py @@ -11,7 +11,7 @@ from numpy.testing import assert_almost_equal, assert_equal from pytest_cases import case, parametrize_with_cases -from imod.mf6.utilities.regrid import ( +from imod.common.utilities.regrid import ( RegridderWeightsCache, ) from imod.msw import Infiltration diff --git a/imod/tests/test_msw/test_initial_conditions.py b/imod/tests/test_msw/test_initial_conditions.py index e81517388..a821e3ccd 100644 --- a/imod/tests/test_msw/test_initial_conditions.py +++ b/imod/tests/test_msw/test_initial_conditions.py @@ -4,7 +4,7 @@ import numpy as np import pytest -from imod.mf6.utilities.regrid import ( +from imod.common.utilities.regrid import ( RegridderWeightsCache, ) from imod.msw import ( diff --git a/imod/tests/test_msw/test_landuse_options.py b/imod/tests/test_msw/test_landuse_options.py index ec0652d71..9838e4f6a 100644 --- a/imod/tests/test_msw/test_landuse_options.py +++ b/imod/tests/test_msw/test_landuse_options.py @@ -5,7 +5,7 @@ import xarray as xr from numpy.testing import assert_almost_equal, assert_equal -from imod.mf6.utilities.regrid import ( +from imod.common.utilities.regrid import ( RegridderWeightsCache, ) from imod.msw import LanduseOptions diff --git a/imod/tests/test_msw/test_meteo_grid.py b/imod/tests/test_msw/test_meteo_grid.py index 66d7fe95e..79c7cdddf 100644 --- a/imod/tests/test_msw/test_meteo_grid.py +++ b/imod/tests/test_msw/test_meteo_grid.py @@ -9,7 +9,7 @@ import pytest from numpy.testing import assert_equal -from imod.mf6.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw import MeteoGrid, MeteoGridCopy diff --git a/imod/tests/test_msw/test_model.py b/imod/tests/test_msw/test_model.py index 78f4c0f27..671ec2e9c 100644 --- a/imod/tests/test_msw/test_model.py +++ b/imod/tests/test_msw/test_model.py @@ -7,7 +7,7 @@ from pytest_cases import parametrize_with_cases from imod import mf6, msw -from imod.mf6.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw.copy_files import FileCopier from imod.msw.model import DEFAULT_SETTINGS from imod.msw.utilities.parse import read_para_sim diff --git a/imod/tests/test_msw/test_ponding.py b/imod/tests/test_msw/test_ponding.py index 94f0d64d0..e647acc2e 100644 --- a/imod/tests/test_msw/test_ponding.py +++ b/imod/tests/test_msw/test_ponding.py @@ -6,7 +6,7 @@ from numpy import nan from numpy.testing import assert_almost_equal, assert_equal -from imod.mf6.utilities.regrid import ( +from imod.common.utilities.regrid import ( RegridderWeightsCache, ) from imod.msw import Ponding diff --git a/imod/tests/test_msw/test_scaling_factors.py b/imod/tests/test_msw/test_scaling_factors.py index 9b9f8c36f..b009c6be5 100644 --- a/imod/tests/test_msw/test_scaling_factors.py +++ b/imod/tests/test_msw/test_scaling_factors.py @@ -6,7 +6,7 @@ from numpy import nan from numpy.testing import assert_almost_equal, assert_equal -from imod.mf6.utilities.regrid import ( +from imod.common.utilities.regrid import ( RegridderWeightsCache, ) from imod.msw import ScalingFactors From 6e149c78e161341e1967dac5cd7bca5b8d2dfaf7 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 13:51:13 +0100 Subject: [PATCH 02/17] Move regrid_method_type to common utilities --- imod/common/interfaces/iregridpackage.py | 2 +- imod/common/utilities/regrid.py | 3 +- imod/common/utilities/regrid_method_type.py | 32 +++++++++++++++++++++ imod/mf6/dis.py | 3 +- imod/mf6/ghb.py | 2 +- imod/mf6/model_gwf.py | 2 +- imod/mf6/package.py | 2 +- imod/mf6/regrid/__init__.py | 2 +- imod/mf6/regrid/regrid_schemes.py | 6 ++-- imod/mf6/simulation.py | 2 +- imod/mf6/ssm.py | 2 +- imod/mf6/utilities/imod5_converter.py | 2 +- imod/msw/idf_mapping.py | 2 +- imod/msw/initial_conditions.py | 2 +- imod/msw/landuse.py | 3 +- imod/msw/meteo_grid.py | 2 +- imod/msw/meteo_mapping.py | 2 +- imod/msw/output_control.py | 2 +- imod/msw/pkgbase.py | 2 +- imod/msw/regrid/regrid_schemes.py | 6 ++-- imod/msw/vegetation.py | 2 +- imod/util/regrid_method_type.py | 28 ------------------ 22 files changed, 59 insertions(+), 52 deletions(-) create mode 100644 imod/common/utilities/regrid_method_type.py diff --git a/imod/common/interfaces/iregridpackage.py b/imod/common/interfaces/iregridpackage.py index 9c6089f72..d54204eff 100644 --- a/imod/common/interfaces/iregridpackage.py +++ b/imod/common/interfaces/iregridpackage.py @@ -2,7 +2,7 @@ from typing import Optional from imod.common.interfaces.ipackage import IPackage -from imod.util.regrid_method_type import RegridMethodType +from imod.common.utilities.regrid_method_type import RegridMethodType class IRegridPackage(IPackage, abc.ABC): diff --git a/imod/common/utilities/regrid.py b/imod/common/utilities/regrid.py index 6c7476281..aceff868c 100644 --- a/imod/common/utilities/regrid.py +++ b/imod/common/utilities/regrid.py @@ -16,6 +16,7 @@ from imod.common.interfaces.iregridpackage import IRegridPackage from imod.common.interfaces.isimulation import ISimulation from imod.common.statusinfo import NestedStatusInfo +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.mf6.auxiliary_variables import ( expand_transient_auxiliary_variables, remove_expanded_auxiliary_variables_from_dataset, @@ -31,9 +32,7 @@ ones_like, ) from imod.util.regrid_method_type import ( - EmptyRegridMethod, RegridderType, - RegridMethodType, ) HashRegridderMapping = Tuple[int, int, BaseRegridder] diff --git a/imod/common/utilities/regrid_method_type.py b/imod/common/utilities/regrid_method_type.py new file mode 100644 index 000000000..25a61f79b --- /dev/null +++ b/imod/common/utilities/regrid_method_type.py @@ -0,0 +1,32 @@ +from typing import ClassVar, Protocol, Tuple, TypeAlias + +from pydantic import ConfigDict +from pydantic.dataclasses import dataclass + +from imod.util.regrid_method_type import RegridderType + + +class RegridMethodType(Protocol): + # Work around that type annotation is a bit hard on dataclasses, as they + # don't expose a class interface. + # Adapted from: https://stackoverflow.com/a/55240861 + # "As already noted in comments, checking for this attribute is currently the + # most reliable way to ascertain that something is a dataclass" + # See also: + # https://github.com/python/mypy/issues/6568#issuecomment-1324196557 + + __dataclass_fields__: ClassVar[dict] + + def asdict(self) -> dict: + return vars(self) + + +_CONFIG = ConfigDict(extra="forbid") + + +@dataclass(config=_CONFIG) +class EmptyRegridMethod(RegridMethodType): + pass + + +_RegridVarType: TypeAlias = Tuple[RegridderType, str] | Tuple[RegridderType] diff --git a/imod/mf6/dis.py b/imod/mf6/dis.py index 29067139b..849ff1ecf 100644 --- a/imod/mf6/dis.py +++ b/imod/mf6/dis.py @@ -12,10 +12,11 @@ _regrid_like, _regrid_package_data, ) +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.disv import VerticesDiscretization from imod.mf6.package import Package -from imod.mf6.regrid.regrid_schemes import DiscretizationRegridMethod, RegridMethodType +from imod.mf6.regrid.regrid_schemes import DiscretizationRegridMethod from imod.mf6.utilities.grid import create_smallest_target_grid from imod.mf6.utilities.imod5_converter import convert_ibound_to_idomain from imod.mf6.validation import DisBottomSchema diff --git a/imod/mf6/ghb.py b/imod/mf6/ghb.py index ab3a387f3..7e88d1661 100644 --- a/imod/mf6/ghb.py +++ b/imod/mf6/ghb.py @@ -6,6 +6,7 @@ from imod.common.interfaces.iregridpackage import IRegridPackage from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization @@ -13,7 +14,6 @@ from imod.mf6.npf import NodePropertyFlow from imod.mf6.regrid.regrid_schemes import ( GeneralHeadBoundaryRegridMethod, - RegridMethodType, ) from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA from imod.prepare.cleanup import cleanup_ghb diff --git a/imod/mf6/model_gwf.py b/imod/mf6/model_gwf.py index b4c8757fb..0e5aa990a 100644 --- a/imod/mf6/model_gwf.py +++ b/imod/mf6/model_gwf.py @@ -8,6 +8,7 @@ import numpy as np from imod.common.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.logging import init_log_decorator from imod.logging.logging_decorators import standard_log_decorator from imod.mf6 import ConstantHead @@ -28,7 +29,6 @@ InitialConditionsRegridMethod, NodePropertyFlowRegridMethod, RechargeRegridMethod, - RegridMethodType, RiverRegridMethod, StorageCoefficientRegridMethod, ) diff --git a/imod/mf6/package.py b/imod/mf6/package.py index 0cd62a958..b09792d2f 100644 --- a/imod/mf6/package.py +++ b/imod/mf6/package.py @@ -19,6 +19,7 @@ RegridderWeightsCache, _regrid_like, ) +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.logging import standard_log_decorator from imod.mf6.auxiliary_variables import ( get_variable_names, @@ -40,7 +41,6 @@ ValidationError, ) from imod.typing import GridDataArray -from imod.util.regrid_method_type import EmptyRegridMethod, RegridMethodType class Package(PackageBase, IPackage, abc.ABC): diff --git a/imod/mf6/regrid/__init__.py b/imod/mf6/regrid/__init__.py index 2e5382a62..15c668f31 100644 --- a/imod/mf6/regrid/__init__.py +++ b/imod/mf6/regrid/__init__.py @@ -1,3 +1,4 @@ +from imod.common.utilities.regrid_method_type import EmptyRegridMethod from imod.mf6.regrid.regrid_schemes import ( ConstantHeadRegridMethod, DiscretizationRegridMethod, @@ -13,4 +14,3 @@ SpecificStorageRegridMethod, StorageCoefficientRegridMethod, ) -from imod.util.regrid_method_type import EmptyRegridMethod diff --git a/imod/mf6/regrid/regrid_schemes.py b/imod/mf6/regrid/regrid_schemes.py index 213856a4c..94c6ae202 100644 --- a/imod/mf6/regrid/regrid_schemes.py +++ b/imod/mf6/regrid/regrid_schemes.py @@ -1,11 +1,13 @@ from pydantic.dataclasses import dataclass -from imod.util.regrid_method_type import ( +from imod.common.utilities.regrid_method_type import ( _CONFIG, - RegridderType, RegridMethodType, _RegridVarType, ) +from imod.util.regrid_method_type import ( + RegridderType, +) @dataclass(config=_CONFIG) diff --git a/imod/mf6/simulation.py b/imod/mf6/simulation.py index 4d843b301..0689e99f2 100644 --- a/imod/mf6/simulation.py +++ b/imod/mf6/simulation.py @@ -25,6 +25,7 @@ from imod.common.interfaces.isimulation import ISimulation from imod.common.statusinfo import NestedStatusInfo from imod.common.utilities.regrid import _regrid_like +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.logging import LogLevel, logger, standard_log_decorator from imod.mf6.gwfgwf import GWFGWF from imod.mf6.gwfgwt import GWFGWT @@ -40,7 +41,6 @@ from imod.mf6.multimodel.modelsplitter import create_partition_info, slice_model from imod.mf6.out import open_cbc, open_conc, open_hds from imod.mf6.package import Package -from imod.mf6.regrid.regrid_schemes import RegridMethodType from imod.mf6.ssm import SourceSinkMixing from imod.mf6.utilities.mask import _mask_all_models from imod.mf6.validation_context import ValidationContext diff --git a/imod/mf6/ssm.py b/imod/mf6/ssm.py index 376a56978..9035760eb 100644 --- a/imod/mf6/ssm.py +++ b/imod/mf6/ssm.py @@ -1,11 +1,11 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.logging import init_log_decorator, logger from imod.mf6 import GroundwaterFlowModel from imod.mf6.boundary_condition import BoundaryCondition from imod.schemata import DTypeSchema -from imod.util.regrid_method_type import EmptyRegridMethod, RegridMethodType def with_index_dim(array_like): diff --git a/imod/mf6/utilities/imod5_converter.py b/imod/mf6/utilities/imod5_converter.py index a59e9cfbb..d34e62d6f 100644 --- a/imod/mf6/utilities/imod5_converter.py +++ b/imod/mf6/utilities/imod5_converter.py @@ -5,11 +5,11 @@ import xarray as xr from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.mf6.package import Package from imod.typing import GridDataDict, Imod5DataDict from imod.typing.grid import full_like from imod.util.dims import drop_layer_dim_cap_data -from imod.util.regrid_method_type import RegridMethodType def convert_ibound_to_idomain( diff --git a/imod/msw/idf_mapping.py b/imod/msw/idf_mapping.py index eefa438c9..7fb708d5b 100644 --- a/imod/msw/idf_mapping.py +++ b/imod/msw/idf_mapping.py @@ -6,11 +6,11 @@ from imod.common.interfaces.iregridpackage import IRegridPackage from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_array +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.regrid.regrid_schemes import IdfMappingRegridMethod from imod.typing import GridDataArray -from imod.util.regrid_method_type import RegridMethodType from imod.util.spatial import spatial_reference diff --git a/imod/msw/initial_conditions.py b/imod/msw/initial_conditions.py index 6b99062d5..a659252c1 100644 --- a/imod/msw/initial_conditions.py +++ b/imod/msw/initial_conditions.py @@ -3,9 +3,9 @@ from typing import Any, TextIO from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage -from imod.util.regrid_method_type import EmptyRegridMethod, RegridMethodType class InitialConditionsEquilibrium(MetaSwapPackage, IRegridPackage): diff --git a/imod/msw/landuse.py b/imod/msw/landuse.py index 26d708167..7aac43d81 100644 --- a/imod/msw/landuse.py +++ b/imod/msw/landuse.py @@ -1,10 +1,9 @@ from typing import Any, TextIO from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage -from imod.msw.regrid.regrid_schemes import RegridMethodType -from imod.util.regrid_method_type import EmptyRegridMethod class LanduseOptions(MetaSwapPackage, IRegridPackage): diff --git a/imod/msw/meteo_grid.py b/imod/msw/meteo_grid.py index ba60eada3..7111d7533 100644 --- a/imod/msw/meteo_grid.py +++ b/imod/msw/meteo_grid.py @@ -9,13 +9,13 @@ import imod from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.msw.pkgbase import MetaSwapPackage from imod.msw.regrid.regrid_schemes import MeteoGridRegridMethod from imod.msw.timeutil import to_metaswap_timeformat from imod.msw.utilities.common import find_in_file_list from imod.msw.utilities.mask import MaskValues from imod.typing import Imod5DataDict -from imod.util.regrid_method_type import EmptyRegridMethod, RegridMethodType class MeteoGrid(MetaSwapPackage, IRegridPackage): diff --git a/imod/msw/meteo_mapping.py b/imod/msw/meteo_mapping.py index f0125b33e..3b7db1f97 100644 --- a/imod/msw/meteo_mapping.py +++ b/imod/msw/meteo_mapping.py @@ -9,12 +9,12 @@ import imod from imod.common.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.utilities.common import find_in_file_list from imod.prepare import common from imod.typing import GridDataArray, Imod5DataDict, IntArray -from imod.util.regrid_method_type import RegridMethodType def _is_parsable_and_existing_path(potential_path: str, mete_grid_path: Path) -> bool: diff --git a/imod/msw/output_control.py b/imod/msw/output_control.py index 023622570..cbfc58aa6 100644 --- a/imod/msw/output_control.py +++ b/imod/msw/output_control.py @@ -5,11 +5,11 @@ from imod.common.interfaces.iregridpackage import IRegridPackage from imod.common.utilities.regrid import RegridderWeightsCache +from imod.common.utilities.regrid_method_type import RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.timeutil import to_metaswap_timeformat from imod.typing import GridDataArray -from imod.util.regrid_method_type import RegridMethodType # I did not use long variable names here (e.g. "precipitation"), as MetaSWAP diff --git a/imod/msw/pkgbase.py b/imod/msw/pkgbase.py index b9e142452..e0b0033f1 100644 --- a/imod/msw/pkgbase.py +++ b/imod/msw/pkgbase.py @@ -11,12 +11,12 @@ RegridderWeightsCache, _regrid_like, ) +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.mf6.dis import StructuredDiscretization from imod.mf6.mf6_wel_adapter import Mf6Wel from imod.msw.fixed_format import format_fixed_width from imod.typing import IntArray from imod.typing.grid import GridDataArray, GridDataset -from imod.util.regrid_method_type import EmptyRegridMethod, RegridMethodType DataDictType: TypeAlias = dict[str, IntArray | int | str] diff --git a/imod/msw/regrid/regrid_schemes.py b/imod/msw/regrid/regrid_schemes.py index 7bb27ae16..1bca88732 100644 --- a/imod/msw/regrid/regrid_schemes.py +++ b/imod/msw/regrid/regrid_schemes.py @@ -1,11 +1,13 @@ from pydantic.dataclasses import dataclass -from imod.util.regrid_method_type import ( +from imod.common.utilities.regrid_method_type import ( _CONFIG, - RegridderType, RegridMethodType, _RegridVarType, ) +from imod.util.regrid_method_type import ( + RegridderType, +) @dataclass(config=_CONFIG) diff --git a/imod/msw/vegetation.py b/imod/msw/vegetation.py index ac7429257..b1020b150 100644 --- a/imod/msw/vegetation.py +++ b/imod/msw/vegetation.py @@ -4,9 +4,9 @@ import xarray as xr from imod.common.interfaces.iregridpackage import IRegridPackage +from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage -from imod.util.regrid_method_type import EmptyRegridMethod, RegridMethodType class AnnualCropFactors(MetaSwapPackage, IRegridPackage): diff --git a/imod/util/regrid_method_type.py b/imod/util/regrid_method_type.py index b21984df5..360a06b7a 100644 --- a/imod/util/regrid_method_type.py +++ b/imod/util/regrid_method_type.py @@ -1,11 +1,6 @@ from enum import Enum -from typing import ClassVar, Protocol, Tuple, TypeAlias import xugrid as xu -from pydantic import ConfigDict -from pydantic.dataclasses import dataclass - -_CONFIG = ConfigDict(extra="forbid") class RegridderType(Enum): @@ -20,26 +15,3 @@ class RegridderType(Enum): BARYCENTRIC = xu.BarycentricInterpolator OVERLAP = xu.OverlapRegridder RELATIVEOVERLAP = xu.RelativeOverlapRegridder - - -_RegridVarType: TypeAlias = Tuple[RegridderType, str] | Tuple[RegridderType] - - -class RegridMethodType(Protocol): - # Work around that type annotation is a bit hard on dataclasses, as they - # don't expose a class interface. - # Adapted from: https://stackoverflow.com/a/55240861 - # "As already noted in comments, checking for this attribute is currently the - # most reliable way to ascertain that something is a dataclass" - # See also: - # https://github.com/python/mypy/issues/6568#issuecomment-1324196557 - - __dataclass_fields__: ClassVar[dict] - - def asdict(self) -> dict: - return vars(self) - - -@dataclass(config=_CONFIG) -class EmptyRegridMethod(RegridMethodType): - pass From e74746127212eab25438ac6b4f1f7aac99efeade Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 13:53:09 +0100 Subject: [PATCH 03/17] Rename to ``imod.util.regrid`` --- imod/common/utilities/regrid.py | 2 +- imod/common/utilities/regrid_method_type.py | 2 +- imod/mf6/regrid/regrid_schemes.py | 2 +- imod/msw/model.py | 2 +- imod/msw/regrid/regrid_schemes.py | 2 +- imod/tests/test_mf6/test_mf6_npf.py | 2 +- imod/tests/test_mf6/test_mf6_regrid_simulation.py | 2 +- imod/util/{regrid_method_type.py => regrid.py} | 0 8 files changed, 7 insertions(+), 7 deletions(-) rename imod/util/{regrid_method_type.py => regrid.py} (100%) diff --git a/imod/common/utilities/regrid.py b/imod/common/utilities/regrid.py index aceff868c..2cfbd411d 100644 --- a/imod/common/utilities/regrid.py +++ b/imod/common/utilities/regrid.py @@ -31,7 +31,7 @@ is_unstructured, ones_like, ) -from imod.util.regrid_method_type import ( +from imod.util.regrid import ( RegridderType, ) diff --git a/imod/common/utilities/regrid_method_type.py b/imod/common/utilities/regrid_method_type.py index 25a61f79b..283b48c56 100644 --- a/imod/common/utilities/regrid_method_type.py +++ b/imod/common/utilities/regrid_method_type.py @@ -3,7 +3,7 @@ from pydantic import ConfigDict from pydantic.dataclasses import dataclass -from imod.util.regrid_method_type import RegridderType +from imod.util.regrid import RegridderType class RegridMethodType(Protocol): diff --git a/imod/mf6/regrid/regrid_schemes.py b/imod/mf6/regrid/regrid_schemes.py index 94c6ae202..12a0f36f9 100644 --- a/imod/mf6/regrid/regrid_schemes.py +++ b/imod/mf6/regrid/regrid_schemes.py @@ -5,7 +5,7 @@ RegridMethodType, _RegridVarType, ) -from imod.util.regrid_method_type import ( +from imod.util.regrid import ( RegridderType, ) diff --git a/imod/msw/model.py b/imod/msw/model.py index 4ca3f521f..adb0d4ecb 100644 --- a/imod/msw/model.py +++ b/imod/msw/model.py @@ -37,7 +37,7 @@ from imod.msw.vegetation import AnnualCropFactors from imod.typing import Imod5DataDict from imod.util.dims import drop_layer_dim_cap_data -from imod.util.regrid_method_type import RegridderType +from imod.util.regrid import RegridderType REQUIRED_PACKAGES = ( GridData, diff --git a/imod/msw/regrid/regrid_schemes.py b/imod/msw/regrid/regrid_schemes.py index 1bca88732..311177b50 100644 --- a/imod/msw/regrid/regrid_schemes.py +++ b/imod/msw/regrid/regrid_schemes.py @@ -5,7 +5,7 @@ RegridMethodType, _RegridVarType, ) -from imod.util.regrid_method_type import ( +from imod.util.regrid import ( RegridderType, ) diff --git a/imod/tests/test_mf6/test_mf6_npf.py b/imod/tests/test_mf6/test_mf6_npf.py index 5de755872..0d4426d8a 100644 --- a/imod/tests/test_mf6/test_mf6_npf.py +++ b/imod/tests/test_mf6/test_mf6_npf.py @@ -9,7 +9,7 @@ import imod from imod.schemata import ValidationError -from imod.util.regrid_method_type import RegridderType +from imod.util.regrid import RegridderType def test_render(): diff --git a/imod/tests/test_mf6/test_mf6_regrid_simulation.py b/imod/tests/test_mf6/test_mf6_regrid_simulation.py index dc36a5784..94aabd36a 100644 --- a/imod/tests/test_mf6/test_mf6_regrid_simulation.py +++ b/imod/tests/test_mf6/test_mf6_regrid_simulation.py @@ -12,7 +12,7 @@ grid_data_structured, grid_data_unstructured, ) -from imod.util.regrid_method_type import RegridderType +from imod.util.regrid import RegridderType def test_regrid_structured_simulation_to_structured_simulation( diff --git a/imod/util/regrid_method_type.py b/imod/util/regrid.py similarity index 100% rename from imod/util/regrid_method_type.py rename to imod/util/regrid.py From 9888c4ed9766bb115b77667efcd62a96427f9564 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 14:01:05 +0100 Subject: [PATCH 04/17] Move RegridderWeightsCache to imod.util as it is part of the public API --- .../mf6/different_ways_to_regrid_models.py | 3 +- examples/user-guide/08-regridding.py | 2 +- imod/common/__init__.py | 3 +- imod/common/utilities/regrid.py | 97 +----------------- imod/mf6/chd.py | 3 +- imod/mf6/dis.py | 2 +- imod/mf6/drn.py | 2 +- imod/mf6/ghb.py | 3 +- imod/mf6/ic.py | 3 +- imod/mf6/model.py | 3 +- imod/mf6/model_gwf.py | 2 +- imod/mf6/npf.py | 2 +- imod/mf6/package.py | 2 +- imod/mf6/rch.py | 3 +- imod/mf6/riv.py | 6 +- imod/mf6/sto.py | 3 +- imod/mf6/utilities/imod5_converter.py | 3 +- imod/msw/idf_mapping.py | 3 +- imod/msw/meteo_mapping.py | 2 +- imod/msw/model.py | 3 +- imod/msw/output_control.py | 2 +- imod/msw/pkgbase.py | 2 +- imod/tests/test_mf6/test_mf6_hfb.py | 2 +- imod/tests/test_mf6/test_mf6_rch.py | 2 +- .../tests/test_mf6/test_mf6_regrid_package.py | 2 +- .../test_mf6/test_mf6_regrid_simulation.py | 3 +- .../test_mf6_unsupported_grid_operations.py | 2 +- .../test_utilities/test_regrid_utils.py | 3 +- .../test_msw/test_annual_crop_factors.py | 4 +- imod/tests/test_msw/test_grid_data.py | 2 +- imod/tests/test_msw/test_idf_mapping.py | 2 +- imod/tests/test_msw/test_infiltration.py | 6 +- .../tests/test_msw/test_initial_conditions.py | 6 +- imod/tests/test_msw/test_landuse_options.py | 4 +- imod/tests/test_msw/test_meteo_grid.py | 2 +- imod/tests/test_msw/test_model.py | 2 +- imod/tests/test_msw/test_ponding.py | 6 +- imod/tests/test_msw/test_scaling_factors.py | 6 +- imod/util/regrid.py | 99 +++++++++++++++++++ 39 files changed, 161 insertions(+), 146 deletions(-) diff --git a/examples/mf6/different_ways_to_regrid_models.py b/examples/mf6/different_ways_to_regrid_models.py index d2c83e799..d5c618a58 100644 --- a/examples/mf6/different_ways_to_regrid_models.py +++ b/examples/mf6/different_ways_to_regrid_models.py @@ -25,8 +25,9 @@ import xarray as xr from example_models import create_twri_simulation -from imod.common.utilities.regrid import RegridderType, RegridderWeightsCache +from imod.common.utilities.regrid import RegridderType from imod.mf6.regrid import NodePropertyFlowRegridMethod +from imod.util.regrid import RegridderWeightsCache # %% # Now we create the twri simulation itself. It yields a simulation of a flow problem, with a grid of 3 layers and 15 cells in both x and y directions. diff --git a/examples/user-guide/08-regridding.py b/examples/user-guide/08-regridding.py index 370552cc3..39913e21d 100644 --- a/examples/user-guide/08-regridding.py +++ b/examples/user-guide/08-regridding.py @@ -189,7 +189,7 @@ # Set up the input needed for custom regridding. Create a regridder # weight-cache. This object can (and should) be reused for all the packages that # undergo custom regridding at this stage. -from imod.common.utilities.regrid import RegridderWeightsCache +from imod.util.regrid import RegridderWeightsCache regrid_cache = RegridderWeightsCache() diff --git a/imod/common/__init__.py b/imod/common/__init__.py index b8dcda5a6..8af64b3e9 100644 --- a/imod/common/__init__.py +++ b/imod/common/__init__.py @@ -1 +1,2 @@ -from imod.common.utilities.regrid import RegridderType, RegridderWeightsCache +from imod.common.utilities.regrid import RegridderType +from imod.util.regrid import RegridderWeightsCache diff --git a/imod/common/utilities/regrid.py b/imod/common/utilities/regrid.py index 2cfbd411d..b669e4daa 100644 --- a/imod/common/utilities/regrid.py +++ b/imod/common/utilities/regrid.py @@ -1,8 +1,7 @@ -import abc import copy from collections import defaultdict from dataclasses import asdict -from typing import Any, Dict, Optional, Tuple, Union +from typing import Any, Optional, Union import xarray as xr from fastcore.dispatch import typedispatch @@ -27,106 +26,14 @@ from imod.typing.grid import ( GridDataArray, GridDataset, - get_grid_geometry_hash, is_unstructured, ones_like, ) from imod.util.regrid import ( RegridderType, + RegridderWeightsCache, ) -HashRegridderMapping = Tuple[int, int, BaseRegridder] - - -class RegridderWeightsCache: - """ - This class stores any number of regridders that can regrid a single source - grid to a single target grid. By storing the regridders, we make sure the - regridders can be re-used for different arrays on the same grid. Regridders - are stored based on their type (`see these - docs`_) - and planar coordinates (x, y). This is important because computing the - regridding weights is a costly affair. - """ - - def __init__( - self, - max_cache_size: int = 6, - ) -> None: - self.regridder_instances: dict[ - tuple[type[BaseRegridder], Optional[str]], BaseRegridder - ] = {} - self.weights_cache: Dict[HashRegridderMapping, GridDataArray] = {} - self.max_cache_size = max_cache_size - - def __get_regridder_class( - self, regridder_type: RegridderType | BaseRegridder - ) -> type[BaseRegridder]: - if isinstance(regridder_type, abc.ABCMeta): - if not issubclass(regridder_type, BaseRegridder): - raise ValueError( - "only derived types of BaseRegridder can be instantiated" - ) - return regridder_type - elif isinstance(regridder_type, RegridderType): - return regridder_type.value - - raise ValueError("invalid type for regridder") - - def get_regridder( - self, - source_grid: GridDataArray, - target_grid: GridDataArray, - regridder_type: Union[RegridderType, BaseRegridder], - method: Optional[str] = None, - ) -> BaseRegridder: - """ - returns a regridder of the specified type and with the specified method. - The desired type can be passed through the argument "regridder_type" as - an enumerator or as a class. The following two are equivalent: - instancesCollection.get_regridder(RegridderType.OVERLAP, "mean") - instancesCollection.get_regridder(xu.OverlapRegridder, "mean") - - - Parameters - ---------- - regridder_type: RegridderType or regridder class - indicates the desired regridder type - method: str or None - indicates the method the regridder should apply - - Returns - ------- - a regridder of the specified characteristics - """ - regridder_class = self.__get_regridder_class(regridder_type) - - if "layer" not in source_grid.coords and "layer" in target_grid.coords: - target_grid = target_grid.drop_vars("layer") - - source_hash = get_grid_geometry_hash(source_grid) - target_hash = get_grid_geometry_hash(target_grid) - key = (source_hash, target_hash, regridder_class) - if key not in self.weights_cache.keys(): - if len(self.weights_cache) >= self.max_cache_size: - self.remove_first_regridder() - kwargs = {"source": source_grid, "target": target_grid} - if method is not None: - kwargs["method"] = method - regridder = regridder_class(**kwargs) - self.weights_cache[key] = regridder.weights - else: - kwargs = {"weights": self.weights_cache[key], "target": target_grid} - if method is not None: - kwargs["method"] = method - regridder = regridder_class.from_weights(**kwargs) - - return regridder - - def remove_first_regridder(self): - keys = list(self.weights_cache.keys()) - self.weights_cache.pop(keys[0]) - def handle_extra_coords(coordname: str, target_grid: GridDataArray, variable_data: Any): """ diff --git a/imod/mf6/chd.py b/imod/mf6/chd.py index 6213e4151..9583e50d2 100644 --- a/imod/mf6/chd.py +++ b/imod/mf6/chd.py @@ -4,7 +4,7 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid import _regrid_package_data from imod.logging import init_log_decorator from imod.logging.logging_decorators import standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition @@ -23,6 +23,7 @@ OtherCoordsSchema, ) from imod.typing import GridDataArray +from imod.util.regrid import RegridderWeightsCache class ConstantHead(BoundaryCondition, IRegridPackage): diff --git a/imod/mf6/dis.py b/imod/mf6/dis.py index 849ff1ecf..c838daadd 100644 --- a/imod/mf6/dis.py +++ b/imod/mf6/dis.py @@ -8,7 +8,6 @@ from imod.common.interfaces.imaskingsettings import IMaskingSettings from imod.common.interfaces.iregridpackage import IRegridPackage from imod.common.utilities.regrid import ( - RegridderWeightsCache, _regrid_like, _regrid_package_data, ) @@ -32,6 +31,7 @@ ValidationError, ) from imod.typing.grid import GridDataArray, is_unstructured +from imod.util.regrid import RegridderWeightsCache class StructuredDiscretization(Package, IRegridPackage, IMaskingSettings): diff --git a/imod/mf6/drn.py b/imod/mf6/drn.py index d8ad9e4fc..2afcd53be 100644 --- a/imod/mf6/drn.py +++ b/imod/mf6/drn.py @@ -6,7 +6,6 @@ from imod.common.interfaces.iregridpackage import IRegridPackage from imod.common.utilities.regrid import ( - RegridderWeightsCache, _regrid_package_data, ) from imod.logging import init_log_decorator, standard_log_decorator @@ -37,6 +36,7 @@ from imod.typing import GridDataArray from imod.typing.grid import enforce_dim_order, has_negative_layer, is_planar_grid from imod.util.expand_repetitions import expand_repetitions +from imod.util.regrid import RegridderWeightsCache class Drainage(BoundaryCondition, IRegridPackage): diff --git a/imod/mf6/ghb.py b/imod/mf6/ghb.py index 7e88d1661..f6fd6114a 100644 --- a/imod/mf6/ghb.py +++ b/imod/mf6/ghb.py @@ -5,7 +5,7 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid import _regrid_package_data from imod.common.utilities.regrid_method_type import RegridMethodType from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition @@ -37,6 +37,7 @@ from imod.typing import GridDataArray from imod.typing.grid import enforce_dim_order, has_negative_layer, is_planar_grid from imod.util.expand_repetitions import expand_repetitions +from imod.util.regrid import RegridderWeightsCache class GeneralHeadBoundary(BoundaryCondition, IRegridPackage): diff --git a/imod/mf6/ic.py b/imod/mf6/ic.py index 63a00d317..47320ba5a 100644 --- a/imod/mf6/ic.py +++ b/imod/mf6/ic.py @@ -5,7 +5,7 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid import _regrid_package_data from imod.logging import init_log_decorator from imod.mf6.package import Package from imod.mf6.regrid.regrid_schemes import ( @@ -19,6 +19,7 @@ IndexesSchema, ) from imod.typing import GridDataArray +from imod.util.regrid import RegridderWeightsCache class InitialConditions(Package, IRegridPackage): diff --git a/imod/mf6/model.py b/imod/mf6/model.py index dadccace0..173fcefa7 100644 --- a/imod/mf6/model.py +++ b/imod/mf6/model.py @@ -20,7 +20,7 @@ import imod from imod.common.interfaces.imodel import IModel from imod.common.statusinfo import NestedStatusInfo, StatusInfo, StatusInfoBase -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_like +from imod.common.utilities.regrid import _regrid_like from imod.logging import LogLevel, logger, standard_log_decorator from imod.mf6.drn import Drainage from imod.mf6.ghb import GeneralHeadBoundary @@ -36,6 +36,7 @@ from imod.mf6.write_context import WriteContext from imod.schemata import ValidationError from imod.typing import GridDataArray +from imod.util.regrid import RegridderWeightsCache HFB_PKGNAME = "hfb_merged" SUGGESTION_TEXT = ( diff --git a/imod/mf6/model_gwf.py b/imod/mf6/model_gwf.py index 0e5aa990a..5a3d156e3 100644 --- a/imod/mf6/model_gwf.py +++ b/imod/mf6/model_gwf.py @@ -7,7 +7,6 @@ import cftime import numpy as np -from imod.common.utilities.regrid import RegridderWeightsCache from imod.common.utilities.regrid_method_type import RegridMethodType from imod.logging import init_log_decorator from imod.logging.logging_decorators import standard_log_decorator @@ -42,6 +41,7 @@ ) from imod.typing import GridDataArray, StressPeriodTimesType from imod.typing.grid import zeros_like +from imod.util.regrid import RegridderWeightsCache class GroundwaterFlowModel(Modflow6Model): diff --git a/imod/mf6/npf.py b/imod/mf6/npf.py index 615e6418c..e590675a1 100644 --- a/imod/mf6/npf.py +++ b/imod/mf6/npf.py @@ -7,7 +7,6 @@ from imod.common.interfaces.iregridpackage import IRegridPackage from imod.common.utilities.regrid import ( - RegridderWeightsCache, _regrid_package_data, ) from imod.logging import init_log_decorator @@ -28,6 +27,7 @@ ) from imod.typing import GridDataArray from imod.typing.grid import zeros_like +from imod.util.regrid import RegridderWeightsCache def _dataarray_to_bool(griddataarray: GridDataArray) -> bool: diff --git a/imod/mf6/package.py b/imod/mf6/package.py index b09792d2f..fa783f51a 100644 --- a/imod/mf6/package.py +++ b/imod/mf6/package.py @@ -16,7 +16,6 @@ import imod from imod.common.interfaces.ipackage import IPackage from imod.common.utilities.regrid import ( - RegridderWeightsCache, _regrid_like, ) from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType @@ -41,6 +40,7 @@ ValidationError, ) from imod.typing import GridDataArray +from imod.util.regrid import RegridderWeightsCache class Package(PackageBase, IPackage, abc.ABC): diff --git a/imod/mf6/rch.py b/imod/mf6/rch.py index de9eb157f..38aa9df62 100644 --- a/imod/mf6/rch.py +++ b/imod/mf6/rch.py @@ -5,7 +5,7 @@ import xarray as xr from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid import _regrid_package_data from imod.logging import init_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization @@ -35,6 +35,7 @@ is_planar_grid, ) from imod.util.dims import drop_layer_dim_cap_data +from imod.util.regrid import RegridderWeightsCache class Recharge(BoundaryCondition, IRegridPackage): diff --git a/imod/mf6/riv.py b/imod/mf6/riv.py index 049872e34..b1f51783b 100644 --- a/imod/mf6/riv.py +++ b/imod/mf6/riv.py @@ -6,9 +6,6 @@ from imod import logging from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import ( - RegridderWeightsCache, -) from imod.logging import init_log_decorator, standard_log_decorator from imod.mf6.boundary_condition import BoundaryCondition from imod.mf6.dis import StructuredDiscretization @@ -46,6 +43,9 @@ is_planar_grid, ) from imod.util.expand_repetitions import expand_repetitions +from imod.util.regrid import ( + RegridderWeightsCache, +) def set_repeat_stress_if_available( diff --git a/imod/mf6/sto.py b/imod/mf6/sto.py index 049a098eb..006859852 100644 --- a/imod/mf6/sto.py +++ b/imod/mf6/sto.py @@ -5,7 +5,7 @@ import numpy as np from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid import _regrid_package_data from imod.logging import init_log_decorator from imod.mf6.package import Package from imod.mf6.regrid.regrid_schemes import ( @@ -23,6 +23,7 @@ ) from imod.typing import GridDataArray from imod.typing.grid import zeros_like +from imod.util.regrid import RegridderWeightsCache class Storage(Package): diff --git a/imod/mf6/utilities/imod5_converter.py b/imod/mf6/utilities/imod5_converter.py index d34e62d6f..9a0b2bb5d 100644 --- a/imod/mf6/utilities/imod5_converter.py +++ b/imod/mf6/utilities/imod5_converter.py @@ -4,12 +4,13 @@ import pandas as pd import xarray as xr -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_package_data +from imod.common.utilities.regrid import _regrid_package_data from imod.common.utilities.regrid_method_type import RegridMethodType from imod.mf6.package import Package from imod.typing import GridDataDict, Imod5DataDict from imod.typing.grid import full_like from imod.util.dims import drop_layer_dim_cap_data +from imod.util.regrid import RegridderWeightsCache def convert_ibound_to_idomain( diff --git a/imod/msw/idf_mapping.py b/imod/msw/idf_mapping.py index 7fb708d5b..1dd2e64a6 100644 --- a/imod/msw/idf_mapping.py +++ b/imod/msw/idf_mapping.py @@ -5,12 +5,13 @@ import xarray as xr from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import RegridderWeightsCache, _regrid_array +from imod.common.utilities.regrid import _regrid_array from imod.common.utilities.regrid_method_type import RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.regrid.regrid_schemes import IdfMappingRegridMethod from imod.typing import GridDataArray +from imod.util.regrid import RegridderWeightsCache from imod.util.spatial import spatial_reference diff --git a/imod/msw/meteo_mapping.py b/imod/msw/meteo_mapping.py index 3b7db1f97..c737672c6 100644 --- a/imod/msw/meteo_mapping.py +++ b/imod/msw/meteo_mapping.py @@ -8,13 +8,13 @@ import xarray as xr import imod -from imod.common.utilities.regrid import RegridderWeightsCache from imod.common.utilities.regrid_method_type import RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.utilities.common import find_in_file_list from imod.prepare import common from imod.typing import GridDataArray, Imod5DataDict, IntArray +from imod.util.regrid import RegridderWeightsCache def _is_parsable_and_existing_path(potential_path: str, mete_grid_path: Path) -> bool: diff --git a/imod/msw/model.py b/imod/msw/model.py index adb0d4ecb..a8de4f644 100644 --- a/imod/msw/model.py +++ b/imod/msw/model.py @@ -8,7 +8,6 @@ import numpy as np import xarray as xr -from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.dis import StructuredDiscretization from imod.mf6.mf6_wel_adapter import Mf6Wel from imod.msw.copy_files import FileCopier @@ -37,7 +36,7 @@ from imod.msw.vegetation import AnnualCropFactors from imod.typing import Imod5DataDict from imod.util.dims import drop_layer_dim_cap_data -from imod.util.regrid import RegridderType +from imod.util.regrid import RegridderType, RegridderWeightsCache REQUIRED_PACKAGES = ( GridData, diff --git a/imod/msw/output_control.py b/imod/msw/output_control.py index cbfc58aa6..6fb6f83ac 100644 --- a/imod/msw/output_control.py +++ b/imod/msw/output_control.py @@ -4,12 +4,12 @@ import pandas as pd from imod.common.interfaces.iregridpackage import IRegridPackage -from imod.common.utilities.regrid import RegridderWeightsCache from imod.common.utilities.regrid_method_type import RegridMethodType from imod.msw.fixed_format import VariableMetaData from imod.msw.pkgbase import MetaSwapPackage from imod.msw.timeutil import to_metaswap_timeformat from imod.typing import GridDataArray +from imod.util.regrid import RegridderWeightsCache # I did not use long variable names here (e.g. "precipitation"), as MetaSWAP diff --git a/imod/msw/pkgbase.py b/imod/msw/pkgbase.py index e0b0033f1..ddeeb2e3d 100644 --- a/imod/msw/pkgbase.py +++ b/imod/msw/pkgbase.py @@ -8,7 +8,6 @@ import xarray as xr from imod.common.utilities.regrid import ( - RegridderWeightsCache, _regrid_like, ) from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType @@ -17,6 +16,7 @@ from imod.msw.fixed_format import format_fixed_width from imod.typing import IntArray from imod.typing.grid import GridDataArray, GridDataset +from imod.util.regrid import RegridderWeightsCache DataDictType: TypeAlias = dict[str, IntArray | int | str] diff --git a/imod/tests/test_mf6/test_mf6_hfb.py b/imod/tests/test_mf6/test_mf6_hfb.py index df98ac1b7..29303e3cc 100644 --- a/imod/tests/test_mf6/test_mf6_hfb.py +++ b/imod/tests/test_mf6/test_mf6_hfb.py @@ -10,7 +10,6 @@ from pytest_cases import parametrize_with_cases from shapely import Polygon, get_coordinates, linestrings -from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6 import ( HorizontalFlowBarrierHydraulicCharacteristic, HorizontalFlowBarrierMultiplier, @@ -36,6 +35,7 @@ ) from imod.tests.fixtures.flow_basic_fixture import BasicDisSettings from imod.typing.grid import nan_like, ones_like +from imod.util.regrid import RegridderWeightsCache @pytest.mark.parametrize("dis", ["basic_unstructured_dis", "basic_dis"]) diff --git a/imod/tests/test_mf6/test_mf6_rch.py b/imod/tests/test_mf6/test_mf6_rch.py index efb1cba5d..6678d4659 100644 --- a/imod/tests/test_mf6/test_mf6_rch.py +++ b/imod/tests/test_mf6/test_mf6_rch.py @@ -9,11 +9,11 @@ import xarray as xr import imod -from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.dis import StructuredDiscretization from imod.mf6.write_context import WriteContext from imod.schemata import ValidationError from imod.typing.grid import is_planar_grid, is_transient_data_grid, nan_like +from imod.util.regrid import RegridderWeightsCache @pytest.fixture(scope="function") diff --git a/imod/tests/test_mf6/test_mf6_regrid_package.py b/imod/tests/test_mf6/test_mf6_regrid_package.py index 481b099d8..342ce6265 100644 --- a/imod/tests/test_mf6/test_mf6_regrid_package.py +++ b/imod/tests/test_mf6/test_mf6_regrid_package.py @@ -8,7 +8,6 @@ import xugrid as xu import imod -from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.package import Package from imod.tests.fixtures.mf6_small_models_fixture import ( grid_data_structured, @@ -16,6 +15,7 @@ grid_data_unstructured, grid_data_unstructured_layered, ) +from imod.util.regrid import RegridderWeightsCache def create_package_instances(is_structured: bool) -> List[Package]: diff --git a/imod/tests/test_mf6/test_mf6_regrid_simulation.py b/imod/tests/test_mf6/test_mf6_regrid_simulation.py index 94aabd36a..4de2a91e9 100644 --- a/imod/tests/test_mf6/test_mf6_regrid_simulation.py +++ b/imod/tests/test_mf6/test_mf6_regrid_simulation.py @@ -4,7 +4,6 @@ import pytest import imod -from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6 import VerticesDiscretization from imod.mf6.regrid.regrid_schemes import ConstantHeadRegridMethod from imod.tests.fixtures.mf6_modelrun_fixture import assert_simulation_can_run @@ -12,7 +11,7 @@ grid_data_structured, grid_data_unstructured, ) -from imod.util.regrid import RegridderType +from imod.util.regrid import RegridderType, RegridderWeightsCache def test_regrid_structured_simulation_to_structured_simulation( diff --git a/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py b/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py index 8de41a6ab..606347615 100644 --- a/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py +++ b/imod/tests/test_mf6/test_mf6_unsupported_grid_operations.py @@ -2,8 +2,8 @@ import pytest import xarray as xr -from imod.common.utilities.regrid import RegridderWeightsCache from imod.typing.grid import zeros_like +from imod.util.regrid import RegridderWeightsCache def finer_grid(grid): diff --git a/imod/tests/test_mf6/test_utilities/test_regrid_utils.py b/imod/tests/test_mf6/test_utilities/test_regrid_utils.py index ab5ffd177..d1ff5c253 100644 --- a/imod/tests/test_mf6/test_utilities/test_regrid_utils.py +++ b/imod/tests/test_mf6/test_utilities/test_regrid_utils.py @@ -4,8 +4,9 @@ import pytest from xugrid import OverlapRegridder -from imod.common.utilities.regrid import RegridderType, RegridderWeightsCache +from imod.common.utilities.regrid import RegridderType from imod.mf6 import Dispersion +from imod.util.regrid import RegridderWeightsCache def is_equal_regridder(instance_1, instance_2) -> bool: diff --git a/imod/tests/test_msw/test_annual_crop_factors.py b/imod/tests/test_msw/test_annual_crop_factors.py index 45f2e9e12..65cdbf401 100644 --- a/imod/tests/test_msw/test_annual_crop_factors.py +++ b/imod/tests/test_msw/test_annual_crop_factors.py @@ -1,10 +1,10 @@ import numpy as np import xarray as xr -from imod.common.utilities.regrid import ( +from imod.msw import AnnualCropFactors +from imod.util.regrid import ( RegridderWeightsCache, ) -from imod.msw import AnnualCropFactors def setup_cropfactors(): diff --git a/imod/tests/test_msw/test_grid_data.py b/imod/tests/test_msw/test_grid_data.py index c6526b3ef..5ebe60b7b 100644 --- a/imod/tests/test_msw/test_grid_data.py +++ b/imod/tests/test_msw/test_grid_data.py @@ -11,10 +11,10 @@ from numpy.testing import assert_almost_equal, assert_equal from pytest_cases import case, parametrize_with_cases -from imod.common.utilities.regrid import RegridderWeightsCache from imod.mf6.dis import StructuredDiscretization from imod.msw import GridData from imod.msw.fixed_format import format_fixed_width +from imod.util.regrid import RegridderWeightsCache from imod.util.spatial import get_total_grid_area diff --git a/imod/tests/test_msw/test_idf_mapping.py b/imod/tests/test_msw/test_idf_mapping.py index 40f613cfa..8c7549634 100644 --- a/imod/tests/test_msw/test_idf_mapping.py +++ b/imod/tests/test_msw/test_idf_mapping.py @@ -1,8 +1,8 @@ import numpy as np -from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw.idf_mapping import IdfMapping from imod.tests.fixtures.msw_regrid_fixture import get_3x3_area, get_5x5_new_grid +from imod.util.regrid import RegridderWeightsCache def test_idf_mapping_regrid(): diff --git a/imod/tests/test_msw/test_infiltration.py b/imod/tests/test_msw/test_infiltration.py index 8d9921454..f9ee8b7a1 100644 --- a/imod/tests/test_msw/test_infiltration.py +++ b/imod/tests/test_msw/test_infiltration.py @@ -11,12 +11,12 @@ from numpy.testing import assert_almost_equal, assert_equal from pytest_cases import case, parametrize_with_cases -from imod.common.utilities.regrid import ( - RegridderWeightsCache, -) from imod.msw import Infiltration from imod.msw.fixed_format import format_fixed_width from imod.typing import GridDataDict +from imod.util.regrid import ( + RegridderWeightsCache, +) @pytest.fixture(scope="function") diff --git a/imod/tests/test_msw/test_initial_conditions.py b/imod/tests/test_msw/test_initial_conditions.py index a821e3ccd..34daf864a 100644 --- a/imod/tests/test_msw/test_initial_conditions.py +++ b/imod/tests/test_msw/test_initial_conditions.py @@ -4,9 +4,6 @@ import numpy as np import pytest -from imod.common.utilities.regrid import ( - RegridderWeightsCache, -) from imod.msw import ( InitialConditionsEquilibrium, InitialConditionsPercolation, @@ -14,6 +11,9 @@ InitialConditionsSavedState, ) from imod.typing.grid import is_empty +from imod.util.regrid import ( + RegridderWeightsCache, +) DUMMY_ARGS = None, None, None, None diff --git a/imod/tests/test_msw/test_landuse_options.py b/imod/tests/test_msw/test_landuse_options.py index 9838e4f6a..275f6b709 100644 --- a/imod/tests/test_msw/test_landuse_options.py +++ b/imod/tests/test_msw/test_landuse_options.py @@ -5,10 +5,10 @@ import xarray as xr from numpy.testing import assert_almost_equal, assert_equal -from imod.common.utilities.regrid import ( +from imod.msw import LanduseOptions +from imod.util.regrid import ( RegridderWeightsCache, ) -from imod.msw import LanduseOptions def create_landuse_dict(): diff --git a/imod/tests/test_msw/test_meteo_grid.py b/imod/tests/test_msw/test_meteo_grid.py index 79c7cdddf..1e6335adb 100644 --- a/imod/tests/test_msw/test_meteo_grid.py +++ b/imod/tests/test_msw/test_meteo_grid.py @@ -9,8 +9,8 @@ import pytest from numpy.testing import assert_equal -from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw import MeteoGrid, MeteoGridCopy +from imod.util.regrid import RegridderWeightsCache def test_meteo_grid_init(meteo_grids): diff --git a/imod/tests/test_msw/test_model.py b/imod/tests/test_msw/test_model.py index 671ec2e9c..77ec5d447 100644 --- a/imod/tests/test_msw/test_model.py +++ b/imod/tests/test_msw/test_model.py @@ -7,11 +7,11 @@ from pytest_cases import parametrize_with_cases from imod import mf6, msw -from imod.common.utilities.regrid import RegridderWeightsCache from imod.msw.copy_files import FileCopier from imod.msw.model import DEFAULT_SETTINGS from imod.msw.utilities.parse import read_para_sim from imod.typing import GridDataArray, Imod5DataDict +from imod.util.regrid import RegridderWeightsCache def test_msw_model_write(msw_model, coupled_mf6_model, coupled_mf6wel, tmp_path): diff --git a/imod/tests/test_msw/test_ponding.py b/imod/tests/test_msw/test_ponding.py index e647acc2e..3cee28e86 100644 --- a/imod/tests/test_msw/test_ponding.py +++ b/imod/tests/test_msw/test_ponding.py @@ -6,11 +6,11 @@ from numpy import nan from numpy.testing import assert_almost_equal, assert_equal -from imod.common.utilities.regrid import ( - RegridderWeightsCache, -) from imod.msw import Ponding from imod.typing import GridDataArray, GridDataDict +from imod.util.regrid import ( + RegridderWeightsCache, +) def setup_ponding() -> tuple[GridDataDict, np.ndarray, GridDataArray]: diff --git a/imod/tests/test_msw/test_scaling_factors.py b/imod/tests/test_msw/test_scaling_factors.py index b009c6be5..fb9fb1608 100644 --- a/imod/tests/test_msw/test_scaling_factors.py +++ b/imod/tests/test_msw/test_scaling_factors.py @@ -6,11 +6,11 @@ from numpy import nan from numpy.testing import assert_almost_equal, assert_equal -from imod.common.utilities.regrid import ( - RegridderWeightsCache, -) from imod.msw import ScalingFactors from imod.typing.grid import ones_like +from imod.util.regrid import ( + RegridderWeightsCache, +) def setup_scaling_factor_grids(): diff --git a/imod/util/regrid.py b/imod/util/regrid.py index 360a06b7a..77c2a0803 100644 --- a/imod/util/regrid.py +++ b/imod/util/regrid.py @@ -1,6 +1,12 @@ +import abc from enum import Enum +from typing import Dict, Optional, Tuple, Union import xugrid as xu +from xugrid.regrid.regridder import BaseRegridder + +from imod.typing import GridDataArray +from imod.typing.grid import get_grid_geometry_hash class RegridderType(Enum): @@ -15,3 +21,96 @@ class RegridderType(Enum): BARYCENTRIC = xu.BarycentricInterpolator OVERLAP = xu.OverlapRegridder RELATIVEOVERLAP = xu.RelativeOverlapRegridder + + +HashRegridderMapping = Tuple[int, int, BaseRegridder] + + +class RegridderWeightsCache: + """ + This class stores any number of regridders that can regrid a single source + grid to a single target grid. By storing the regridders, we make sure the + regridders can be re-used for different arrays on the same grid. Regridders + are stored based on their type (`see these + docs`_) + and planar coordinates (x, y). This is important because computing the + regridding weights is a costly affair. + """ + + def __init__( + self, + max_cache_size: int = 6, + ) -> None: + self.regridder_instances: dict[ + tuple[type[BaseRegridder], Optional[str]], BaseRegridder + ] = {} + self.weights_cache: Dict[HashRegridderMapping, GridDataArray] = {} + self.max_cache_size = max_cache_size + + def __get_regridder_class( + self, regridder_type: RegridderType | BaseRegridder + ) -> type[BaseRegridder]: + if isinstance(regridder_type, abc.ABCMeta): + if not issubclass(regridder_type, BaseRegridder): + raise ValueError( + "only derived types of BaseRegridder can be instantiated" + ) + return regridder_type + elif isinstance(regridder_type, RegridderType): + return regridder_type.value + + raise ValueError("invalid type for regridder") + + def get_regridder( + self, + source_grid: GridDataArray, + target_grid: GridDataArray, + regridder_type: Union[RegridderType, BaseRegridder], + method: Optional[str] = None, + ) -> BaseRegridder: + """ + returns a regridder of the specified type and with the specified method. + The desired type can be passed through the argument "regridder_type" as + an enumerator or as a class. The following two are equivalent: + instancesCollection.get_regridder(RegridderType.OVERLAP, "mean") + instancesCollection.get_regridder(xu.OverlapRegridder, "mean") + + + Parameters + ---------- + regridder_type: RegridderType or regridder class + indicates the desired regridder type + method: str or None + indicates the method the regridder should apply + + Returns + ------- + a regridder of the specified characteristics + """ + regridder_class = self.__get_regridder_class(regridder_type) + + if "layer" not in source_grid.coords and "layer" in target_grid.coords: + target_grid = target_grid.drop_vars("layer") + + source_hash = get_grid_geometry_hash(source_grid) + target_hash = get_grid_geometry_hash(target_grid) + key = (source_hash, target_hash, regridder_class) + if key not in self.weights_cache.keys(): + if len(self.weights_cache) >= self.max_cache_size: + self.remove_first_regridder() + kwargs = {"source": source_grid, "target": target_grid} + if method is not None: + kwargs["method"] = method + regridder = regridder_class(**kwargs) + self.weights_cache[key] = regridder.weights + else: + kwargs = {"weights": self.weights_cache[key], "target": target_grid} + if method is not None: + kwargs["method"] = method + regridder = regridder_class.from_weights(**kwargs) + + return regridder + + def remove_first_regridder(self): + keys = list(self.weights_cache.keys()) + self.weights_cache.pop(keys[0]) From ef82bb44c11de8f460c023e185be91db8d2e9b11 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 14:07:23 +0100 Subject: [PATCH 05/17] Update changelog --- docs/api/changelog.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/api/changelog.rst b/docs/api/changelog.rst index 790c9a06c..e652093a6 100644 --- a/docs/api/changelog.rst +++ b/docs/api/changelog.rst @@ -61,7 +61,8 @@ Changed ``scipy.sparse.linalg.cg`` as the backend. We've remove the support for the ``ibound`` argument, the ``iter1`` argument has been dropped, ``mxiter`` has been renamed to ``maxiter``, ``close`` has been renamed to ``rtol``. - +- Moved ``imod.mf6.utilities.regrid.RegridderWeightsCache`` to the + :class:`imod.util.regrid.RegridderWeightsCache`. Fixed ~~~~~ @@ -472,7 +473,7 @@ Changed :class:`imod.mf6.Solution` to match newer MODFLOW 6 releases. - Changed no_ptc from a bool to an option string in :class:`imod.mf6.Solution`. - Removed constructor arguments `source` and `target` from - :class:`imod.mf6.utilities.regrid.RegridderWeightsCache`, as they were not + ``imod.mf6.utilities.regrid.RegridderWeightsCache``, as they were not used. - :func:`imod.mf6.open_cbc` now returns arrays which contain np.nan for cells where budget variables are not defined. Based on new budget output a disquisition between From 0d77553a51db6056d0a959e592719d5cdb5106b2 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 14:13:19 +0100 Subject: [PATCH 06/17] Delete unnecessary __init__ --- imod/common/__init__.py | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 imod/common/__init__.py diff --git a/imod/common/__init__.py b/imod/common/__init__.py deleted file mode 100644 index 8af64b3e9..000000000 --- a/imod/common/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from imod.common.utilities.regrid import RegridderType -from imod.util.regrid import RegridderWeightsCache From d473bc643e609b3c78b3d2fc974f8f5bff3cbaed Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 14:13:54 +0100 Subject: [PATCH 07/17] Add empty __init__ again --- imod/common/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 imod/common/__init__.py diff --git a/imod/common/__init__.py b/imod/common/__init__.py new file mode 100644 index 000000000..e69de29bb From e1a1643545e1d07f8a9a9f822560d6b919dd37d2 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 14:21:34 +0100 Subject: [PATCH 08/17] Move is_valid to common.filter_values --- imod/common/utilities/filter_values.py | 25 +++++++++++++++++++++++++ imod/common/utilities/regrid.py | 4 ++-- imod/mf6/package.py | 4 ++-- imod/mf6/utilities/package.py | 20 -------------------- 4 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 imod/common/utilities/filter_values.py diff --git a/imod/common/utilities/filter_values.py b/imod/common/utilities/filter_values.py new file mode 100644 index 000000000..7c2259fcb --- /dev/null +++ b/imod/common/utilities/filter_values.py @@ -0,0 +1,25 @@ +import numpy as np + + +from typing import Any + + +def is_valid(value: Any) -> bool: + """ + Filters values that are None, False, or a numpy.bool_ False. + Needs to be this specific, since 0.0 and 0 are valid values, but are + equal to a boolean False. + """ + # Test singletons + if value is False or value is None: + return False + # Test numpy bool (not singleton) + elif isinstance(value, np.bool_) and not value: + return False + # When dumping to netCDF and reading back, None will have been + # converted into a NaN. Only check NaN if it's a floating type to avoid + # TypeErrors. + elif np.issubdtype(type(value), np.floating) and np.isnan(value): + return False + else: + return True \ No newline at end of file diff --git a/imod/common/utilities/regrid.py b/imod/common/utilities/regrid.py index b669e4daa..5e6a84f7b 100644 --- a/imod/common/utilities/regrid.py +++ b/imod/common/utilities/regrid.py @@ -21,7 +21,7 @@ remove_expanded_auxiliary_variables_from_dataset, ) from imod.mf6.utilities.clip import clip_by_grid -from imod.mf6.utilities.package import _is_valid +from imod.common.utilities.filter_values import is_valid from imod.schemata import ValidationError from imod.typing.grid import ( GridDataArray, @@ -69,7 +69,7 @@ def _regrid_array( # skip regridding for scalar arrays with no valid values (such as "None") scalar_da: bool = is_scalar(da) - if scalar_da and not _is_valid(da.values[()]): + if scalar_da and not is_valid(da.values[()]): return None # the dataarray might be a scalar. If it is, then it does not need regridding. diff --git a/imod/mf6/package.py b/imod/mf6/package.py index fa783f51a..d7c01cfdd 100644 --- a/imod/mf6/package.py +++ b/imod/mf6/package.py @@ -29,7 +29,7 @@ PackageBase, ) from imod.mf6.utilities.mask import mask_package -from imod.mf6.utilities.package import _is_valid +from imod.common.utilities.filter_values import is_valid from imod.mf6.utilities.schemata import filter_schemata_dict from imod.mf6.validation import validation_pkg_error_message from imod.mf6.write_context import WriteContext @@ -87,7 +87,7 @@ def cleanup(self, dis: Any): @staticmethod def _valid(value: Any) -> bool: - return _is_valid(value) + return is_valid(value) @staticmethod def _number_format(dtype: type): diff --git a/imod/mf6/utilities/package.py b/imod/mf6/utilities/package.py index 6d0c84239..836904006 100644 --- a/imod/mf6/utilities/package.py +++ b/imod/mf6/utilities/package.py @@ -1,4 +1,3 @@ -from typing import Any import numpy as np import xarray as xr @@ -29,22 +28,3 @@ def get_repeat_stress(times) -> xr.DataArray: ) -def _is_valid(value: Any) -> bool: - """ - Filters values that are None, False, or a numpy.bool_ False. - Needs to be this specific, since 0.0 and 0 are valid values, but are - equal to a boolean False. - """ - # Test singletons - if value is False or value is None: - return False - # Test numpy bool (not singleton) - elif isinstance(value, np.bool_) and not value: - return False - # When dumping to netCDF and reading back, None will have been - # converted into a NaN. Only check NaN if it's a floating type to avoid - # TypeErrors. - elif np.issubdtype(type(value), np.floating) and np.isnan(value): - return False - else: - return True From b074595056b16bcede09cce17a3fef21f42b8699 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 14:22:50 +0100 Subject: [PATCH 09/17] Format and rename to value_filters --- imod/common/utilities/regrid.py | 2 +- .../utilities/{filter_values.py => value_filters.py} | 7 +++---- imod/mf6/package.py | 2 +- imod/mf6/utilities/package.py | 3 --- 4 files changed, 5 insertions(+), 9 deletions(-) rename imod/common/utilities/{filter_values.py => value_filters.py} (97%) diff --git a/imod/common/utilities/regrid.py b/imod/common/utilities/regrid.py index 5e6a84f7b..44faee982 100644 --- a/imod/common/utilities/regrid.py +++ b/imod/common/utilities/regrid.py @@ -16,12 +16,12 @@ from imod.common.interfaces.isimulation import ISimulation from imod.common.statusinfo import NestedStatusInfo from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType +from imod.common.utilities.value_filters import is_valid from imod.mf6.auxiliary_variables import ( expand_transient_auxiliary_variables, remove_expanded_auxiliary_variables_from_dataset, ) from imod.mf6.utilities.clip import clip_by_grid -from imod.common.utilities.filter_values import is_valid from imod.schemata import ValidationError from imod.typing.grid import ( GridDataArray, diff --git a/imod/common/utilities/filter_values.py b/imod/common/utilities/value_filters.py similarity index 97% rename from imod/common/utilities/filter_values.py rename to imod/common/utilities/value_filters.py index 7c2259fcb..1200b8c6b 100644 --- a/imod/common/utilities/filter_values.py +++ b/imod/common/utilities/value_filters.py @@ -1,8 +1,7 @@ -import numpy as np - - from typing import Any +import numpy as np + def is_valid(value: Any) -> bool: """ @@ -22,4 +21,4 @@ def is_valid(value: Any) -> bool: elif np.issubdtype(type(value), np.floating) and np.isnan(value): return False else: - return True \ No newline at end of file + return True diff --git a/imod/mf6/package.py b/imod/mf6/package.py index d7c01cfdd..7d7dc4df1 100644 --- a/imod/mf6/package.py +++ b/imod/mf6/package.py @@ -19,6 +19,7 @@ _regrid_like, ) from imod.common.utilities.regrid_method_type import EmptyRegridMethod, RegridMethodType +from imod.common.utilities.value_filters import is_valid from imod.logging import standard_log_decorator from imod.mf6.auxiliary_variables import ( get_variable_names, @@ -29,7 +30,6 @@ PackageBase, ) from imod.mf6.utilities.mask import mask_package -from imod.common.utilities.filter_values import is_valid from imod.mf6.utilities.schemata import filter_schemata_dict from imod.mf6.validation import validation_pkg_error_message from imod.mf6.write_context import WriteContext diff --git a/imod/mf6/utilities/package.py b/imod/mf6/utilities/package.py index 836904006..2a9f2aada 100644 --- a/imod/mf6/utilities/package.py +++ b/imod/mf6/utilities/package.py @@ -1,4 +1,3 @@ - import numpy as np import xarray as xr @@ -26,5 +25,3 @@ def get_repeat_stress(times) -> xr.DataArray: data=np.column_stack((keys, values)), dims=("repeat", "repeat_items"), ) - - From 8cc38ad0375eebd9752180d66edfbe3cfec16b61 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Wed, 26 Feb 2025 16:56:00 +0100 Subject: [PATCH 10/17] extend tests to verify results are the same --- imod/tests/test_regrid.py | 64 ++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/imod/tests/test_regrid.py b/imod/tests/test_regrid.py index a88baa6f8..df6a6b96b 100644 --- a/imod/tests/test_regrid.py +++ b/imod/tests/test_regrid.py @@ -5,6 +5,7 @@ import pandas as pd import pytest import xarray as xr +import xugrid as xu import imod @@ -34,6 +35,17 @@ def weightedmean(values, weights): return vsum / wsum +def weightedmean_xu(values, weights, workspace): + vsum = 0.0 + wsum = 0.0 + for i in range(values.size): + v = values[i] + w = weights[i] + vsum += w * v + wsum += w + return vsum / wsum + + def conductance(values, weights): v_agg = 0.0 w_sum = 0.0 @@ -405,17 +417,21 @@ def test_regrid_mean2d(chunksize): ] ) assert np.allclose(out.values, compare) + # Verify xugrid returns same result + out_xu = xu.OverlapRegridder( + source=source, target=like, method=weightedmean_xu + ).regrid(source) + assert np.allclose(out_xu.values, compare) # Now with chunking source = source.chunk({"x": chunksize, "y": chunksize}) out = imod.prepare.Regridder(method=weightedmean).regrid(source, like) assert np.allclose(out.values, compare) - - # Now with orthogonal chunks - source = source.chunk({"x": 2}) - likecoords = {"y": dst_x} - like = xr.DataArray(np.empty(2), likecoords, ["y"]) - out = imod.prepare.Regridder(method=weightedmean).regrid(source, like) + # Verify xugrid returns same result + out_xu = xu.OverlapRegridder( + source=source, target=like, method=weightedmean_xu + ).regrid(source) + assert np.allclose(out_xu.values, compare) @pytest.mark.parametrize("chunksize", [1, 2, 3]) @@ -446,13 +462,22 @@ def test_regrid_mean2d_over3darray(chunksize): ) compare = np.empty((5, 2, 2)) compare[:, ...] = compare_values - assert np.allclose(out.values, compare) + # Verify xugrid returns same result + out_xu = xu.OverlapRegridder( + source=source, target=like, method=weightedmean_xu + ).regrid(source) + assert np.allclose(out_xu.values, compare) # Now with chunking source = source.chunk({"x": chunksize, "y": chunksize}) out = imod.prepare.Regridder(method=weightedmean).regrid(source, like) assert np.allclose(out.values, compare) + # Verify xugrid returns same result + out_xu = xu.OverlapRegridder( + source=source, target=like, method=weightedmean_xu + ).regrid(source) + assert np.allclose(out_xu.values, compare) def test_regrid_conductance2d(): @@ -465,10 +490,16 @@ def test_regrid_conductance2d(): src_da = xr.DataArray( [[10.0, 10.0], [10.0, 10.0]], {"y": [7.5, 2.5], "x": [2.5, 7.5]}, dims ) + src_da[0, 0] = np.nan regridder = imod.prepare.Regridder(method=conductance, use_relative_weights=True) dst_da = regridder.regrid(src_da, like_da) assert float(src_da.sum()) == float(dst_da.sum()) + # Verify xugrid returns same result + dst_da_xu = xu.RelativeOverlapRegridder( + source=src_da, target=like_da, method="conductance" + ).regrid(src_da) + assert float(src_da.sum()) == float(dst_da_xu.sum()) # Second case, different domain, smaller cellsizes dx = np.array([2.5, 2.5, 2.5, 3.5]) @@ -478,6 +509,11 @@ def test_regrid_conductance2d(): like_da = xr.DataArray(np.empty((4, 4)), coords, dims) dst_da = regridder.regrid(src_da, like_da) assert float(src_da.sum()) == float(dst_da.sum()) + # Verify xugrid returns same result + dst_da_xu = xu.RelativeOverlapRegridder( + source=src_da, target=like_da, method="conductance" + ).regrid(src_da) + assert float(src_da.sum()) == float(dst_da_xu.sum()) # Third case, same domain, small to large cellsizes y = np.arange(10.0, 0.0, -2.5) - 1.25 @@ -485,12 +521,18 @@ def test_regrid_conductance2d(): coords = {"y": y, "x": x} dims = ("y", "x") src_da = xr.DataArray(np.full((4, 4), 10.0), coords, dims) + src_da[0, 0] = np.nan like_da = xr.DataArray( [[10.0, 10.0], [10.0, 10.0]], {"y": [7.5, 2.5], "x": [2.5, 7.5]}, dims ) dst_da = regridder.regrid(src_da, like_da) assert float(src_da.sum()) == float(dst_da.sum()) + # Verify xugrid returns same result + dst_da_xu = xu.RelativeOverlapRegridder( + source=src_da, target=like_da, method="conductance" + ).regrid(src_da) + assert float(src_da.sum()) == float(dst_da_xu.sum()) # Fourth case, larger domain, small to large cellsizes like_da = xr.DataArray( @@ -499,6 +541,11 @@ def test_regrid_conductance2d(): dst_da = regridder.regrid(src_da, like_da) assert float(src_da.sum()) == float(dst_da.sum()) + # Verify xugrid returns same result + dst_da_xu = xu.RelativeOverlapRegridder( + source=src_da, target=like_da, method="conductance" + ).regrid(src_da) + assert float(src_da.sum()) == float(dst_da_xu.sum()) def test_regrid_errors(): @@ -541,7 +588,8 @@ def test_str_method(): out = imod.prepare.Regridder(method="mean").regrid(source, like) assert np.allclose(out.values, compare) - out = imod.prepare.Regridder(method="nearest").regrid(source, like) + # Test if "nearest" name is still supported + imod.prepare.Regridder(method="nearest").regrid(source, like) def test_no_overlap(): From 38164ba494451125643d21b8738911269bf990ef Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Thu, 27 Feb 2025 14:57:31 +0100 Subject: [PATCH 11/17] Extend docstring with warning and code example --- imod/prepare/regrid.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/imod/prepare/regrid.py b/imod/prepare/regrid.py index a6fc6663f..a654e3951 100644 --- a/imod/prepare/regrid.py +++ b/imod/prepare/regrid.py @@ -299,12 +299,14 @@ def _nd_regrid(src, dst, src_coords, dst_coords, iter_regrid, use_relative_weigh WARNING_MSG = textwrap.dedent( - """{name} is deprecated and we plan to remove it in the - final v1.0 release. Use the regridder in xugrid instead. To regrid a single - array, see: + """{name} is deprecated so we will remove it eventually. Use the + the regridder in ``xugrid`` instead, which is around 10 times faster. To + regrid a single array, see: https://deltares.github.io/xugrid/examples/regridder_overview.html. To - regrid Modflow6 packages or entire simulations, see the user guide: - https://deltares.github.io/imod-python/user-guide/08-regridding.html.""" + regrid Modflow6 packages or entire simulations, see the iMOD Python user + guide: + https://deltares.github.io/imod-python/user-guide/08-regridding.html. + """ ) @@ -313,6 +315,22 @@ class Regridder(object): Object to repeatedly regrid similar objects. Compiles once on first call, can then be repeatedly called without JIT compilation overhead. + .. caution:: + + ``imod.prepare.Regridder`` is deprecated so we will remove it + eventually. Use the regridder in ``xugrid`` instead, which is around 10 + times faster. It is as simple as: + + >>> import xugrid as xu + >>> regridder = xu.OverlapRegridder(source=source, target=like, method="mean") + >>> result = regridder.regrid(source) + + For more information, see: + https://deltares.github.io/xugrid/examples/regridder_overview.html. To + regrid MODFLOW6 packages or entire MODFLOW6 simulations, see the iMOD + Python user guide: + https://deltares.github.io/imod-python/user-guide/08-regridding.html. + Attributes ---------- method : str, function From 0dca9d7ed7c4b9bc465feaf6af97979cc11c17b8 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Thu, 27 Feb 2025 17:59:09 +0100 Subject: [PATCH 12/17] Document subtle difference in changelog. --- docs/api/changelog.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/api/changelog.rst b/docs/api/changelog.rst index d62852b66..4c15e5d4e 100644 --- a/docs/api/changelog.rst +++ b/docs/api/changelog.rst @@ -9,6 +9,16 @@ The format is based on `Keep a Changelog`_, and this project adheres to [Unreleased] ------------ +From this release on, we recommend using `xugrids regridding utilities +`_ for +regridding individual grids instead of :class:`imod.prepare.Regridder`. Xugrid's +regridders are tested to be about 10 times faster than +:class:`imod.prepare.Regridder`. There is one small difference: xugrid's +``xugrid.BaryCentricInterpolator`` considers points cell edges to be inside, +whereas :class:`imod.prepare.Regridder` considers them to be outside. This +difference is negligible for most applications, but might create slightly less +``np.nan`` values than before. + Removed ~~~~~~~ - ``imod.flow`` module has been removed for generating iMODFLOW models. Use From 3c8ca25ae78eeadd1b5a9c4a4d7e0182e76491be Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Thu, 27 Feb 2025 17:59:20 +0100 Subject: [PATCH 13/17] Update "How do I" --- docs/faq/how-do-i/modification.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/faq/how-do-i/modification.rst b/docs/faq/how-do-i/modification.rst index d7d81cd8b..b520a0560 100644 --- a/docs/faq/how-do-i/modification.rst +++ b/docs/faq/how-do-i/modification.rst @@ -99,19 +99,24 @@ Make sure the grids have the same spatial coordinates. Change cellsize (and extent) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`xugrid `_ +allows regridding structured grids (next to regridding unstructured grids). + Nearest neighbor: .. code-block:: python - regridder = imod.prepare.Regridder(source, destination, method="nearest") - out = regridder.regrid(source) + import xugrid as xu + regridder = xu.CentroidLocatorRegridder(source=source, target=like) + result = regridder.regrid(source) Area weighted mean: .. code-block:: python - regridder = imod.prepare.Regridder(source, destination, method="mean") - out = regridder.regrid(source) + import xugrid as xu + regridder = xu.OverlapRegridder(source=source, target=like, method="mean") + result = regridder.regrid(source) Change time resolution ~~~~~~~~~~~~~~~~~~~~~~ From 2171438e914ca85446ea1fdc9a225887a0615ea6 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Thu, 27 Feb 2025 18:03:35 +0100 Subject: [PATCH 14/17] Fix duplicate target name --- docs/faq/how-do-i/modification.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/faq/how-do-i/modification.rst b/docs/faq/how-do-i/modification.rst index b520a0560..dd48dc552 100644 --- a/docs/faq/how-do-i/modification.rst +++ b/docs/faq/how-do-i/modification.rst @@ -99,8 +99,9 @@ Make sure the grids have the same spatial coordinates. Change cellsize (and extent) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`xugrid `_ -allows regridding structured grids (next to regridding unstructured grids). +`xugrid's regridding functionality +`_ allows +regridding structured grids (next to regridding unstructured grids). Nearest neighbor: From e655ea55e3aec786ac329504f0cd977ab8172e81 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Fri, 28 Feb 2025 10:45:29 +0100 Subject: [PATCH 15/17] Clarify text --- docs/api/changelog.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/api/changelog.rst b/docs/api/changelog.rst index 4c15e5d4e..0f5928b2c 100644 --- a/docs/api/changelog.rst +++ b/docs/api/changelog.rst @@ -9,15 +9,16 @@ The format is based on `Keep a Changelog`_, and this project adheres to [Unreleased] ------------ -From this release on, we recommend using `xugrids regridding utilities +From this release on, we recommend using `xugrid's regridding utilities `_ for regridding individual grids instead of :class:`imod.prepare.Regridder`. Xugrid's regridders are tested to be about 10 times faster than :class:`imod.prepare.Regridder`. There is one small difference: xugrid's -``xugrid.BaryCentricInterpolator`` considers points cell edges to be inside, -whereas :class:`imod.prepare.Regridder` considers them to be outside. This -difference is negligible for most applications, but might create slightly less -``np.nan`` values than before. +``xugrid.BaryCentricInterpolator`` considers sample points of the destination +grid that lie on the source grid's cell edges to be inside, whereas +:class:`imod.prepare.Regridder` considers them to be outside. This difference is +negligible for most applications, but might create slightly fewer ``np.nan`` +values than before. Removed ~~~~~~~ From 4bdaad3e2ea2adc6e7ebaf1b002ec3f01f997b9e Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Fri, 28 Feb 2025 11:05:56 +0100 Subject: [PATCH 16/17] Try adding deprecated decorator --- imod/prepare/regrid.py | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/imod/prepare/regrid.py b/imod/prepare/regrid.py index a654e3951..083d746a4 100644 --- a/imod/prepare/regrid.py +++ b/imod/prepare/regrid.py @@ -29,6 +29,7 @@ import warnings from collections import namedtuple from typing import List, Sequence +from deprecated.sphinx import deprecated import dask import numba @@ -299,38 +300,29 @@ def _nd_regrid(src, dst, src_coords, dst_coords, iter_regrid, use_relative_weigh WARNING_MSG = textwrap.dedent( - """{name} is deprecated so we will remove it eventually. Use the - the regridder in ``xugrid`` instead, which is around 10 times faster. To - regrid a single array, see: + """ + ``imod.prepare.Regridder`` is deprecated so we will remove it + eventually. Use the regridder in ``xugrid`` instead, which is around 10 + times faster. It is as simple as: + + >>> import xugrid as xu + >>> regridder = xu.OverlapRegridder(source=source, target=like, method="mean") + >>> result = regridder.regrid(source) + + For more information, see: https://deltares.github.io/xugrid/examples/regridder_overview.html. To - regrid Modflow6 packages or entire simulations, see the iMOD Python user - guide: + regrid MODFLOW6 packages or entire MODFLOW6 simulations, see the iMOD + Python user guide: https://deltares.github.io/imod-python/user-guide/08-regridding.html. """ ) - +@deprecated(reason=WARNING_MSG) class Regridder(object): """ Object to repeatedly regrid similar objects. Compiles once on first call, can then be repeatedly called without JIT compilation overhead. - .. caution:: - - ``imod.prepare.Regridder`` is deprecated so we will remove it - eventually. Use the regridder in ``xugrid`` instead, which is around 10 - times faster. It is as simple as: - - >>> import xugrid as xu - >>> regridder = xu.OverlapRegridder(source=source, target=like, method="mean") - >>> result = regridder.regrid(source) - - For more information, see: - https://deltares.github.io/xugrid/examples/regridder_overview.html. To - regrid MODFLOW6 packages or entire MODFLOW6 simulations, see the iMOD - Python user guide: - https://deltares.github.io/imod-python/user-guide/08-regridding.html. - Attributes ---------- method : str, function @@ -398,10 +390,6 @@ class Regridder(object): def __init__( self, method, ndim_regrid=None, use_relative_weights=False, extra_overlap=0 ): - warnings.warn( - WARNING_MSG.format(name=self.__class__.__name__), - DeprecationWarning, - ) _method = common._get_method(method, common.METHODS) self.method = _method From c586758c925280766719fdbc3f602c6f4d844fd3 Mon Sep 17 00:00:00 2001 From: JoerivanEngelen Date: Fri, 28 Feb 2025 11:09:43 +0100 Subject: [PATCH 17/17] Revert "Try adding deprecated decorator" This reverts commit 4bdaad3e2ea2adc6e7ebaf1b002ec3f01f997b9e. --- imod/prepare/regrid.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/imod/prepare/regrid.py b/imod/prepare/regrid.py index 083d746a4..a654e3951 100644 --- a/imod/prepare/regrid.py +++ b/imod/prepare/regrid.py @@ -29,7 +29,6 @@ import warnings from collections import namedtuple from typing import List, Sequence -from deprecated.sphinx import deprecated import dask import numba @@ -300,29 +299,38 @@ def _nd_regrid(src, dst, src_coords, dst_coords, iter_regrid, use_relative_weigh WARNING_MSG = textwrap.dedent( - """ - ``imod.prepare.Regridder`` is deprecated so we will remove it - eventually. Use the regridder in ``xugrid`` instead, which is around 10 - times faster. It is as simple as: - - >>> import xugrid as xu - >>> regridder = xu.OverlapRegridder(source=source, target=like, method="mean") - >>> result = regridder.regrid(source) - - For more information, see: + """{name} is deprecated so we will remove it eventually. Use the + the regridder in ``xugrid`` instead, which is around 10 times faster. To + regrid a single array, see: https://deltares.github.io/xugrid/examples/regridder_overview.html. To - regrid MODFLOW6 packages or entire MODFLOW6 simulations, see the iMOD - Python user guide: + regrid Modflow6 packages or entire simulations, see the iMOD Python user + guide: https://deltares.github.io/imod-python/user-guide/08-regridding.html. """ ) -@deprecated(reason=WARNING_MSG) + class Regridder(object): """ Object to repeatedly regrid similar objects. Compiles once on first call, can then be repeatedly called without JIT compilation overhead. + .. caution:: + + ``imod.prepare.Regridder`` is deprecated so we will remove it + eventually. Use the regridder in ``xugrid`` instead, which is around 10 + times faster. It is as simple as: + + >>> import xugrid as xu + >>> regridder = xu.OverlapRegridder(source=source, target=like, method="mean") + >>> result = regridder.regrid(source) + + For more information, see: + https://deltares.github.io/xugrid/examples/regridder_overview.html. To + regrid MODFLOW6 packages or entire MODFLOW6 simulations, see the iMOD + Python user guide: + https://deltares.github.io/imod-python/user-guide/08-regridding.html. + Attributes ---------- method : str, function @@ -390,6 +398,10 @@ class Regridder(object): def __init__( self, method, ndim_regrid=None, use_relative_weights=False, extra_overlap=0 ): + warnings.warn( + WARNING_MSG.format(name=self.__class__.__name__), + DeprecationWarning, + ) _method = common._get_method(method, common.METHODS) self.method = _method