General Questions#

How is imod-python different from FloPy?#

FloPy is the USGS supported Python package to “create, run, and post-process MODFLOW-based models.”. Creating, running, and post-processing MODFLOW-based models is largely the goal of imod-python as well. So, what are the differences?

Supported models#

FloPy nearly fully supports a list of models and software:

  • MODFLOW2005

  • MODFLOW-LGR

  • MODFLOW-USG

  • MODFLOW6

  • SEAWAT

  • MODPATH

  • MT3D

imod supports (partially):

  • iMODFLOW

  • iMOD-WQ

  • MODFLOW6

Data structures#

FloPy is built on NumPy arrays, the fundamental package for scientic computing in Python; imod is built on xarray. To quote “Why xarray?”:

Xarray introduces labels in the form of dimensions, coordinates and attributes on top of raw NumPy-like multidimensional arrays, which allows for a more intuitive, more concise, and less error-prone developer experience.

imod-python started out as a package to read iMODFLOW files (IDF, IPF) into Python as xarray and pandas data structures. The Why xarray? page provides more background, but we can demonstrate with an example of two tasks for a (structured) MODFLOW6 model:

  1. selecting the heads at a specified (x, y) point;

  2. computing a mean of the heads over time.

With FloPy:

import flopy

hds = flopy.utils.binaryfile.HeadFile("GWF_1/GWF_1.hds")
head = hds.get_alldata()

simulation = flopy.mf6.MFSimulation.load(sim_ws=".")
model = simulation.get_model("GWF_1")
grid = model.modelgrid
row, column = grid.intersect(50_000.0, 30_000.0)
selection = head[:, :, row, column]

mean_head = head.mean(axix=0)

With imod:

import imod

head = imod.mf6.open_hds("GWF_1/GWF_1.hds", "GWF_1/GWF_1.grb")

selection = head.sel(x=50_000.0, y=30_000.0, method="nearest")
mean_head = head.mean("time")

Some differences to note:

  • imod always requires the binary grid file – it needs this file to provide dimensions and coordinates to the head data; FloPy stores this information in a separate modelgrid object.

  • As the xarray object knows is location in space, we can directly select values based on coordinates.

  • imod provides us greater convenience – the convenience of xarray over numpy, of coordinates (labels) over integer locations.

  • These conveniences extend to many facets of modeling, e.g.: resampling in time, plotting, input from and output to many file formats.

Dependencies#

FloPy has only two required dependencies:

install_requires =
    numpy >= 1.15.0
    matplotlib >= 1.4.0

imod has significantly more:

install_requires =

affine dask cftime >=1 Jinja2 matplotlib numba numpy pandas pooch tqdm scipy toolz xarray >=0.15 xugrid

Many of these packages have binary dependencies, but both FloPy and imod can be installed via pip. However, full functionality – such as exporting to netCDF, reading and writing GIS formats, or 3D visualization – involves more complex dependencies, which require the pixi or conda package managers to install correctly (especially on Windows).

Large data#

imod has been designed to deal gracefully with large datasets. Mostly, this is thanks to xarray’s Lazy evaluation. However, imod also makes use of dask to deal with datasets that do not fit in memory.

Let’s revisit the example above. What if the heads file of the simulation is a 100 gigabyte and we’d like to store it as a netCDF? Using FloPy, we’d have to write a loop, appending time steps to the netCDF file one by one (using hds.get_data(time=...) instead of hds.get_alldata()). Because imod uses dask, it will automatically process the data on a chunk-by-chunk basis with head.to_netcdf("head.nc").