In [ ]:
import pyemu
import os, shutil
import pandas as pd
import flopy
import numpy as np
import platform

This notebook is designed to show high-level PST file generation and manipulation examples, and also highlight some of the steps that Schur object functions perform in a bit more detail

The assumption is that a user has a model that flopy can grok. That's all we need, using the BOSS PEST-ification techniques pyemu has.


In [ ]:
nam_file = "freyberg.nam"
org_model_ws = "freyberg_sfr_update"
new_model_ws = "pest_setup"

# load the model, change dir and run once to get a hydmod output file and list file
m = flopy.modflow.Modflow.load(nam_file,model_ws=org_model_ws,check=False)
m.change_model_ws("temp",reset_external=True)
m.name = nam_file.split(".")[0]

# # let's just retain the calibration data for now by trimming HYDMOD
# hyd = m.get_package('hyd')
# hyddf = pd.DataFrame(hyd.obsdata)
# #hyddf = hyddf.loc[[True if 'cr' in i.decode() else False for i in hyddf.hydlbl]]
# hyd.obsdata = hyddf.to_records(index=False).astype(hyd.obsdata.dtype)
# hyd.nhyd = len(hyd.obsdata)
m.exe_name = 'mfnwt'
m.write_input()

EXE_DIR = os.path.join("..","bin")
if "window" in platform.platform().lower():
    EXE_DIR = os.path.join(EXE_DIR,"win")
elif "darwin" in platform.platform().lower():
    EXE_DIR = os.path.join(EXE_DIR,"mac")
else:
    EXE_DIR = os.path.join(EXE_DIR,"linux")

[shutil.copy2(os.path.join(EXE_DIR,f),os.path.join('temp',f)) for f in os.listdir(EXE_DIR)]

pyemu.helpers.run('{0} {1}'.format(m.exe_name,nam_file), cwd='temp')

Let's make some parameters. How about zones for HK and a constants for SY and RCH?


In [ ]:
zn_array = np.loadtxt(os.path.join("Freyberg_Truth","hk.zones"))
k_zone_dict = {k:zn_array for k in range(m.nlay)}
const_props = []
const_props.append(["upw.sy", None])
const_props.append(["rch.rech",None])
zone_props = [['upw.hk',0]]

Maybe we ought to also treat well pumping as an uncertain parameter


In [ ]:
bc_props = [["wel.flux",None]]

In [ ]:
mfp = pyemu.helpers.PstFromFlopyModel(m,new_model_ws="schur_test",zone_props=zone_props,
                                          const_props=const_props,k_zone_dict=k_zone_dict,
                                          remove_existing=True,temporal_bc_props=bc_props)
EXE_DIR = os.path.join("..","bin")
if "window" in platform.platform().lower():
    EXE_DIR = os.path.join(EXE_DIR,"win")
elif "darwin" in platform.platform().lower():
    EXE_DIR = os.path.join(EXE_DIR,"mac")
else:
    EXE_DIR = os.path.join(EXE_DIR,"linux")

[shutil.copy2(os.path.join(EXE_DIR,f),os.path.join("schur_test",f)) for f in os.listdir(EXE_DIR)]

Cool - let's take a quick look at the PST control file this made


In [ ]:
inpst = mfp.pst

In [ ]:
inpst.parameter_data

Looks like we have multipliers on our zones. Let's set noptmax to -1 to calculate a Jacobian matrix and then see what pyemu functionality we can light up

First, though, we can report all the control data to see noptmax and everything else


In [ ]:
inpst.control_data.formatted_values

In [ ]:
inpst.control_data.noptmax=-1
inpst.write(os.path.join('schur_test','freyberg_pest.pst'))

In [ ]:
num_workers = 5
pyemu.helpers.start_workers('schur_test','pestpp-glm','freyberg_pest.pst',
                            num_workers=num_workers,master_dir="master_glm")

Now, once we have a Jacobian matrix, we can create a posterior Schur complement object. If we don't pass an explicit covariance matrix, the object will use parameter bounds for the prior variance (diagonal matrix) for parameters (also note that if no .pst file was passed, pyemu will look for one with the same root name in the same location as the .jco file that was passed.


In [ ]:
sc = pyemu.Schur(os.path.join('master_glm','freyberg_pest.jcb'),verbose=True)

In [ ]:
sc.get_parameter_summary()

In [ ]:
sc.jco.col_names

In [ ]:


In [ ]: