Source code for flopy.mf6.utils.generate_classes

import os
import shutil
import tempfile
import time
from warnings import warn

from .createpackages import create_packages

thisfilepath = os.path.dirname(os.path.abspath(__file__))
flopypth = os.path.join(thisfilepath, "..", "..")
flopypth = os.path.abspath(flopypth)
protected_dfns = ["flopy.dfn"]

default_owner = "MODFLOW-USGS"
default_repo = "modflow6"


[docs]def delete_files(files, pth, allow_failure=False, exclude=None): if exclude is None: exclude = [] else: if not isinstance(exclude, list): exclude = [exclude] for fn in files: if fn in exclude: continue fpth = os.path.join(pth, fn) try: print(f" removing...{fn}") os.remove(fpth) except: print(f"could not remove...{fn}") if not allow_failure: return False return True
[docs]def list_files(pth, exts=["py"]): print(f"\nLIST OF FILES IN {pth}") files = [ entry for entry in os.listdir(pth) if os.path.isfile(os.path.join(pth, entry)) ] idx = 0 for fn in files: ext = os.path.splitext(fn)[1][1:].lower() if ext in exts: idx += 1 print(f" {idx:5d} - {fn}")
[docs]def download_dfn(owner, repo, ref, new_dfn_pth): try: from modflow_devtools.download import download_and_unzip except ImportError: raise ImportError( "The modflow-devtools package must be installed in order to " "generate the MODFLOW 6 classes. This can be with:\n" " pip install modflow-devtools" ) mf6url = f"https://github.com/{owner}/{repo}/archive/{ref}.zip" print(f" Downloading MODFLOW 6 repository from {mf6url}") with tempfile.TemporaryDirectory() as tmpdirname: dl_path = download_and_unzip(mf6url, tmpdirname, verbose=True) dl_contents = list(dl_path.glob("modflow6-*")) proj_path = next(iter(dl_contents), None) if not proj_path: raise ValueError( f"Could not find modflow6 project dir in {dl_path}: found {dl_contents}" ) downloaded_dfn_pth = os.path.join( proj_path, "doc", "mf6io", "mf6ivar", "dfn" ) shutil.copytree(downloaded_dfn_pth, new_dfn_pth)
[docs]def backup_existing_dfns(flopy_dfn_path): parent_folder = os.path.dirname(flopy_dfn_path) timestr = time.strftime("%Y%m%d-%H%M%S") backup_folder = os.path.join(parent_folder, "dfn_backup", timestr) shutil.copytree(flopy_dfn_path, backup_folder) assert os.path.isdir( backup_folder ), f"dfn backup files not found: {backup_folder}"
[docs]def replace_dfn_files(new_dfn_pth, flopy_dfn_path): # remove the old files, unless the file is protected filenames = os.listdir(flopy_dfn_path) delete_files(filenames, flopy_dfn_path, exclude=protected_dfns) # copy the new ones into the folder filenames = os.listdir(new_dfn_pth) for filename in filenames: filename_w_path = os.path.join(new_dfn_pth, filename) print(f" copying..{filename}") shutil.copy(filename_w_path, flopy_dfn_path)
[docs]def delete_mf6_classes(): pth = os.path.join(flopypth, "mf6", "modflow") files = [ entry for entry in os.listdir(pth) if os.path.isfile(os.path.join(pth, entry)) ] delete_files(files, pth)
[docs]def generate_classes( owner=default_owner, repo=default_repo, branch=None, ref="master", dfnpath=None, backup=True, ): """ Generate the MODFLOW 6 flopy classes using definition files from the MODFLOW 6 GitHub repository or a set of definition files in a folder provided by the user. Parameters ---------- owner : str, default "MODFLOW-USGS" Owner of the MODFLOW 6 repository to use to update the definition files and generate the MODFLOW 6 classes. repo : str, default "modflow6" Name of the MODFLOW 6 repository to use to update the definition. branch : str, optional Branch name of the MODFLOW 6 repository to use to update the definition files and generate the MODFLOW 6 classes. .. deprecated:: 3.5.0 Use ref instead. ref : str, default "master" Branch name, tag, or commit hash to use to update the definition. dfnpath : str Path to a definition file folder that will be used to generate the MODFLOW 6 classes. Default is none, which means that the branch will be used instead. dfnpath will take precedence over branch if dfnpath is specified. backup : bool, default True Keep a backup of the definition files in dfn_backup with a date and timestamp from when the definition files were replaced. """ # print header print(2 * "\n") print(72 * "*") print("Updating the flopy MODFLOW 6 classes") flopy_dfn_path = os.path.join(flopypth, "mf6", "data", "dfn") # download the dfn files and put them in flopy.mf6.data or update using # user provided dfnpath if dfnpath is None: timestr = time.strftime("%Y%m%d-%H%M%S") new_dfn_pth = os.path.join(flopypth, "mf6", "data", f"dfn_{timestr}") # branch deprecated 3.5.0 if not ref and not branch: raise ValueError("branch or ref must be provided") if branch: warn("branch is deprecated, use ref instead", DeprecationWarning) ref = branch print(f" Updating the MODFLOW 6 classes using {owner}/{repo}/{ref}") download_dfn(owner, repo, ref, new_dfn_pth) else: print(f" Updating the MODFLOW 6 classes using {dfnpath}") assert os.path.isdir(dfnpath) new_dfn_pth = dfnpath if backup: print(f" Backup existing definition files in: {flopy_dfn_path}") backup_existing_dfns(flopy_dfn_path) print(" Replacing existing definition files with new ones.") replace_dfn_files(new_dfn_pth, flopy_dfn_path) if dfnpath is None: shutil.rmtree(new_dfn_pth) print(" Deleting existing mf6 classes.") delete_mf6_classes() print(" Create mf6 classes using the downloaded definition files.") create_packages() list_files(os.path.join(flopypth, "mf6", "modflow"))
[docs]def cli_main(): """Command-line interface for generate_classes().""" import argparse import sys parser = argparse.ArgumentParser( description=generate_classes.__doc__.split("\n\n")[0], ) parser.add_argument( "--owner", type=str, default=default_owner, help=f"GitHub repository owner; default is '{default_owner}'.", ) parser.add_argument( "--repo", default=default_repo, help=f"Name of GitHub repository; default is '{default_repo}'.", ) parser.add_argument( "--ref", default="master", help="Branch name, tag, or commit hash to use to update the " "definition; default is 'master'.", ) parser.add_argument( "--dfnpath", help="Path to a definition file folder that will be used to generate " "the MODFLOW 6 classes.", ) parser.add_argument( "--no-backup", action="store_true", help="Set to disable backup. " "Default behavior is to keep a backup of the definition files in " "dfn_backup with a date and timestamp from when the definition " "files were replaced.", ) args = vars(parser.parse_args()) # Handle flipped logic args["backup"] = not args.pop("no_backup") try: generate_classes(**args) except (EOFError, KeyboardInterrupt): sys.exit(f" cancelling '{sys.argv[0]}'")
if __name__ == "__main__": """Run command-line with: python -m flopy.mf6.utils.generate_classes""" cli_main()