Skip to content

Commit a31d86f

Browse files
Issue #1041 import simulation (#1059)
Fixes #1041 # Description Adds a function that imports a simulation from an imod5 project file. It creates a simulation, containing 1 GroundwaterFlowModel, containing the packages for which we support importing currently. This includes the mandatory flow packages like NPF and STO, and some boundary conditions (like DRN and RCH) but not yet CHD or WEL. # Checklist - [X] Links to correct issue - [ ] Update changelog, if changes affect users - [X] PR title starts with ``Issue #nr``, e.g. ``Issue #737`` - [X] Unit tests were added - [ ] **If feature added**: Added/extended example --------- Co-authored-by: Joeri van Engelen <joerivanengelen@hotmail.com>
1 parent fc6e1b7 commit a31d86f

File tree

5 files changed

+222
-8
lines changed

5 files changed

+222
-8
lines changed

imod/mf6/model_gwf.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,21 @@
66
import numpy as np
77

88
from imod.logging import init_log_decorator
9+
from imod.logging.logging_decorators import standard_log_decorator
910
from imod.mf6 import ConstantHead
1011
from imod.mf6.clipped_boundary_condition_creator import create_clipped_boundary
12+
from imod.mf6.dis import StructuredDiscretization
13+
from imod.mf6.drn import Drainage
14+
from imod.mf6.ic import InitialConditions
1115
from imod.mf6.model import Modflow6Model
16+
from imod.mf6.npf import NodePropertyFlow
17+
from imod.mf6.rch import Recharge
18+
from imod.mf6.sto import StorageCoefficient
19+
from imod.mf6.utilities.regridding_types import RegridderType
20+
from imod.prepare.topsystem.default_allocation_methods import (
21+
SimulationAllocationOptions,
22+
SimulationDistributingOptions,
23+
)
1224
from imod.typing import GridDataArray
1325

1426

@@ -153,3 +165,83 @@ def update_buoyancy_package(self, transport_models_per_flow_model) -> None:
153165
transport_models_old = buoyancy_package.get_transport_model_names()
154166
if len(transport_models_old) == len(transport_models_per_flow_model):
155167
buoyancy_package.update_transport_models(transport_models_per_flow_model)
168+
169+
@classmethod
170+
@standard_log_decorator()
171+
def from_imod5_data(
172+
cls,
173+
imod5_data: dict[str, dict[str, GridDataArray]],
174+
allocation_options: SimulationAllocationOptions,
175+
distributing_options: SimulationDistributingOptions,
176+
regridder_types: Optional[dict[str, tuple[RegridderType, str]]] = None,
177+
) -> "GroundwaterFlowModel":
178+
"""
179+
Imports a GroundwaterFlowModel (GWF) from the data in an IMOD5 project file.
180+
It adds the packages for which import from imod5 is supported.
181+
Some packages (like OC) must be added manually later.
182+
183+
184+
Parameters
185+
----------
186+
imod5_data: dict[str, dict[str, GridDataArray]]
187+
dictionary containing the arrays mentioned in the project file as xarray datasets,
188+
under the key of the package type to which it belongs
189+
allocation_options: SimulationAllocationOptions
190+
object containing the allocation options per package type.
191+
If you want a package to have a different allocation option,
192+
then it should be imported separately
193+
distributing_options: SimulationDistributingOptions
194+
object containing the conductivity distribution options per package type.
195+
If you want a package to have a different allocation option,
196+
then it should be imported separately
197+
regridder_types: Optional[dict[str, dict[str, tuple[RegridderType, str]]]]
198+
the first key is the package name. The second key is the array name, and the value is
199+
the RegridderType tuple (method + function)
200+
201+
Returns
202+
-------
203+
A GWF model containing the packages that could be imported form IMOD5. Users must still
204+
add the OC package to the model.
205+
206+
"""
207+
# first import the singleton packages
208+
# import discretization
209+
dis_pkg = StructuredDiscretization.from_imod5_data(imod5_data, regridder_types)
210+
grid = dis_pkg.dataset["idomain"]
211+
212+
# import npf
213+
npf_pkg = NodePropertyFlow.from_imod5_data(imod5_data, grid, regridder_types)
214+
215+
# import sto
216+
sto_pkg = StorageCoefficient.from_imod5_data(imod5_data, grid, regridder_types)
217+
218+
# import initial conditions
219+
ic_pkg = InitialConditions.from_imod5_data(imod5_data, grid, regridder_types)
220+
221+
# import recharge
222+
rch_pkg = Recharge.from_imod5_data(imod5_data, dis_pkg, regridder_types)
223+
224+
result = GroundwaterFlowModel()
225+
result["dis"] = dis_pkg
226+
result["npf"] = npf_pkg
227+
result["sto"] = sto_pkg
228+
result["ic"] = ic_pkg
229+
result["rch"] = rch_pkg
230+
231+
# now import the non-singleton packages
232+
# import drainage
233+
imod5_keys = list(imod5_data.keys())
234+
drainage_keys = [key for key in imod5_keys if key[0:3] == "drn"]
235+
for drn_key in drainage_keys:
236+
drn_pkg = Drainage.from_imod5_data(
237+
drn_key,
238+
imod5_data,
239+
dis_pkg,
240+
npf_pkg,
241+
allocation_options.drn,
242+
distributing_option=distributing_options.drn,
243+
regridder_types=regridder_types,
244+
)
245+
result[drn_key] = drn_pkg
246+
247+
return result

