def test_parse_modflowgwf_kwargs(shellmound_cfg): cfg = deepcopy(shellmound_cfg) cfg = MF6model._parse_model_kwargs(cfg) kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') m = MF6model(cfg=cfg, **kwargs) sim_path = os.path.normpath( m.simulation.simulation_data.mfpath._sim_path).lower() assert sim_path == cfg['simulation']['sim_ws'].lower() m.write() # verify that options were written correctly to namefile # newton solver, but without underrelaxation nampath = os.path.join(m.model_ws, m.model_nam_file) options = read_mf6_block(nampath, 'options') assert os.path.normpath(options['list'][0]).lower() == \ os.path.normpath(cfg['model']['list']).lower() for option in ['print_input', 'print_flows', 'save_flows']: if cfg['model'][option]: assert option in options assert len(options['newton']) == 0 # newton solver, with underrelaxation cfg['model']['options']['newton_under_relaxation'] = True cfg = MF6model._parse_model_kwargs(cfg) assert cfg['model']['options']['newtonoptions'] == ['under_relaxation'] kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') m = MF6model(cfg=cfg, **kwargs) m.write() options = read_mf6_block(nampath, 'options') assert options['newton'] == ['under_relaxation']
def test_init(shellmound_cfg): cfg = deepcopy(shellmound_cfg) sim = mf6.MFSimulation() assert isinstance(sim, mf6.MFSimulation) kwargs = get_input_arguments(cfg['simulation'], mf6.MFSimulation, warn=False) sim = mf6.MFSimulation(**kwargs) assert isinstance(sim, mf6.MFSimulation) cfg['model']['packages'] = [] cfg['model']['simulation'] = sim cfg = MF6model._parse_model_kwargs(cfg) kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') # test initialization with no packages m = MF6model(cfg=cfg, **kwargs) assert isinstance(m, MF6model) # test initialization with no arguments m = MF6model(simulation=sim) assert isinstance(m, MF6model)
def test_packagelist(shellmound_cfg_path): cfg = load_cfg(shellmound_cfg_path, default_file='/mf6_defaults.yml') packages = cfg['model']['packages'] kwargs = get_input_arguments(cfg['simulation'], mf6.MFSimulation) sim = mf6.MFSimulation(**kwargs) cfg['model']['simulation'] = sim cfg = MF6model._parse_model_kwargs(cfg) kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') m = MF6model(cfg=cfg, **kwargs) assert m.package_list == [p for p in m._package_setup_order if p in packages]
def test_rotated_grid(shellmound_cfg, shellmound_simulation): cfg = deepcopy(shellmound_cfg) #simulation = deepcopy(simulation) cfg['model']['simulation'] = shellmound_simulation cfg['setup_grid']['snap_to_NHG'] = False cfg['setup_grid']['rotation'] = 18. cfg['setup_grid']['xoff'] += 8000 cfg['dis']['dimensions']['nrow'] = 20 cfg['dis']['dimensions']['ncol'] = 25 cfg = MF6model._parse_model_kwargs(cfg) kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') m = MF6model(cfg=cfg, **kwargs) m.setup_grid() assert m.modelgrid.angrot == 18. assert m.modelgrid.xoffset == cfg['setup_grid']['xoff'] assert m.modelgrid.yoffset == cfg['setup_grid']['yoff'] m.setup_dis() #m.setup_tdis() #m.setup_solver() #m.setup_packages(reset_existing=False) #m.write_input() j = 2
def get_pleasant_mf6(pleasant_mf6_cfg, pleasant_simulation): print('creating Pleasant Lake MF6model instance from cfgfile...') cfg = copy.deepcopy(pleasant_mf6_cfg) cfg['model']['simulation'] = pleasant_simulation kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') m = MF6model(cfg=cfg, **kwargs) return m
def setup_dis(self): """""" package = 'dis' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # resample the top from the DEM if self.cfg['dis']['remake_top']: self._setup_array(package, 'top', datatype='array2d', resample_method='linear', write_fmt='%.2f') # make the botm array self._setup_array(package, 'botm', datatype='array3d', resample_method='linear', write_fmt='%.2f') # set number of layers to length of the created bottom array # this needs to be set prior to setting up the idomain, # otherwise idomain may have wrong number of layers self.cfg['dis']['dimensions']['nlay'] = len( self.cfg['dis']['griddata']['botm']) # initial idomain input for creating a dis package instance self._setup_array(package, 'idomain', datatype='array3d', write_fmt='%d', resample_method='nearest', dtype=int) # put together keyword arguments for dis package kwargs = self.cfg['grid'].copy() # nrow, ncol, delr, delc kwargs.update(self.cfg['dis']) kwargs.update(self.cfg['dis']['dimensions']) # nper, nlay, etc. kwargs.update(self.cfg['dis']['griddata']) # modelgrid: dis arguments remaps = {'xoff': 'xorigin', 'yoff': 'yorigin', 'rotation': 'angrot'} for k, v in remaps.items(): if v not in kwargs: kwargs[v] = kwargs.pop(k) kwargs['length_units'] = self.length_units # get the arguments for the flopy version of ModflowGwfdis # but instantiate with modflow-setup subclass of ModflowGwfdis kwargs = get_input_arguments(kwargs, mf6.ModflowGwfdis) dis = ModflowGwfdis(model=self, **kwargs) self._perioddata = None # reset perioddata #if not isinstance(self._modelgrid, MFsetupGrid): # self._modelgrid = None # override DIS package grid setup self._mg_resync = False self._reset_bc_arrays() self._set_idomain() print("finished in {:.2f}s\n".format(time.time() - t0)) return dis
def setup_rch(self): """ Sets up the RCH package. Parameters ---------- Notes ----- """ package = 'rch' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # make the irch array irch = make_irch(self.idomain) self._setup_array('rch', 'irch', data={0: irch}, datatype='array2d', write_fmt='%d', dtype=int) # make the rech array self._setup_array(package, 'recharge', datatype='transient2d', resample_method='nearest', write_fmt='%.6e', write_nodata=0.) kwargs = self.cfg[package].copy() kwargs.update(self.cfg[package]['options']) kwargs = get_input_arguments(kwargs, mf6.ModflowGwfrcha) rch = mf6.ModflowGwfrcha(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return rch
def setup_ic(self): """ Sets up the IC package. Parameters ---------- Notes ----- """ package = 'ic' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # make the starting heads array self._setup_array(package, 'strt', datatype='array3d', resample_method='linear', write_fmt='%.2f') kwargs = self.cfg[package]['griddata'].copy() kwargs = get_input_arguments(kwargs, mf6.ModflowGwfic) ic = mf6.ModflowGwfic(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return ic
def _parse_model_kwargs(cfg): if isinstance(cfg['model']['simulation'], str): # assume that simulation for model # is the one simulation specified in configuration # (regardless of the name specified in model configuration) cfg['model']['simulation'] = cfg['simulation'] if isinstance(cfg['model']['simulation'], dict): # create simulation from simulation block in config dict kwargs = cfg['simulation'].copy() kwargs.update(cfg['simulation']['options']) kwargs = get_input_arguments(kwargs, mf6.MFSimulation) sim = flopy.mf6.MFSimulation(**kwargs) cfg['model']['simulation'] = sim sim_ws = cfg['simulation']['sim_ws'] # if a simulation has already been created, get the path from the instance elif isinstance(cfg['model']['simulation'], mf6.MFSimulation): sim_ws = cfg['model']['simulation'].simulation_data.mfpath._sim_path else: raise TypeError('unrecognized configuration input for simulation.') # listing file cfg['model']['list'] = os.path.join(cfg['model']['list_filename_fmt'] .format(cfg['model']['modelname'])) # newton options if cfg['model']['options'].get('newton', False): cfg['model']['options']['newtonoptions'] = [''] if cfg['model']['options'].get('newton_under_relaxation', False): cfg['model']['options']['newtonoptions'] = ['under_relaxation'] cfg['model'].update(cfg['model']['options']) return cfg
def test_snap_to_NHG(shellmound_cfg, shellmound_simulation): cfg = deepcopy(shellmound_cfg) #simulation = deepcopy(simulation) cfg['model']['simulation'] = shellmound_simulation cfg['setup_grid']['snap_to_NHG'] = True cfg = MF6model._parse_model_kwargs(cfg) kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') m = MF6model(cfg=cfg, **kwargs) m.setup_grid() # national grid parameters xul, yul = -2553045.0, 3907285.0 # upper left corner ngrows = 4000 ngcols = 4980 natCellsize = 1000 # locations of left and top cell edges ngx = np.arange(ngcols) * natCellsize + xul ngy = np.arange(ngrows) * -natCellsize + yul x0, x1, y0, y1 = m.modelgrid.extent assert np.min(np.abs(ngx - x0)) == 0 assert np.min(np.abs(ngy - y0)) == 0 assert np.min(np.abs(ngx - x1)) == 0 assert np.min(np.abs(ngy - y1)) == 0
def setup_obs(self): """ Sets up the OBS utility. Parameters ---------- Notes ----- """ package = 'obs' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # munge the observation data df = setup_head_observations(self, format=package, obsname_column='obsname') # reformat to flopy input format obsdata = df[['obsname', 'obstype', 'id']].to_records(index=False) filename = self.cfg[package]['filename_fmt'].format(self.name) obsdata = {filename: obsdata} kwargs = self.cfg[package].copy() kwargs.update(self.cfg[package]['options']) kwargs['continuous'] = obsdata kwargs = get_input_arguments(kwargs, mf6.ModflowUtlobs) obs = mf6.ModflowUtlobs(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return obs
def setup_wel(self): """ Sets up the WEL package. Parameters ---------- Notes ----- """ package = 'wel' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # option to write stress_period_data to external files external_files = self.cfg[package]['external_files'] # munge well package input # returns dataframe with information to populate stress_period_data df = setup_wel_data(self, for_external_files=external_files) if len(df) == 0: print('No wells in active model area') return # set up stress_period_data if external_files: # get the file path (allowing for different external file locations, specified name format, etc.) filename_format = package + '_{:03d}.dat' # stress period suffix filepaths = self.setup_external_filepaths(package, 'stress_period_data', filename_format=filename_format, file_numbers=sorted(df.per.unique().tolist())) spd = {} period_groups = df.groupby('per') for kper in range(self.nper): if kper in period_groups.groups: group = period_groups.get_group(kper) group.drop('per', axis=1, inplace=True) if external_files: group.to_csv(filepaths[kper]['filename'], index=False, sep=' ') # make a copy for the intermediate data folder, for consistency with mf-2005 shutil.copy(filepaths[kper]['filename'], self.cfg['intermediate_data']['output_folder']) else: kspd = mf6.ModflowGwfwel.stress_period_data.empty(self, len(group), boundnames=True)[0] kspd['cellid'] = list(zip(group.k, group.i, group.j)) kspd['q'] = group['q'] kspd['boundname'] = group['boundname'] spd[kper] = kspd else: pass # spd[kper] = None kwargs = self.cfg[package].copy() kwargs.update(self.cfg[package]['options']) if not external_files: kwargs['stress_period_data'] = spd kwargs = get_input_arguments(kwargs, mf6.ModflowGwfwel) wel = mf6.ModflowGwfwel(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return wel
def make_lakarr2d(grid, lakesdata, include_ids, id_column='hydroid'): """ Make a nrow x ncol array with lake package extent for each lake, using the numbers in the 'id' column in the lakes shapefile. """ if isinstance(lakesdata, str): # implement automatic reprojection in gis-utils # maintaining backwards compatibility kwargs = {'dest_crs': grid.crs} kwargs = get_input_arguments(kwargs, shp2df) lakes = shp2df(lakesdata, **kwargs) elif isinstance(lakesdata, pd.DataFrame): lakes = lakesdata.copy() else: raise ValueError( 'unrecognized input for "lakesdata": {}'.format(lakesdata)) id_column = id_column.lower() lakes.columns = [c.lower() for c in lakes.columns] lakes.index = lakes[id_column] lakes = lakes.loc[include_ids] lakes['lakid'] = np.arange(1, len(lakes) + 1) lakes['geometry'] = [Polygon(g.exterior) for g in lakes.geometry] arr = rasterize(lakes, grid=grid, id_column='lakid') # ensure that order of hydroids is unchanged # (used to match features to lake IDs in lake package) assert lakes[id_column].tolist() == include_ids return arr
def setup_npf(self): """ Sets up the NPF package. Parameters ---------- Notes ----- """ package = 'npf' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() hiKlakes_value = float(self.cfg['parent'].get('hiKlakes_value', 1e4)) # make the k array self._setup_array(package, 'k', vmin=0, vmax=hiKlakes_value, resample_method='linear', datatype='array3d', write_fmt='%.6e') # make the k33 array (kv) self._setup_array(package, 'k33', vmin=0, vmax=hiKlakes_value, resample_method='linear', datatype='array3d', write_fmt='%.6e') kwargs = self.cfg[package]['options'].copy() kwargs.update(self.cfg[package]['griddata'].copy()) kwargs = get_input_arguments(kwargs, mf6.ModflowGwfnpf) npf = mf6.ModflowGwfnpf(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return npf
def shellmound_simulation(shellmound_cfg): cfg = shellmound_cfg.copy() kwargs = shellmound_cfg['simulation'].copy() kwargs.update(cfg['simulation']['options']) kwargs = get_input_arguments(kwargs, mf6.MFSimulation) sim = mf6.MFSimulation(**kwargs) return sim
def load(cls, yamlfile, load_only=None, verbose=False, forgive=False, check=False): """Load a model from a config file and set of MODFLOW files. """ print('\nLoading simulation in {}\n'.format(yamlfile)) t0 = time.time() cfg = load_cfg(yamlfile, verbose=verbose, default_file=cls.default_file) # '/mf6_defaults.yml') cfg = cls._parse_model_kwargs(cfg) kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') model = cls(cfg=cfg, **kwargs) model._load = True if 'grid' not in model.cfg.keys(): model.setup_grid() sim = model.cfg['model']['simulation'] # should be a flopy.mf6.MFSimulation instance models = [model] if isinstance(model.inset, dict): for inset_name, inset in model.inset.items(): models.append(inset) # execute the flopy load code on the pre-defined simulation and model instances # (so that the end result is a MFsetup.MF6model instance) # (kludgy) sim = flopy_mfsimulation_load(sim, models) # just return the parent model (inset models should be attached through the inset attribute, # in addition to through the .simulation flopy attribute) m = sim.get_model(model_name=model.name) print('finished loading model in {:.2f}s'.format(time.time() - t0)) return m
def shellmound_model(shellmound_cfg, shellmound_simulation): cfg = shellmound_cfg.copy() cfg['model']['simulation'] = shellmound_simulation cfg = MF6model._parse_model_kwargs(cfg) kwargs = get_input_arguments(cfg['model'], mf6.ModflowGwf, exclude='packages') m = MF6model(cfg=cfg, **kwargs) return m
def setup_bas6(self): """""" package = 'bas6' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # make the strt array self._setup_array(package, 'strt', datatype='array3d', resample_method='linear', write_fmt='%.2f') # initial ibound input for creating a bas6 package instance self._setup_array(package, 'ibound', datatype='array3d', write_fmt='%d', resample_method='nearest', dtype=int) kwargs = get_input_arguments(self.cfg['bas6'], fm.ModflowBas) bas = fm.ModflowBas(model=self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) self._set_ibound() return bas
def test_packagelist(pfl_nwt_test_cfg_path): cfg = load_cfg(pfl_nwt_test_cfg_path, default_file='/mfnwt_defaults.yml') assert len(cfg['model']['packages']) > 0 kwargs = get_input_arguments(cfg['model'], MFnwtModel) m = MFnwtModel(cfg=cfg, **kwargs) assert m.package_list == [p for p in m._package_setup_order if p in cfg['model']['packages']]
def setup_perimeter_boundary(self): """Set up constant head package for perimeter boundary. TODO: integrate perimeter boundary with wel package setup """ package = 'chd' print( 'setting up specified head perimeter boundary with CHD package...') t0 = time.time() # option to write stress_period_data to external files external_files = self.cfg[package]['external_files'] tmr = Tmr(self.parent, self, parent_head_file=self.cfg['parent']['headfile'], inset_parent_layer_mapping=self.parent_layers, inset_parent_period_mapping=self.parent_stress_periods) df = tmr.get_inset_boundary_heads(for_external_files=external_files) if external_files: # get the file path (allowing for different external file locations, specified name format, etc.) filename_format = package + '_{:03d}.dat' # stress period suffix filepaths = self.setup_external_filepaths( package, 'stress_period_data', filename_format=filename_format, file_numbers=sorted(df.per.unique().tolist())) spd = {} by_period = df.groupby('per') for kper, df_per in by_period: if external_files: df_per.rename(columns={'bhead': 'head'}, inplace=True) df_per.drop('per', axis=1, inplace=True) df_per.to_csv(filepaths[kper]['filename'], index=False, sep=' ', float_format='%g') # make a copy for the intermediate data folder, for consistency with mf-2005 shutil.copy(filepaths[kper]['filename'], self.cfg['intermediate_data']['output_folder']) else: maxbound = len(df_per) spd[kper] = mf6.ModflowGwfchd.stress_period_data.empty( self, maxbound=maxbound)[0] spd[kper]['cellid'] = list( zip(df_per['k'], df_per['i'], df_per['j'])) spd[kper]['head'] = df_per['bhead'] kwargs = self.cfg['chd'] kwargs.update(self.cfg['chd']['options']) kwargs = get_input_arguments(kwargs, mf6.ModflowGwfchd) if not external_files: kwargs['stress_period_data'] = spd chd = mf6.ModflowGwfchd(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return chd
def make_bdlknc_zones(grid, lakesshp, include_ids, feat_id_column='feat_id', lake_package_id_column='lak_id'): """ Make zones for populating with lakebed leakance values. Same as lakarr, but with a buffer around each lake so that horizontal connections have non-zero values of bdlknc, and near-shore areas can be assigend higher leakance values. """ print('setting up lakebed leakance zones...') t0 = time.time() if isinstance(lakesshp, str): # implement automatic reprojection in gis-utils # maintaining backwards compatibility kwargs = {'dest_crs': grid.crs} kwargs = get_input_arguments(kwargs, shp2df) lakes = shp2df(lakesshp, **kwargs) elif isinstance(lakesshp, pd.DataFrame): lakes = lakesshp.copy() else: raise ValueError( 'unrecognized input for "lakesshp": {}'.format(lakesshp)) # Exterior buffer id_column = feat_id_column.lower() lakes.columns = [c.lower() for c in lakes.columns] exterior_buffer = 30 # m lakes.index = lakes[id_column] lakes = lakes.loc[include_ids] if lake_package_id_column not in lakes.columns: lakes[lake_package_id_column] = np.arange(1, len(lakes) + 1) # speed up buffer construction by getting exteriors once # and probably more importantly, # simplifying possibly complex geometries of lakes generated from 2ft lidar unbuffered_exteriors = [ Polygon(g.exterior).simplify(5) for g in lakes.geometry ] lakes['geometry'] = [ g.buffer(exterior_buffer) for g in unbuffered_exteriors ] arr = rasterize(lakes, grid=grid, id_column=lake_package_id_column) # Interior buffer for lower leakance, assumed to be 20 m around the lake interior_buffer = -20 # m lakes['geometry'] = [ g.buffer(interior_buffer) for g in unbuffered_exteriors ] arr2 = rasterize(lakes, grid=grid, id_column=lake_package_id_column) arr2 = arr2 * 100 # Create new ids for the interior, as multiples of 10 arr[arr2 > 0] = arr2[arr2 > 0] # ensure that order of hydroids is unchanged # (used to match features to lake IDs in lake package) assert lakes[id_column].tolist() == list(include_ids) print('finished in {:.2f}s'.format(time.time() - t0)) return arr
def setup_riv(self, rivdata=None): """Set up River package. TODO: riv package input through configuration file """ package = 'riv' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() if rivdata is None: raise NotImplementedError("River package input through configuration file;" "currently only supported through to_riv option" "in sfr configuration block.") df = rivdata.stress_period_data if len(df) == 0: print('No input specified or streams not in model.') return # option to write stress_period_data to external files external_files = self.cfg[package].get('external_files', True) if external_files: # get the file path (allowing for different external file locations, specified name format, etc.) filename_format = package + '_{:03d}.dat' # stress period suffix filepaths = self.setup_external_filepaths(package, 'stress_period_data', filename_format=filename_format, file_numbers=sorted(df.per.unique().tolist())) spd = {} by_period = df.groupby('per') for kper, df_per in by_period: if external_files: df_per = df_per[['k', 'i', 'j', 'stage', 'cond', 'rbot']].copy() for col in 'k', 'i', 'j': df_per[col] += 1 df_per.rename(columns={'k': '#k'}, inplace=True) df_per.to_csv(filepaths[kper]['filename'], index=False, sep=' ') # make a copy for the intermediate data folder, for consistency with mf-2005 shutil.copy(filepaths[kper]['filename'], self.cfg['intermediate_data']['output_folder']) else: maxbound = len(df_per) spd[kper] = mf6.ModflowGwfchd.stress_period_data.empty(self, maxbound=maxbound, boundnames=True)[0] spd[kper]['cellid'] = list(zip(df_per['k'], df_per['i'], df_per['j'])) for col in 'cond', 'stage', 'rbot': spd[kper][col] = df_per[col] spd[kper]['boundname'] = ["'{}'".format(s) for s in df_per['name']] kwargs = self.cfg['riv'] # need default options from rivdata instance or cfg defaults kwargs.update(self.cfg['riv']['options']) kwargs = get_input_arguments(kwargs, mf6.ModflowGwfriv) if not external_files: kwargs['stress_period_data'] = spd riv = mf6.ModflowGwfriv(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return riv
def create_lgr_models(self): for k, v in self.cfg['setup_grid']['lgr'].items(): # load the config file for lgr inset model inset_cfg = load_cfg(v['filename'], default_file='/mf6_defaults.yml') # if lgr inset has already been created if inset_cfg['model']['modelname'] in self.simulation._models: return inset_cfg['model']['simulation'] = self.simulation if 'ims' in inset_cfg['model']['packages']: inset_cfg['model']['packages'].remove('ims') # set parent configuation dictionary here # (even though parent model is explicitly set below) # so that the LGR grid is snapped to the parent grid inset_cfg['parent'] = { 'namefile': self.namefile, 'model_ws': self.model_ws, 'version': 'mf6', 'hiKlakes_value': self.cfg['model']['hiKlakes_value'], 'default_source_data': True, 'length_units': self.length_units, 'time_units': self.time_units } inset_cfg = MF6model._parse_model_kwargs(inset_cfg) kwargs = get_input_arguments(inset_cfg['model'], mf6.ModflowGwf, exclude='packages') kwargs['parent'] = self # otherwise will try to load parent model inset_model = MF6model(cfg=inset_cfg, lgr=True, **kwargs) inset_model._load = self._load # whether model is being made or loaded from existing files inset_model.setup_grid() del inset_model.cfg['ims'] inset_model.cfg['tdis'] = self.cfg['tdis'] if self.inset is None: self.inset = {} self.lgr = {} self.inset[inset_model.name] = inset_model #self.inset[inset_model.name]._is_lgr = True # create idomain indicating area of parent grid that is LGR lgr_idomain = make_lgr_idomain( self.modelgrid, self.inset[inset_model.name].modelgrid) ncpp = int(self.modelgrid.delr[0] / self.inset[inset_model.name].modelgrid.delr[0]) ncppl = v.get('layer_refinement', 1) self.lgr[inset_model.name] = Lgr(self.nlay, self.nrow, self.ncol, self.dis.delr.array, self.dis.delc.array, self.dis.top.array, self.dis.botm.array, lgr_idomain, ncpp, ncppl) inset_model._perioddata = self.perioddata self._set_idomain()
def parse_perioddata_groups(perioddata_dict, defaults={}): """Reorganize input in perioddata dict into a list of groups (dicts). """ #perioddata = perioddata_dict.copy() perioddata_groups = [] group0 = defaults.copy() valid_txt = "if transient: perlen specified or 3 of start_date_time, " \ "end_date_time, nper or freq;\n" \ "if steady: nper or perlen specified. Default perlen " \ "for steady-state periods is 1." for k, v in perioddata_dict.items(): if 'group' in k.lower(): data = defaults.copy() data.update(v) if is_valid_perioddata(data): data = get_input_arguments(data, setup_perioddata_group) perioddata_groups.append(data) else: print_item(k, data) prefix = "perioddata input for {} must have".format(k) raise Exception(prefix + valid_txt) elif 'perioddata' in k.lower(): perioddata_groups += parse_perioddata_groups(perioddata_dict[k], defaults=defaults) else: group0[k] = v if len(perioddata_groups) == 0: if not is_valid_perioddata(group0): print_item('perioddata:', group0) prefix = "perioddata input must have" raise Exception(prefix + valid_txt) data = get_input_arguments(group0, setup_perioddata_group) perioddata_groups = [data] for group in perioddata_groups: if 'steady' in group: if np.isscalar(group['steady']): group['steady'] = {0: group['steady']} elif not isinstance(group['steady'], dict): group['steady'] = {i: s for i, s in enumerate(group['steady'])} return perioddata_groups
def setup_sto(self): """ Sets up the STO package. Parameters ---------- Notes ----- """ if np.all(self.perioddata['steady']): print('Skipping STO package, no transient stress periods...') return package = 'sto' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # make the sy array self._setup_array(package, 'sy', datatype='array3d', resample_method='linear', write_fmt='%.6e') # make the ss array self._setup_array(package, 'ss', datatype='array3d', resample_method='linear', write_fmt='%.6e') kwargs = self.cfg[package]['options'].copy() kwargs.update(self.cfg[package]['griddata'].copy()) # get steady/transient info from perioddata table # which parses it from either DIS or STO input (to allow consistent input structure with mf2005) kwargs['steady_state'] = { k: v for k, v in zip(self.perioddata['per'], self.perioddata['steady']) } kwargs['transient'] = { k: not v for k, v in zip(self.perioddata['per'], self.perioddata['steady']) } kwargs = get_input_arguments(kwargs, mf6.ModflowGwfsto) sto = mf6.ModflowGwfsto(self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return sto
def load_modelgrid(filename): """Create a MFsetupGrid instance from model config json file.""" cfg = load(filename) rename = {'xll': 'xoff', 'yll': 'yoff', } for k, v in rename.items(): if k in cfg: cfg[v] = cfg.pop(k) if np.isscalar(cfg['delr']): cfg['delr'] = np.ones(cfg['ncol'])* cfg['delr'] if np.isscalar(cfg['delc']): cfg['delc'] = np.ones(cfg['nrow']) * cfg['delc'] kwargs = get_input_arguments(cfg, MFsetupGrid) return MFsetupGrid(**kwargs)
def setup_oc(self): package = 'oc' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() stress_period_data = {} for i, r in self.perioddata.iterrows(): stress_period_data[(r.per, r.nstp - 1)] = r.oc kwargs = self.cfg['oc'] kwargs['stress_period_data'] = stress_period_data kwargs = get_input_arguments(kwargs, fm.ModflowOc) oc = fm.ModflowOc(model=self, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return oc
def setup_simulation_mover(self): """Set up the MODFLOW-6 water mover package at the simulation level. Automate set-up of the mover between SFR packages in LGR parent and inset models. todo: automate set-up of mover between SFR and lakes (within a model). Other uses of the water mover need to be configured manually using flopy. """ package = 'mvr' print('\nSetting up the simulation water mover package...') t0 = time.time() perioddata_dfs = [] if self.get_package('sfr') is not None: if self.inset is not None: for inset_name, inset in self.inset.items(): if inset.get_package('sfr'): inset_perioddata = get_mover_sfr_package_input( self, inset) perioddata_dfs.append(inset_perioddata) if len(perioddata_dfs) > 0: perioddata = pd.concat(perioddata_dfs) if len(perioddata) > 0: kwargs = flatten(self.cfg[package]) # modelnames (boolean) keyword to indicate that all package names will # be preceded by the model name for the package. Model names are # required when the Mover Package is used with a GWF-GWF Exchange. The # MODELNAME keyword should not be used for a Mover Package that is for # a single GWF Model. # this argument will need to be adapted for implementing a mover package within a model # (between lakes and sfr) kwargs['modelnames'] = True kwargs['maxmvr'] = len( perioddata ) # assumes that input for period 0 applies to all periods packages = set( list(zip(perioddata.mname1, perioddata.pname1)) + list(zip(perioddata.mname2, perioddata.pname2))) kwargs['maxpackages'] = len(packages) kwargs['packages'] = list(packages) kwargs['perioddata'] = { 0: perioddata.values.tolist() } # assumes that input for period 0 applies to all periods kwargs = get_input_arguments(kwargs, mf6.ModflowGwfmvr) mvr = mf6.ModflowMvr(self.simulation, **kwargs) print("finished in {:.2f}s\n".format(time.time() - t0)) return mvr else: print("no packages with mover information\n")
def setup_dis(self): """""" package = 'dis' print('\nSetting up {} package...'.format(package.upper())) t0 = time.time() # resample the top from the DEM if self.cfg['dis']['remake_top']: self._setup_array(package, 'top', datatype='array2d', resample_method='linear', write_fmt='%.2f') # make the botm array self._setup_array(package, 'botm', datatype='array3d', resample_method='linear', write_fmt='%.2f') # put together keyword arguments for dis package kwargs = self.cfg['grid'].copy() # nrow, ncol, delr, delc kwargs.update(self.cfg['dis']) # nper, nlay, etc. kwargs = get_input_arguments(kwargs, fm.ModflowDis) # we need flopy to read the intermediate files # (it will write the files in cfg) lmult = convert_length_units('meters', self.length_units) kwargs.update({ 'top': self.cfg['intermediate_data']['top'][0], 'botm': self.cfg['intermediate_data']['botm'], 'nper': self.nper, 'delc': self.modelgrid.delc * lmult, 'delr': self.modelgrid.delr * lmult }) for arg in ['perlen', 'nstp', 'tsmult', 'steady']: kwargs[arg] = self.perioddata[arg].values dis = fm.ModflowDis(model=self, **kwargs) self._perioddata = None # reset perioddata #if not isinstance(self._modelgrid, MFsetupGrid): # self._modelgrid = None # override DIS package grid setup self.setup_grid() # reset the model grid self._reset_bc_arrays() #self._isbc = None # reset BC property arrays print("finished in {:.2f}s\n".format(time.time() - t0)) return dis
def setup_lgr_exchanges(self): for inset_name, inset_model in self.inset.items(): # get the exchange data exchangelist = self.lgr[inset_name].get_exchange_data( angldegx=True, cdist=True) # make a dataframe for concise unpacking of cellids columns = [ 'cellidm1', 'cellidm2', 'ihc', 'cl1', 'cl2', 'hwva', 'angldegx', 'cdist' ] exchangedf = pd.DataFrame(exchangelist, columns=columns) # unpack the cellids and get their respective ibound values k1, i1, j1 = zip(*exchangedf['cellidm1']) k2, i2, j2 = zip(*exchangedf['cellidm2']) active1 = self.idomain[k1, i1, j1] == 1 active2 = inset_model.idomain[k2, i2, j2] == 1 # screen out connections involving an inactive cell active_connections = active1 & active2 nexg = active_connections.sum() active_exchangelist = [ l for i, l in enumerate(exchangelist) if active_connections[i] ] # arguments to ModflowGwfgwf kwargs = { 'exgtype': 'gwf6-gwf6', 'exgmnamea': self.name, 'exgmnameb': inset_name, 'nexg': nexg, 'auxiliary': [('angldegx', 'cdist')], 'exchangedata': active_exchangelist } # add water mover files if there's a mover package # not sure if one mover file can be used for multiple exchange files or not # if separate (simulation) water mover files are needed, # than this would have to be restructured if 'mvr' in self.simulation.package_key_dict: if inset_name in self.simulation.mvr.packages.array['mname']: kwargs['mvr_filerecord'] = self.simulation.mvr.filename # set up the exchange package kwargs = get_input_arguments(kwargs, mf6.ModflowGwfgwf) gwfe = mf6.ModflowGwfgwf(self.simulation, **kwargs)