4.3.1. Python Modules

A Python module is used to replace the main program block of MODFLOW 6 for coupled simulation. This replacement permits the calling of modified Fortran routines to allow for interprocess communication with mHSP2.

4.3.1.1. pyMF6py.py

pyMF6, python-wrapped, MODFLOW 6 implementation.

This module provides the main entry point for running MODFLOW 6 and the main time loop through Python. To dynamically couple MODFLOW 6 to HSPF, need to be able to break into the time loop at the beginning of each day.

This Python module provides replacement functionality for the Fortran main program block of MODFLOW 6. There are two main program versions within this module. The user controls which version is run by executing ..\coupledMain.py or ..\standaloneMain.py. Both “mains” use functions within this module. Function saMF6TimeLoop is for standalone execution, and function MF6TimeLoop is for coupled (to HSPF) execution.

The message passing queue functions included in this module are only used when coupled mode execution is requested. Also the main block (if __name__ == main():) in this module is what is launcehd to start the independent process, spawned by the coupled controller and queue manager.

pyMF6py.AUTHKEY = b'authkey'

Authorization key for queue access.

This is not currently setup in a secure manner. If you plan on using pyHS2MF6 across a public or partially public network, then you should give some thought to the AUTHKEY and a means of securing this value. AUTHKEY needs to be encoded to a byte string.

pyMF6py.CLIENTHOST = '127.0.0.1'

Host machine for queue clients.

Clients are on the same ‘machine’ as the queue server when local descriptors like ‘localhost’ or ‘127.0.0.1’ are used.

pyMF6py.HOST = 'localhost'

Host machine for queue server.

Localhost means that the process are set-up to be on all the same machine. The queue server and the clients are in different processes on the same ‘machine’.

pyMF6py.MF6TimeLoop(qMF6, qHSP2)

Main MODFLOW6 time loop and all model setup for coupled simulation.

Currently have to run the simulation from the current directory and need to have all of the custom Python files in that directory as well.

Parameters
  • qMF6 (pyMF6py.Queue client) – queue for information from MODFLOW 6

  • qHSP2 (pyMF6py.Queue client) – queue for information from HSP2

Returns

function status; 0 == success

Return type

int

pyMF6py.MF_SIM_NAME = 'mfsim.nam'

Standard and fixed MODFLOW 6 simulation name

pyMF6py.NUM_CPL = 0

Number of cells per layer in the MODFLOW 6 model

pyMF6py.NUM_LAY = 0

Number of layers in the MODFLOW 6 model

pyMF6py.NUM_UZF = 0

Number of UZF cells in the MODFLOW 6 model

pyMF6py.PORT0 = 45492

Port number for the HSP2 queue.

Port numbers for queues need to be the same for each independent process for connection. Additionally, ports need to be opened in any firewall software even for local simulation.

pyMF6py.PORT1 = 45493

Port number for the MODFLOW 6 queue.

Port numbers for queues need to be the same for each independent process for connection. Additionally, ports need to be opened in any firewall software even for local simulation.

pyMF6py.PORT2 = 45494

Port number for global error handling and communications queue.

Port numbers for queues need to be the same for each independent process for connection. Additionally, ports need to be opened in any firewall software even for local simulation.

pyMF6py.QEND_MSG = ['End']

End of simulation message

pyMF6py.QINIT_MSG = ['Hello']

Queue intialization and checkin message

pyMF6py.QREADY_MSG = ['Ready']

Queue intialization and checkin message

pyMF6py.QUEUE_ERROR = ['Error']

Error message to put on queues for program termination

pyMF6py.QUEUE_TIMEOUT = 120.0

Queue wait timeout before error in seconds

This is for the from pyMF6 queue and determines the mHSP2 wait time for receipt from MODFLOW 6.

class pyMF6py.QueueManager(address=None, authkey=None, serializer='pickle', ctx=None)

Create the queue manager class.

Needs to be at top level of the module so that is pickleable. Do not need anything here except to subclass SyncManager.

pyMF6py.QueueServerClient(ThisHost, Porter, CustomAuth)

Get a client connection a queue.

The connection is bidirectional and can use this connection for both get and put.

Parameters
  • ThisHost (str) – host name. Should be ‘127.0.0.1’ for the same machine

  • Porter (int) – the port number to listen on

  • CustomAuth (str) – the custom authorization string

Returns

the client connection

Return type

pyMF6py.QueueManager

pyMF6py.SIM_DAYS = 0

Total number of days to be simulated

pyMF6py.SIM_DAYS_SERIES = None

Pandas date range series of days in date form to be simulated

pyMF6py.SIM_MONTHS = 0

Total number of months to be simulated

pyMF6py.SIM_MONTH_SERIES = None

Pandas date range series of months in date form to be simulated

pyMF6py.START_QUEUE_TO = 300.0

Queue wait timeout before error in seconds.

This is for program startup communications.

pyMF6py.TRACK_DIS_OUT = None

Numpy recarray with dimension NUM_CPL, SIM_MONTHS for tracking the discharge to ground surface sent to HSPF.

pyMF6py.TRACK_REJI_OUT = None