imod/mf6/simulation.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from imod.mf6.gwfgwf import GWFGWF
2525
from imod.mf6.gwfgwt import GWFGWT
2626
from imod.mf6.gwtgwt import GWTGWT
27-
from imod.mf6.ims import Solution
27+
from imod.mf6.ims import Solution, SolutionPresetModerate
2828
from imod.mf6.interfaces.imodel import IModel
2929
from imod.mf6.interfaces.isimulation import ISimulation
3030
from imod.mf6.model import Modflow6Model
@@ -41,7 +41,12 @@
4141
from imod.mf6.statusinfo import NestedStatusInfo
4242
from imod.mf6.utilities.mask import _mask_all_models
4343
from imod.mf6.utilities.regrid import _regrid_like
44+
from imod.mf6.utilities.regridding_types import RegridderType
4445
from imod.mf6.write_context import WriteContext
46+
from imod.prepare.topsystem.default_allocation_methods import (
47+
SimulationAllocationOptions,
48+
SimulationDistributingOptions,
49+
)
4550
from imod.schemata import ValidationError
4651
from imod.typing import GridDataArray, GridDataset
4752
from imod.typing.grid import (
@@ -1308,3 +1313,62 @@ def mask_all_models(
13081313
-1 sets cells to vertical passthrough
13091314
"""
13101315
_mask_all_models(self, mask)
1316+
1317+
@classmethod
1318+
@standard_log_decorator()
1319+
def from_imod5_data(
1320+
cls,
1321+
imod5_data: dict[str, dict[str, GridDataArray]],
1322+
allocation_options: SimulationAllocationOptions,
1323+
distributing_options: SimulationDistributingOptions,
1324+
regridder_types: Optional[dict[str, tuple[RegridderType, str]]] = None,
1325+
) -> "Modflow6Simulation":
1326+
"""
1327+
Imports a GroundwaterFlowModel (GWF) from the data in an IMOD5 project file.
1328+
It adds the packages for which import from imod5 is supported.
1329+
Some packages (like OC) must be added manually later.
1330+
1331+
1332+
Parameters
1333+
----------
1334+
imod5_data: dict[str, dict[str, GridDataArray]]
1335+
dictionary containing the arrays mentioned in the project file as xarray datasets,
1336+
under the key of the package type to which it belongs
1337+
allocation_options: SimulationAllocationOptions
1338+
object containing the allocation options per package type.
1339+
If you want a package to have a different allocation option,
1340+
then it should be imported separately
1341+
distributing_options: SimulationDistributingOptions
1342+
object containing the conductivity distribution options per package type.
1343+
If you want a package to have a different allocation option,
1344+
then it should be imported separately
1345+
regridder_types: Optional[dict[str, dict[str, tuple[RegridderType, str]]]]
1346+
the first key is the package name. The second key is the array name, and the value is
1347+
the RegridderType tuple (method + function)
1348+
1349+
Returns
1350+
-------
1351+
"""
1352+
simulation = Modflow6Simulation("imported_simulation")
1353+
1354+
# import GWF model,
1355+
groundwaterFlowModel = GroundwaterFlowModel.from_imod5_data(
1356+
imod5_data,
1357+
allocation_options,
1358+
distributing_options,
1359+
regridder_types,
1360+
)
1361+
simulation["imported_model"] = groundwaterFlowModel
1362+
1363+
# generate ims package
1364+
solution = SolutionPresetModerate(
1365+
["imported_model"],
1366+
print_option="all",
1367+
)
1368+
simulation["ims"] = solution
1369+
1370+
# cleanup packages for validation
1371+
idomain = groundwaterFlowModel.domain
1372+
simulation.mask_all_models(idomain)
1373+
1374+
return simulation

imod/mf6/sto.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,6 @@ class StorageCoefficient(StorageBase):
290290
AllValueSchema(">=", 0.0),
291291
IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)),
292292
),
293-
"convertible": (
294-
IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)),
295-
# No need to check coords: dataset ensures they align with idomain.
296-
),
297293
}
298294

299295
_template = Package._initialize_template(_pkg_id)
@@ -372,9 +368,7 @@ def from_imod5_data(
372368
data, target_grid, regridder_settings, regrid_context, {}
373369
)
374370

375-
new_package_data["convertible"] = zeros_like(
376-
new_package_data["storage_coefficient"], dtype=int
377-
)
371+
new_package_data["convertible"] = zeros_like(target_grid, dtype=int)
378372
new_package_data["transient"] = np.any(
379373
new_package_data["storage_coefficient"].values > 0
380374
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from dataclasses import dataclass
2+
3+
from imod.prepare.topsystem.allocation import ALLOCATION_OPTION
4+
from imod.prepare.topsystem.conductance import DISTRIBUTING_OPTION
5+
6+
7+
@dataclass()
8+
class SimulationAllocationOptions:
9+
"""
10+
Object containing allocation otpions, specified per packages type on
11+
importing fron imod5. Can be used to set defaults when importing a
12+
simulation or a GroundwaterFlowModel from imod5.
13+
14+
Parameters
15+
----------
16+
drn: allocation option to be used for drainage packages
17+
riv: allocation option to be used for river packages
18+
19+
"""
20+
21+
drn: ALLOCATION_OPTION = ALLOCATION_OPTION.first_active_to_elevation
22+
riv: ALLOCATION_OPTION = ALLOCATION_OPTION.stage_to_riv_bot
23+
24+
25+
@dataclass()
26+
class SimulationDistributingOptions:
27+
"""
28+
Object containing conductivity distribution methods, specified per packages
29+
type. Can be used to set defaults when importing a simulation or a
30+
GroundwaterFlowModel from imod5.
31+
32+
Parameters
33+
----------
34+
drn: distribution option to be used for drainage packages
35+
riv: distribution option to be used for river packages
36+
37+
"""
38+
39+
drn: DISTRIBUTING_OPTION = DISTRIBUTING_OPTION.by_corrected_transmissivity
40+
riv: DISTRIBUTING_OPTION = DISTRIBUTING_OPTION.by_corrected_transmissivity

imod/tests/test_mf6/test_mf6_simulation.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818
import imod
1919
from imod.mf6.model import Modflow6Model
2020
from imod.mf6.multimodel.modelsplitter import PartitionInfo
21+
from imod.mf6.oc import OutputControl
22+
from imod.mf6.simulation import Modflow6Simulation
2123
from imod.mf6.statusinfo import NestedStatusInfo, StatusInfo
24+
from imod.prepare.topsystem.default_allocation_methods import (
25+
SimulationAllocationOptions,
26+
SimulationDistributingOptions,
27+
)
2228
from imod.schemata import ValidationError
2329
from imod.tests.fixtures.mf6_small_models_fixture import (
2430
grid_data_structured,
@@ -461,3 +467,21 @@ def compare_submodel_partition_info(first: PartitionInfo, second: PartitionInfo)
461467
return (first.id == second.id) and np.array_equal(
462468
first.active_domain, second.active_domain
463469
)
470+
471+
472+
@pytest.mark.usefixtures("imod5_dataset")
473+
def test_import_from_imod5(imod5_dataset, tmp_path):
474+
default_simulation_allocation_options = SimulationAllocationOptions
475+
default_simulation_distributing_options = SimulationDistributingOptions
476+
simulation = Modflow6Simulation.from_imod5_data(
477+
imod5_dataset,
478+
default_simulation_allocation_options,
479+
default_simulation_distributing_options,
480+
)
481+
482+
simulation["imported_model"] = OutputControl(save_head="last", save_budget="last")
483+
484+
simulation.create_time_discretization(["01-01-2003", "02-01-2003"])
485+
486+
# write and validate the simulation
487+
simulation.write(tmp_path, False, True, False)

0 commit comments

Comments
 (0)