Skip to content

Commit 95a320a

Browse files
authored
start adapting flopy examples and describing a target api (#45)
* start adapting flopy examples and describing a target api * ipython kernel? * add dev dep group to pyproject.toml, expand idm example * update pixi.lock * move attrs tests to demo notebook, merge array example notebooks, minor cleanup
1 parent bd4abfe commit 95a320a

File tree

10 files changed

+687
-370
lines changed

10 files changed

+687
-370
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ jobs:
8282
activate-environment: true
8383

8484
- name: Run tests
85-
run: pixi run test
85+
run: |
86+
pixi run python -m ipykernel install --name flopy4 --user
87+
pixi run test

docs/examples/array_example.py

Lines changed: 233 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,28 @@
1212
# name: python3
1313
# ---
1414

15-
# This example demonstrates the `MFArray` class.
15+
# # Array variables
16+
#
17+
# This example demonstrates how to work with array input variables.
18+
#
19+
# ## Overview
20+
#
21+
# FloPy works natively with NumPy arrays. Array input data can be provided
22+
# as `ndarray` (or anything acting like one). FloPy also provides an array
23+
# subclass `MFArray` supporting some special behaviors:
24+
#
25+
# - more efficient memory usage for constant arrays
26+
# - convenient layered array manipulation
27+
# - applying a multiplication factor
28+
#
29+
# TODO: rewrite the io stuff (external/internal) once that is moved to a
30+
# separate layer from MFArray
1631

1732
# +
1833
from pathlib import Path
34+
from tempfile import TemporaryDirectory
1935

36+
import flopy
2037
import git
2138
import matplotlib.pyplot as plt
2239
import numpy as np
@@ -271,3 +288,218 @@
271288
mlmfa.sum()
272289

273290
mlmfa.min(), mlmfa.mean(), mlmfa.max()
291+
292+
293+
# ## Grid-shaped array data
294+
#
295+
# Most MODFLOW array data are two (row, column) or three (layer,
296+
# row, column) dimensional and represent data on the model grid.
297+
# grid. Other MODFLOW array data contain data by stress period.
298+
# The following list summarizes the types of MODFLOW array data.
299+
300+
# * Time-invariant multi-dimensional array data. This includes:
301+
# 1. One and two dimensional arrays that do not have a layer dimension.
302+
# Examples include `top`, `delc`, and `delr`.
303+
# 2. Three dimensional arrays that can contain a layer dimension.
304+
# Examples include `botm`, `idomain`, and `k`.
305+
# * Transient arrays that can change with time and therefore contain arrays of
306+
# data for one or more stress periods. Examples include `irch` and
307+
# `recharge` in the `RCHA` package.
308+
#
309+
# In the example below a three dimensional ndarray is constructed for the
310+
# `DIS` package's `botm` array. First, the a simulation and groundwater-flow
311+
# model are set up.
312+
313+
# set up where simulation workspace will be stored
314+
temp_dir = TemporaryDirectory()
315+
workspace = temp_dir.name
316+
name = "grid_array_example"
317+
318+
# create the FloPy simulation and tdis objects
319+
sim = flopy.mf6.MFSimulation(
320+
sim_name=name, exe_name="mf6", version="mf6", sim_ws=workspace
321+
)
322+
tdis = flopy.mf6.modflow.mftdis.ModflowTdis(
323+
sim,
324+
pname="tdis",
325+
time_units="DAYS",
326+
nper=2,
327+
perioddata=[(1.0, 1, 1.0), (1.0, 1, 1.0)],
328+
)
329+
# create the Flopy groundwater flow (gwf) model object
330+
model_nam_file = f"{name}.nam"
331+
gwf = flopy.mf6.ModflowGwf(sim, modelname=name, model_nam_file=model_nam_file)
332+
# create the flopy iterative model solver (ims) package object
333+
ims = flopy.mf6.modflow.mfims.ModflowIms(sim, pname="ims", complexity="SIMPLE")
334+
335+
# Then a three-dimensional ndarray of floating point values is created using
336+
# numpy's `linspace` method.
337+
338+
bot = np.linspace(-50.0 / 3.0, -3.0, 3)
339+
delrow = delcol = 4.0
340+
341+
# The `DIS` package is then created passing the three-dimensional array to the
342+
# `botm` parameter. The `botm` array defines the model's cell bottom
343+
# elevations.
344+
345+
dis = flopy.mf6.modflow.mfgwfdis.ModflowGwfdis(
346+
gwf,
347+
pname="dis",
348+
nogrb=True,
349+
nlay=3,
350+
nrow=10,
351+
ncol=10,
352+
delr=delrow,
353+
delc=delcol,
354+
top=0.0,
355+
botm=bot,
356+
)
357+
358+
# ## Adding MODFLOW Grid Array Data
359+
# MODFLOW grid array data, like the data found in the `NPF` package's
360+
# `GridData` block, can be specified as:
361+
#
362+
# 1. A constant value
363+
# 2. A n-dimensional list
364+
# 3. A numpy ndarray
365+
#
366+
# Additionally, layered grid data (generally arrays with a layer dimension) can
367+
# be specified by layer.
368+
#
369+
# In the example below `icelltype` is specified as constants by layer, `k` is
370+
# specified as a numpy ndarray, `k22` is specified as an array by layer, and
371+
# `k33` is specified as a constant.
372+
373+
# First `k` is set up as a 3 layer, by 10 row, by 10 column array with all
374+
# values set to 10.0 using numpy's full method.
375+
376+
k = np.full((3, 10, 10), 10.0)
377+
378+
# Next `k22` is set up as a three dimensional list of nested lists. This
379+
# option can be useful for those that are familiar with python lists but are
380+
# not familiar with the numpy library.
381+
382+
k22_row = []
383+
for row in range(0, 10):
384+
k22_row.append(8.0)
385+
k22_layer = []
386+
for col in range(0, 10):
387+
k22_layer.append(k22_row)
388+
k22 = [k22_layer, k22_layer, k22_layer]
389+
390+
# `K33` is set up as a single constant value. Whenever an array has all the
391+
# same values the easiest and most efficient way to set it up is as a constant
392+
# value. Constant values also take less space to store.
393+
394+
k33 = 1.0
395+
396+
# The `k`, `k22`, and `k33` values defined above are then passed in on
397+
# construction of the npf package.
398+
399+
npf = flopy.mf6.ModflowGwfnpf(
400+
gwf,
401+
pname="npf",
402+
save_flows=True,
403+
icelltype=[1, 1, 1],
404+
k=k,
405+
k22=k22,
406+
k33=k33,
407+
xt3doptions="xt3d rhs",
408+
rewet_record="REWET WETFCT 1.0 IWETIT 1 IHDWET 0",
409+
)
410+
411+
# ### Layered Data
412+
#
413+
# When we look at what will be written to the npf input file, we
414+
# see that the entire `npf.k22` array is written as one long array with the
415+
# number of values equal to `nlay` * `nrow` * `ncol`. And this whole-array
416+
# specification may be of use in some cases. Often times, however, it is
417+
# easier to work with each layer separately. An `MFArray` object, such as
418+
# `npf.k22` can be converted to a layered array as follows.
419+
420+
npf.k22.make_layered()
421+
422+
# By changing `npf.k22` to layered, we are then able to manage each layer
423+
# separately. Before doing so, however, we need to pass in data that can be
424+
# separated into three layers. An array of the correct size is one option.
425+
426+
shp = npf.k22.array.shape
427+
a = np.arange(shp[0] * shp[1] * shp[2]).reshape(shp)
428+
npf.k22 = a
429+
430+
# Now that `npf.k22` has been set to be layered, if we print information about
431+
# it, we see that each layer is stored separately, however, `npf.k22.array`
432+
# will still return a full three-dimensional array.
433+
434+
type(npf.k22)
435+
npf.k22
436+
437+
# We also see that each layer is printed separately to the npf
438+
# Package input file, and that the LAYERED keyword is activated:
439+
440+
npf.k22
441+
442+
# Working with a layered array provides lots of flexibility. For example,
443+
# constants can be set for some layers, but arrays for others:
444+
445+
npf.k22.set_data([1, a[2], 200])
446+
npf.k22
447+
448+
# The array can be interacted with as usual for NumPy arrays:
449+
npf.k22 = np.stack(
450+
[
451+
100 * np.ones((10, 10)),
452+
50 * np.ones((10, 10)),
453+
30 * np.ones((10, 10)),
454+
]
455+
)
456+
npf.k22
457+
458+
# ## Adding MODFLOW Stress Period Array Data
459+
# Transient array data spanning multiple stress periods must be specified as a
460+
# dictionary of arrays, where the dictionary key is the stress period,
461+
# expressed as a zero-based integer, and the dictionary value is the grid
462+
# data for that stress period.
463+
464+
# In the following example a `RCHA` package is created. First a dictionary
465+
# is created that contains recharge for the model's two stress periods.
466+
# Recharge is specified as a constant value in this example, though it could
467+
# also be specified as a 3-dimensional ndarray or list of lists.
468+
469+
rch_sp1 = 0.01
470+
rch_sp2 = 0.03
471+
rch_spd = {0: rch_sp1, 1: rch_sp2}
472+
473+
# The `RCHA` package is created and the dictionary constructed above is passed
474+
# in as the `recharge` parameter.
475+
476+
rch = flopy.mf6.ModflowGwfrcha(
477+
gwf, readasarrays=True, pname="rch", print_input=True, recharge=rch_spd
478+
)
479+
480+
# Below the `NPF` `k` array is retrieved using the various methods highlighted
481+
# above.
482+
483+
# First, view the `k` array.
484+
485+
npf.k
486+
487+
# `repr` gives a string representation of the data.
488+
489+
repr(npf.k)
490+
491+
# `str` gives a similar string representation of the data.
492+
493+
str(npf.k)
494+
495+
# Next, view the 4-dimensional array.
496+
497+
rch.recharge
498+
499+
# `repr` gives a string representation of the data.
500+
501+
repr(rch.recharge)
502+
503+
# str gives a similar representation of the data.
504+
505+
str(rch.recharge)

0 commit comments

Comments
 (0)