Skip to content

Commit 8764d77

Browse files
mjrenomjreno
authored andcommitted
draft netcdf tutorial
1 parent ff6b824 commit 8764d77

File tree

2 files changed

+283
-2
lines changed

2 files changed

+283
-2
lines changed
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
import sys
2+
from pathlib import Path
3+
from tempfile import TemporaryDirectory
4+
5+
import numpy as np
6+
import xarray as xr
7+
8+
import flopy
9+
10+
print(sys.version)
11+
print(f"flopy version: {flopy.__version__}")
12+
13+
DNODATA = 3.0e30
14+
15+
16+
def create_sim(ws):
17+
name = "uzf01"
18+
perlen = [500.0]
19+
nper = len(perlen)
20+
nstp = [10]
21+
tsmult = nper * [1.0]
22+
crs = "EPSG:26916"
23+
nlay, nrow, ncol = 100, 1, 1
24+
delr = 1.0
25+
delc = 1.0
26+
delv = 1.0
27+
top = 100.0
28+
botm = [top - (k + 1) * delv for k in range(nlay)]
29+
strt = 0.5
30+
hk = 1.0
31+
laytyp = 1
32+
ss = 0.0
33+
sy = 0.1
34+
35+
tdis_rc = []
36+
for i in range(nper):
37+
tdis_rc.append((perlen[i], nstp[i], tsmult[i]))
38+
39+
# build MODFLOW 6 files
40+
sim = flopy.mf6.MFSimulation(
41+
sim_name=name, version="mf6", exe_name="mf6", sim_ws=ws
42+
)
43+
44+
# create tdis package
45+
tdis = flopy.mf6.ModflowTdis(sim, time_units="DAYS", nper=nper, perioddata=tdis_rc)
46+
47+
# create iterative model solution and register the gwf model with it
48+
nouter, ninner = 100, 10
49+
hclose, rclose, relax = 1.5e-6, 1e-6, 0.97
50+
imsgwf = flopy.mf6.ModflowIms(
51+
sim,
52+
print_option="SUMMARY",
53+
outer_dvclose=hclose,
54+
outer_maximum=nouter,
55+
under_relaxation="DBD",
56+
under_relaxation_theta=0.7,
57+
inner_maximum=ninner,
58+
inner_dvclose=hclose,
59+
rcloserecord=rclose,
60+
linear_acceleration="BICGSTAB",
61+
scaling_method="NONE",
62+
reordering_method="NONE",
63+
relaxation_factor=relax,
64+
)
65+
66+
# create gwf model
67+
newtonoptions = "NEWTON UNDER_RELAXATION"
68+
gwf = flopy.mf6.ModflowGwf(
69+
sim,
70+
modelname=name,
71+
newtonoptions=newtonoptions,
72+
save_flows=True,
73+
)
74+
75+
dis = flopy.mf6.ModflowGwfdis(
76+
gwf,
77+
crs=crs,
78+
nlay=nlay,
79+
nrow=nrow,
80+
ncol=ncol,
81+
delr=delr,
82+
delc=delc,
83+
top=top,
84+
botm=botm,
85+
idomain=np.ones((nlay, nrow, ncol), dtype=int),
86+
)
87+
88+
# initial conditions
89+
ic = flopy.mf6.ModflowGwfic(gwf, strt=strt)
90+
91+
# node property flow
92+
npf = flopy.mf6.ModflowGwfnpf(gwf, save_flows=False, icelltype=laytyp, k=hk)
93+
# storage
94+
sto = flopy.mf6.ModflowGwfsto(
95+
gwf,
96+
save_flows=False,
97+
iconvert=laytyp,
98+
ss=ss,
99+
sy=sy,
100+
steady_state={0: False},
101+
transient={0: True},
102+
)
103+
104+
# ghbg
105+
ghb_obs = {f"{name}.ghb.obs.csv": [("100_1_1", "GHB", (99, 0, 0))]}
106+
bhead = np.full(nlay * nrow * ncol, DNODATA, dtype=float)
107+
cond = np.full(nlay * nrow * ncol, DNODATA, dtype=float)
108+
bhead[nlay - 1] = 1.5
109+
cond[nlay - 1] = 1.0
110+
ghb = flopy.mf6.ModflowGwfghbg(
111+
gwf,
112+
print_input=True,
113+
print_flows=True,
114+
bhead=bhead,
115+
cond=cond,
116+
save_flows=False,
117+
)
118+
119+
ghb.obs.initialize(
120+
filename=f"{name}.ghb.obs",
121+
digits=20,
122+
print_input=True,
123+
continuous=ghb_obs,
124+
)
125+
126+
# note: for specifying lake number, use fortran indexing!
127+
uzf_obs = {
128+
f"{name}.uzf.obs.csv": [
129+
("wc 02", "water-content", 2, 0.5),
130+
("wc 50", "water-content", 50, 0.5),
131+
("wcbn 02", "water-content", "uzf 002", 0.5),
132+
("wcbn 50", "water-content", "UZF 050", 0.5),
133+
("rch 02", "uzf-gwrch", "uzf 002"),
134+
("rch 50", "uzf-gwrch", "uzf 050"),
135+
]
136+
}
137+
138+
sd = 0.1
139+
vks = hk
140+
thtr = 0.05
141+
thti = thtr
142+
thts = sy
143+
eps = 4
144+
uzf_pkdat = [[0, (0, 0, 0), 1, 1, sd, vks, thtr, thts, thti, eps, "uzf 001"]] + [
145+
[k, (k, 0, 0), 0, k + 1, sd, vks, thtr, thts, thti, eps, f"uzf {k + 1:03d}"]
146+
for k in range(1, nlay - 1)
147+
]
148+
uzf_pkdat[-1][3] = -1
149+
infiltration = 2.01
150+
uzf_spd = {0: [[0, infiltration, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]}
151+
uzf = flopy.mf6.ModflowGwfuzf(
152+
gwf,
153+
print_input=True,
154+
print_flows=True,
155+
save_flows=True,
156+
boundnames=True,
157+
ntrailwaves=15,
158+
nwavesets=40,
159+
nuzfcells=len(uzf_pkdat),
160+
packagedata=uzf_pkdat,
161+
perioddata=uzf_spd,
162+
budget_filerecord=f"{name}.uzf.bud",
163+
budgetcsv_filerecord=f"{name}.uzf.bud.csv",
164+
observations=uzf_obs,
165+
filename=f"{name}.uzf",
166+
)
167+
168+
# output control
169+
oc = flopy.mf6.ModflowGwfoc(
170+
gwf,
171+
budget_filerecord=f"{name}.bud",
172+
head_filerecord=f"{name}.hds",
173+
headprintrecord=[("COLUMNS", 10, "WIDTH", 15, "DIGITS", 6, "GENERAL")],
174+
saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")],
175+
printrecord=[("HEAD", "LAST"), ("BUDGET", "ALL")],
176+
)
177+
178+
obs_lst = []
179+
obs_lst.append(["obs1", "head", (0, 0, 0)])
180+
obs_lst.append(["obs2", "head", (1, 0, 0)])
181+
obs_dict = {f"{name}.obs.csv": obs_lst}
182+
obs = flopy.mf6.ModflowUtlobs(gwf, pname="head_obs", digits=20, continuous=obs_dict)
183+
184+
return sim
185+
186+
187+
temp_dir = TemporaryDirectory()
188+
workspace = Path(temp_dir.name)
189+
190+
# run the non-netcdf simulation
191+
sim = create_sim(ws=workspace)
192+
sim.write_simulation()
193+
success, buff = sim.run_simulation(silent=True, report=True)
194+
if success:
195+
for line in buff:
196+
print(line)
197+
else:
198+
raise ValueError("Failed to run.")
199+
200+
# create directory for netcdf sim
201+
sim.set_sim_path(workspace / "netcdf")
202+
gwf = sim.get_model("uzf01")
203+
gwf.name_file.nc_filerecord = "uzf01.structured.nc"
204+
sim.write_simulation()
205+
206+
# create the netcdf dataset
207+
ds = xr.Dataset()
208+
209+
# get model netcdf info
210+
ds_info = gwf.netcdf_info()
211+
212+
# update dataset with required attributes
213+
for a in ds_info["attrs"]:
214+
ds.attrs[a] = ds_info["attrs"][a]
215+
216+
# set dimensional info
217+
dis = gwf.modelgrid
218+
xoff = dis.xoffset
219+
yoff = dis.yoffset
220+
x = xoff + dis.xycenters[0]
221+
y = yoff + dis.xycenters[1]
222+
z = [float(x) for x in range(1, dis.nlay + 1)]
223+
nstp = sum(gwf.modeltime.nstp)
224+
time = gwf.modeltime.tslen
225+
226+
# create coordinate vars
227+
var_d = {"time": (["time"], time), "z": (["z"], z), "y": (["y"], y), "x": (["x"], x)}
228+
ds = ds.assign(var_d)
229+
230+
# get ghbg package netcdf info
231+
ghbg = gwf.get_package("ghbg_0")
232+
ghbg_info = ghbg.netcdf_info()
233+
234+
# create ghbg dataset variables
235+
shape = nc_shape = ["time", "z", "y", "x"]
236+
for v in ghbg_info:
237+
varname = ghbg_info[v]["varname"]
238+
data = np.full((nstp, dis.nlay, dis.nrow, dis.ncol), DNODATA, dtype=float)
239+
var_d = {varname: (shape, data)}
240+
ds = ds.assign(var_d)
241+
for a in ghbg_info[v]["attrs"]:
242+
ds[varname].attrs[a] = ghbg_info[v]["attrs"][a]
243+
ds[varname].attrs["_FillValue"] = DNODATA
244+
245+
# update bhead netcdf array from flopy perioddata
246+
for p in ghbg.bhead.get_data():
247+
ds["ghbg_0_bhead"].values[p] = ghbg.bhead.get_data()[p]
248+
249+
# update cond netcdf array from flopy perioddata
250+
for p in ghbg.cond.get_data():
251+
ds["ghbg_0_cond"].values[p] = ghbg.cond.get_data()[p]
252+
253+
# write the netcdf
254+
ds.to_netcdf(
255+
workspace / "netcdf/uzf01.structured.nc", format="NETCDF4", engine="netcdf4"
256+
)
257+
258+
# update ghbg input to read bhead and cond from netcdf
259+
with open(workspace / "netcdf/uzf01.ghbg", "w") as f:
260+
f.write("BEGIN options\n")
261+
f.write(" READARRAYGRID\n")
262+
f.write(" PRINT_INPUT\n")
263+
f.write(" PRINT_FLOWS\n")
264+
f.write(" OBS6 FILEIN uzf01.ghb.obs\n")
265+
f.write("END options\n\n")
266+
f.write("BEGIN period 1\n")
267+
f.write(" bhead NETCDF\n")
268+
f.write(" cond NETCDF\n")
269+
f.write("END period 1\n")
270+
271+
# run the netcdf sim
272+
success, buff = sim.run_simulation(silent=False, report=True)
273+
if success:
274+
for line in buff:
275+
print(line)
276+
else:
277+
raise ValueError("Failed to run.")

flopy/mf6/mfpackage.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3485,15 +3485,19 @@ def netcdf_attrs(mtype, ptype, auxiliary=None, mesh=None, nlay=1):
34853485

34863486
if ptype.lower() in sim_struct.package_struct_objs:
34873487
pso = sim_struct.package_struct_objs[ptype.lower()]
3488+
if pso.multi_package_support:
3489+
pname = f"<{ptype}name>"
3490+
else:
3491+
pname = ptype
34883492
for key, block in pso.blocks.items():
34893493
if key != "griddata" and key != "period":
34903494
continue
34913495
for d in block.data_structures:
34923496
if block.data_structures[d].netcdf:
34933497
MFPackage._add_netcdf_entries(
34943498
attrs,
3495-
mtype,
3496-
ptype,
3499+
f"<{mtype}name>",
3500+
pname,
34973501
block.data_structures[d],
34983502
auxiliary,
34993503
mesh,

0 commit comments

Comments
 (0)