|
| 1 | +# # TWRI |
| 2 | +# |
| 3 | +# Import dependencies. |
| 4 | + |
| 5 | +from pathlib import Path |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import xarray as xr |
| 9 | + |
| 10 | +import flopy4 |
| 11 | + |
| 12 | +# Timing |
| 13 | +time = flopy4.mf6.utils.time.Time.from_timestamps( |
| 14 | + ["2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04"] |
| 15 | +) |
| 16 | +nper = time.nper |
| 17 | + |
| 18 | +# Grid |
| 19 | +nlay = 5 |
| 20 | +nrow = 15 |
| 21 | +ncol = 15 |
| 22 | +shape = (nlay, nrow, ncol) |
| 23 | +nodes = np.prod(shape) |
| 24 | +delr = np.ones((ncol,)) * 5000.0 |
| 25 | +delc = np.ones((nrow,)) * 5000.0 |
| 26 | +idomain = np.ones(shape, dtype=int) |
| 27 | +top = np.ones((nrow, ncol), dtype=float) * 200.0 |
| 28 | +bottom = np.stack([np.full((nrow, ncol), val) for val in [-150.0, -200.0, -300.0, -350.0, -450.0]]) |
| 29 | +grid = flopy4.mf6.utils.grid.StructuredGrid( |
| 30 | + nlay=nlay, nrow=nrow, ncol=ncol, top=top, botm=bottom, delr=delr, delc=delc, idomain=idomain |
| 31 | +) |
| 32 | +dims = {"nper": nper, **dict(grid.dataset.sizes)} # TODO: temporary |
| 33 | + |
| 34 | +# Grid discretization |
| 35 | +dis = flopy4.mf6.gwf.Dis.from_grid(grid=grid) |
| 36 | + |
| 37 | +# Constant head boundary on the left |
| 38 | +chd = flopy4.mf6.gwf.Chd( |
| 39 | + head={"*": {(k, i, 0): 0.0 for k in range(nlay) for i in range(nrow)}}, |
| 40 | + print_input=True, |
| 41 | + print_flows=True, |
| 42 | + save_flows=True, |
| 43 | + dims=dims, |
| 44 | +) |
| 45 | + |
| 46 | +# Drain in the center left of the model |
| 47 | +elevation = [0.0, 0.0, 10.0, 20.0, 30.0, 50.0, 70.0, 90.0, 100.0] |
| 48 | +conductance = 1.0 |
| 49 | +drn = flopy4.mf6.gwf.Drn( |
| 50 | + elev={"*": {(0, 7, j): elevation[j] for j in range(9)}}, |
| 51 | + cond={"*": {(0, 7, j): conductance for j in range(9)}}, |
| 52 | + print_input=True, |
| 53 | + print_flows=True, |
| 54 | + save_flows=True, |
| 55 | + dims=dims, |
| 56 | +) |
| 57 | + |
| 58 | +# Initial conditions |
| 59 | +ic = flopy4.mf6.gwf.Ic(strt=0.0, dims=dims) |
| 60 | + |
| 61 | +# Node properties |
| 62 | +icelltype = np.stack([np.full((nrow, ncol), val) for val in [1, 0, 0, 0, 0]]) |
| 63 | +k = np.stack([np.full((nrow, ncol), val) for val in [1.0e-3, 1.0e-8, 1.0e-4, 5.0e-7, 2.0e-4]]) |
| 64 | +k33 = np.stack([np.full((nrow, ncol), val) for val in [1.0e-3, 1.0e-8, 1.0e-4, 5.0e-7, 2.0e-4]]) |
| 65 | +npf = flopy4.mf6.gwf.Npf( |
| 66 | + # TODO: no need for reshaping once array structuring converter is done |
| 67 | + icelltype=icelltype.reshape((nodes,)), |
| 68 | + k=k.reshape((nodes,)), |
| 69 | + k33=k33.reshape((nodes,)), |
| 70 | + cvoptions=flopy4.mf6.gwf.Npf.CvOptions(variablecv=True, dewatered=True), |
| 71 | + perched=True, |
| 72 | + save_flows=True, |
| 73 | + dims=dims, |
| 74 | +) |
| 75 | + |
| 76 | +# Storage |
| 77 | +sto = flopy4.mf6.gwf.Sto( |
| 78 | + storagecoefficient=False, |
| 79 | + ss=1.0e-5, |
| 80 | + sy=0.15, |
| 81 | + transient={"*": False}, |
| 82 | + iconvert=0, |
| 83 | + dims=dims, |
| 84 | +) |
| 85 | + |
| 86 | +# Uniform recharge on the top layer |
| 87 | +rch_rate = xr.full_like(grid.idomain.sel(k=1), 3.0e-8, dtype=float) |
| 88 | +rch = flopy4.mf6.gwf.Rch(recharge=rch_rate, dims=dims) |
| 89 | + |
| 90 | +# Output control |
| 91 | +# TODO: show both ways to set up the Oc package, strings |
| 92 | +# and proper record types? and/or with perioddata param? |
| 93 | +oc = flopy4.mf6.gwf.Oc(save_head="all", save_budget="all", dims=dims) |
| 94 | + |
| 95 | +# Wells scattered throughout the model |
| 96 | +wel_q = -5.0 |
| 97 | +wel_nodes = [ |
| 98 | + [3, 4, 10, -5.0], |
| 99 | + [2, 3, 5, -5.0], |
| 100 | + [2, 5, 11, -5.0], |
| 101 | + [0, 8, 7, -5.0], |
| 102 | + [0, 8, 9, -5.0], |
| 103 | + [0, 8, 11, -5.0], |
| 104 | + [0, 8, 13, -5.0], |
| 105 | + [0, 10, 7, -5.0], |
| 106 | + [0, 10, 9, -5.0], |
| 107 | + [0, 10, 11, -5.0], |
| 108 | + [0, 10, 13, -5.0], |
| 109 | + [0, 12, 7, -5.0], |
| 110 | + [0, 12, 9, -5.0], |
| 111 | + [0, 12, 11, -5.0], |
| 112 | + [0, 12, 13, -5.0], |
| 113 | +] |
| 114 | +wel = flopy4.mf6.gwf.Wel( |
| 115 | + q={"*": {(layer, row, col): wel_q for layer, row, col, wel_q in wel_nodes}}, |
| 116 | + dims=dims, |
| 117 | +) |
| 118 | + |
| 119 | +# Flow model |
| 120 | +gwf = flopy4.mf6.gwf.Gwf( |
| 121 | + dis=grid, |
| 122 | + chd=chd, |
| 123 | + ic=ic, |
| 124 | + npf=npf, |
| 125 | + sto=sto, |
| 126 | + oc=oc, |
| 127 | + rch=rch, |
| 128 | + wel=wel, |
| 129 | + drn=drn, |
| 130 | + dims=dims, |
| 131 | +) |
| 132 | + |
| 133 | +# Solver |
| 134 | +ims = flopy4.mf6.Ims( |
| 135 | + print_option="summary", |
| 136 | + outer_dvclose=1.0e-4, |
| 137 | + outer_maximum=500, |
| 138 | + under_relaxation=None, |
| 139 | + inner_dvclose=1.0e-4, |
| 140 | + inner_rclose=0.001, |
| 141 | + inner_maximum=100, |
| 142 | + linear_acceleration="cg", |
| 143 | + scaling_method=None, |
| 144 | + reordering_method=None, |
| 145 | + relaxation_factor=0.97, |
| 146 | +) |
| 147 | + |
| 148 | +# TDIS |
| 149 | +tdis = flopy4.mf6.simulation.Tdis.from_time(time) |
| 150 | + |
| 151 | +# Create workspace |
| 152 | +workspace = Path(__file__).parent / "twri" |
| 153 | +workspace.mkdir(parents=True, exist_ok=True) |
| 154 | + |
| 155 | +# Create simulation |
| 156 | +sim = flopy4.mf6.simulation.Simulation( |
| 157 | + name="twri", |
| 158 | + tdis=tdis, |
| 159 | + models={"gwf": gwf}, |
| 160 | + solutions={"ims": ims}, |
| 161 | + workspace=workspace, |
| 162 | +) |
| 163 | + |
| 164 | +# Write input files and run the simulation |
| 165 | +sim.write() |
| 166 | +sim.run() # assumes the ``mf6`` executable is available on your PATH. |
| 167 | + |
| 168 | +# Load head results |
| 169 | +gwf_ws = Path(workspace) / gwf.name |
| 170 | +head = flopy4.mf6.utils.open_hds( |
| 171 | + gwf_ws / f"{gwf.name}.hds", |
| 172 | + gwf_ws / f"{dis.name}.dis.grb", |
| 173 | +) |
| 174 | + |
| 175 | +# Plot head results |
| 176 | +head.isel(layer=0, time=0).plot.contourf() |
0 commit comments