def pleasant_lgr_stand_alone_parent(pleasant_lgr_test_cfg_path, tmpdir): """Stand-alone version of lgr parent model for comparing with LGR results. """ # Edit the configuration file before the file paths within it are converted to absolute # (model.load_cfg converts the file paths) cfg = load(pleasant_lgr_test_cfg_path) del cfg['setup_grid']['lgr'] cfg['simulation']['sim_ws'] = os.path.join(tmpdir, 'pleasant_lgr_just_parent') # save out the edited configuration file path, fname = os.path.split(pleasant_lgr_test_cfg_path) new_file = os.path.join(path, 'pleasant_lgr_just_parent.yml') dump(new_file, cfg) # load in the edited configuration file, converting the paths to absolute cfg = MF6model.load_cfg(new_file) # add some stuff just for the tests cfg['gisdir'] = os.path.join(cfg['simulation']['sim_ws'], 'gis') m = MF6model.setup_from_cfg(cfg) m.write_input() #if hasattr(m, 'sfr'): # sfr_package_filename = os.path.join(m.model_ws, m.sfr.filename) # m.sfrdata.write_package(sfr_package_filename, # version='mf6' # ) return m
def test_load_modelgrid(tmpdir): cfg = { 'xoff': 100, 'yoff': 100, 'angrot': 20., 'proj_str': 'epsg:3070', 'delr': np.ones(10).tolist(), 'delc': np.ones(2).tolist() } grid1 = MFsetupGrid(**cfg) grid_file = os.path.join(tmpdir, 'test_grid.json') dump(grid_file, cfg) grid2 = load_modelgrid(grid_file) assert grid1 == grid2
def test_solver_defaults(test_data_path, tmpdir): """Verify that default values aren't applied to solver packages if the simplified settings options are used (e.g. simple/moderate/complex)""" # modflow-6 IMS package mf6_model_config = test_data_path / 'pleasant_mf6_test.yml' cfg = MF6model.load_cfg(mf6_model_config) keep_keys = { 'simulation', 'model', 'parent', 'setup_grid', 'dis', 'tdis', 'intermediate_data', 'postprocessing' } new_cfg = {k: v for k, v in cfg.items() if k in keep_keys} new_cfg['model']['packages'] = ['dis'] new_cfg['ims'] = {'options': {'complexity': 'moderate'}} temp_yaml = Path(tmpdir) / 'junk.yml' dump(temp_yaml, new_cfg) m = MF6model.setup_from_yaml(temp_yaml) assert 'nonlinear' not in m.cfg['ims'] assert 'linear' not in m.cfg['ims'] # modflow-nwt NWT package mfnwt_model_config = test_data_path / 'pleasant_nwt_test.yml' cfg = MFnwtModel.load_cfg(mfnwt_model_config) keep_keys = { 'simulation', 'model', 'parent', 'setup_grid', 'dis', 'bas6', 'intermediate_data', 'postprocessing' } new_cfg = {k: v for k, v in cfg.items() if k in keep_keys} new_cfg['model']['packages'] = ['dis', 'bas6'] new_cfg['nwt'] = {'options': 'moderate'} temp_yaml = Path(tmpdir) / 'junk.yml' dump(temp_yaml, new_cfg) m = MFnwtModel.setup_from_yaml(temp_yaml) expected_keys = { 'headtol', 'fluxtol', 'maxiterout', 'thickfact', 'linmeth', 'iprnwt', 'ibotav', 'Continue', 'use_existing_file', 'options' } assert not set(m.cfg['nwt'].keys()).difference(expected_keys) assert m.cfg['nwt']['options'] == 'moderate'
def setup_structured_grid(xoff=None, yoff=None, xul=None, yul=None, nrow=None, ncol=None, nlay=None, dxy=None, delr=None, delc=None, top=None, botm=None, rotation=0., parent_model=None, snap_to_NHG=False, features=None, features_shapefile=None, id_column=None, include_ids=None, buffer=1000, crs=None, epsg=None, model_length_units=None, grid_file='grid.json', bbox_shapefile=None, **kwargs): """""" print('setting up model grid...') t0 = time.time() # conversions for model/parent model units to meters # set regular flag for handling delc/delr to_meters_inset = convert_length_units(model_length_units, 'meters') regular = True if dxy is not None: delr_m = np.round(dxy * to_meters_inset, 4) # dxy is specified in model units delc_m = delr_m if delr is not None: delr_m = np.round(delr * to_meters_inset, 4) # delr is specified in model units if not np.isscalar(delr_m): if (set(delr_m)) == 1: delr_m = delr_m[0] else: regular = False if delc is not None: delc_m = np.round(delc * to_meters_inset, 4) # delc is specified in model units if not np.isscalar(delc_m): if (set(delc_m)) == 1: delc_m = delc_m[0] else: regular = False if parent_model is not None: to_meters_parent = convert_length_units( get_model_length_units(parent_model), 'meters') # parent model grid spacing in meters parent_delr_m = np.round( parent_model.dis.delr.array[0] * to_meters_parent, 4) if not parent_delr_m % delr_m == 0: raise ValueError( 'inset delr spacing of {} must be factor of parent spacing of {}' .format(delr_m, parent_delr_m)) parent_delc_m = np.round( parent_model.dis.delc.array[0] * to_meters_parent, 4) if not parent_delc_m % delc_m == 0: raise ValueError( 'inset delc spacing of {} must be factor of parent spacing of {}' .format(delc_m, parent_delc_m)) if epsg is not None: crs = pyproj.crs.CRS.from_epsg(epsg) elif crs is not None: from gisutils import get_authority_crs crs = get_authority_crs(crs) elif parent_model is not None: crs = parent_model.modelgrid.crs # option 1: make grid from xoff, yoff and specified dimensions if xoff is not None and yoff is not None: assert nrow is not None and ncol is not None, \ "Need to specify nrow and ncol if specifying xoffset and yoffset." if regular: height_m = np.round(delc_m * nrow, 4) width_m = np.round(delr_m * ncol, 4) else: height_m = np.sum(delc_m) width_m = np.sum(delr_m) # optionally align grid with national hydrologic grid # grids snapping to NHD must have spacings that are a factor of 1 km if snap_to_NHG: assert regular and np.allclose(1000 % delc_m, 0, atol=1e-4) x, y = get_point_on_national_hydrogeologic_grid(xoff, yoff, offset='edge', op=np.floor) xoff = x yoff = y rotation = 0. # need to specify xul, yul in case snapping to parent # todo: allow snapping to parent grid on xoff, yoff if rotation != 0: raise NotImplementedError('Rotated grids not supported.') xul = xoff yul = yoff + height_m # option 2: make grid using buffered feature bounding box else: if features is None and features_shapefile is not None: # Make sure shapefile and bbox filter are in dest (model) CRS # TODO: CRS wrangling could be added to shp2df as a feature reproject_filter = False try: from gisutils import get_shapefile_crs features_crs = get_shapefile_crs(features_shapefile) if features_crs != crs: reproject_filter = True except: features_crs = get_proj_str(features_shapefile) reproject_filter = True filter = None if parent_model is not None: if reproject_filter: filter = project(parent_model.modelgrid.bbox, parent_model.modelgrid.crs, features_crs).bounds else: filter = parent_model.modelgrid.bbox.bounds shp2df_kwargs = {'dest_crs': crs} shp2df_kwargs = get_input_arguments(shp2df_kwargs, shp2df) df = shp2df(features_shapefile, filter=filter, **shp2df_kwargs) # optionally subset shapefile data to specified features if id_column is not None and include_ids is not None: df = df.loc[df[id_column].isin(include_ids)] # use all features by default features = df.geometry.tolist() # convert multiple features to a MultiPolygon if isinstance(features, list): if len(features) > 1: features = MultiPolygon(features) else: features = features[0] # size the grid based on the bbox for features x1, y1, x2, y2 = features.bounds L = buffer # distance from area of interest to boundary xul = x1 - L yul = y2 + L height_m = np.round(yul - (y1 - L), 4) # initial model height from buffer distance width_m = np.round((x2 + L) - xul, 4) rotation = 0. # rotation not supported with this option # align model with parent grid if there is a parent model # (and not snapping to national hydrologic grid) if parent_model is not None and not snap_to_NHG: # get location of coinciding cell in parent model for upper left pi, pj = parent_model.modelgrid.intersect(xul, yul) verts = np.array(parent_model.modelgrid.get_cell_vertices(pi, pj)) xul, yul = verts[:, 0].min(), verts[:, 1].max() # adjust the dimensions to align remaining corners def roundup(number, increment): return int(np.ceil(number / increment) * increment) height = roundup(height_m, parent_delr_m) width = roundup(width_m, parent_delc_m) # update nrow, ncol after snapping to parent grid if regular: nrow = int(height / delc_m) # h is in meters ncol = int(width / delr_m) # set the grid configuration dictionary # spacing is in meters (consistent with projected CRS) # (modelgrid object will be updated automatically from this dictionary) #if rotation == 0.: # xll = xul # yll = yul - model.height grid_cfg = { 'nrow': int(nrow), 'ncol': int(ncol), 'nlay': nlay, 'delr': delr_m, 'delc': delc_m, 'xoff': xoff, 'yoff': yoff, 'xul': xul, 'yul': yul, 'rotation': rotation, 'lenuni': 2 } if regular: grid_cfg['delr'] = np.ones(grid_cfg['ncol'], dtype=float) * grid_cfg['delr'] grid_cfg['delc'] = np.ones(grid_cfg['nrow'], dtype=float) * grid_cfg['delc'] grid_cfg['delr'] = grid_cfg['delr'].tolist() # for serializing to json grid_cfg['delc'] = grid_cfg['delc'].tolist() # renames for flopy modelgrid renames = {'rotation': 'angrot'} for k, v in renames.items(): if k in grid_cfg: grid_cfg[v] = grid_cfg.pop(k) # add epsg or wkt if there isn't an epsg if epsg is not None: grid_cfg['epsg'] = epsg elif crs is not None: if 'epsg' in crs.srs.lower(): grid_cfg['epsg'] = int(crs.srs.split(':')[1]) else: grid_cfg['wkt'] = crs.srs else: warnings.warn('No coordinate system reference provided for model grid!' 'Model input data may not be mapped correctly.') # set up the model grid instance grid_cfg['top'] = top grid_cfg['botm'] = botm grid_cfg.update(kwargs) # update with any kwargs from function call kwargs = get_input_arguments(grid_cfg, MFsetupGrid) modelgrid = MFsetupGrid(**kwargs) modelgrid.cfg = grid_cfg # write grid info to json, and shapefile of bbox # omit top and botm arrays from json represenation of grid # (just for horizontal disc.) del grid_cfg['top'] del grid_cfg['botm'] fileio.dump(grid_file, grid_cfg) if bbox_shapefile is not None: write_bbox_shapefile(modelgrid, bbox_shapefile) print("finished in {:.2f}s\n".format(time.time() - t0)) return modelgrid