Numpy recarray with dimension NUM_CPL, SIM_MONTHS for tracking the rejected infiltration sent to HSPF.

pyMF6py.TRACK_UZF_IN = None

Numpy recarray with dimensions NUM_UZF, SIM_MONTHS for tracking the UZF inflows passed from HSPF

pyMF6py.WriteQueueCheckToLog(qMF6)

Write queue checks to log file

Parameters

qMF6 (pyMF6py.Queue) – the from MODFLOW6 queue

Returns

function status; 0 == success

Return type

int

pyMF6py.metaChecks(mf6root, cwd, start_dt, end_dt, num_lay, num_cpl, num_vert, num_uzf)

Check meta data items passed from the queue server as part of initial setup.

Uses flopy to implement these checks.

Parameters
  • mf6root (str) – MODFLOW 6 model root name

  • cwd (str) – MODFLOW 6 model directory

  • start_dt (dt.datetime) – starting date time

  • end_dt (dt.datetime) – ending date time

  • num_lay (int) – number of layers

  • num_cpl (int) – number of cells per layer

  • num_vert (int) – number of vertices per layer

  • num_uzf (int) – number of UZF cells in the model

Returns

function status; 0 == success

Return type

int

pyMF6py.outputTracking()

Output all of our tracking collections so that these can be post-processed if desired.

Output to four pickle files.

  1. ‘IndexDict.pickle’, dictionary, DI, with keys

    • ‘ncpl’ = NUM_CPL

    • ‘nuzf’ = NUM_UZF

    • ‘sim_day_index’ = SIM_DAYS_SERIES

    • ‘sim_month_index’ = SIM_MONTH_SERIES

  2. ‘UZF_from_HSPF.pickle’, TRACK_UZF_IN

  3. ‘GSURF_from_MF6.pickle’, TRACK_DIS_OUT

  4. ‘REJINF_from_MF6.pickle’, TRACK_REJI_OUT

Returns

function status; 0 == success

Return type

int

pyMF6py.processArrayComm(NpArray)

Process the array communication from MODFLOW 6

Parameters

NpArray (np.array) – the array of discharge to the surface from MODFLOW 6

Returns

function status; success == 0

Return type

int

pyMF6py.processInitMeta(PassList)

Process the initial metadata communication received from the queue server.

Call the various routines as required to check start time, end time, and grid specifications.

Parameters

PassList (list) –

list of metadata items - these are pickled and unpickled and following formats are required.

  1. (str): model directory

  2. (dt.datetime): start time

  3. (dt.datetime): end time

  4. (int): number of layers

  5. (int): number of cells per layer

  6. (int): number of vertices per layer

  7. (str): MODFLOW 6 root file name

  8. (int): number of uzf cells

Returns

FQDN path for MODFLOW 6 simulation files

Return type

str

pyMF6py.processReadyComm(StringList)

Process the ready communication with external processes

Parameters

StringList (list) – list of string items

Returns

function status; success == 0

Return type

int

pyMF6py.saMF6TimeLoop(simdir)

Main MODFLOW 6 time loop and all model setup for standalone simulation.

Parameters

simdir (str) – directory with simulation inputs

Returns

function status; 0 == success

Return type

int

pyMF6py.setupRecarrays(ncpl, nuzf, nmonths)

Three recarrays are used to track communications between HSPF and MODFLOW on a grid basis.

These recarrays are instantiated and initialized here

Parameters
  • ncpl (int) – number of computational cells in a layer, NCPL

  • nuzf (int) – number of UZF cells, NUZFCELLS

  • nmonths (int) – number of months to be simulated

Returns

function status; 0 == success

Return type

int

pyMF6py.updateDischargeRAs(curpTS, tArray)

Update the two rec arrays that track discharge to the ground surface

Parameters
  • curpTS (pd.Timestamp) – current simulation time/day

  • tArray (np.array, 2D, (2, NCPL)) – array that extracted from MODFLOW at the end of every time step

Returns

function status; 0 == success

Return type

int

pyMF6py.updateUZFRAs(curpTS, rArray)

Update the rec array that tracks inputs from HSPF.

Parameters
  • curpTS (pd.Timestamp) – current simulation time/day

  • rArray (np.array, 1D, NUZF) – array that passed from HSPF after checking

Returns

function status; 0 == success

Return type

int

4.3.1.2. pyMFLogger.py

pyMF6 custom logger leveraging Python logging

Provides specification and configuration of Python’s logging API to use for debugging and informational purposes.

A log file, specified with LOGNAME, is used for receipt of log statements. Logging level is set with LOG_LEVEL.

pyMF6Logger.FH = None

File handler

pyMF6Logger.FORMATTER = <logging.Formatter object>

Custom formatter

pyMF6Logger.LOGNAME = 'pyMF6_Log.txt'

Log file name

pyMF6Logger.LOGR = <Logger pyMF6 (DEBUG)>

Custom logger

pyMF6Logger.LOG_LEVEL = 10

Logging level

pyMF6Logger.START_TIME = None

MODFLOW 6 model start time

pyMF6Logger.loggerEnd()

End the pyMF6 logger

pyMF6Logger.loggerStart(LFPath)

Start the logger to use for pyMF6