diff --git a/autotest/t504_test.py b/autotest/t504_test.py index 5b25c7334a..d8411cead6 100644 --- a/autotest/t504_test.py +++ b/autotest/t504_test.py @@ -318,6 +318,16 @@ def test006_gwf3(): # load simulation sim = MFSimulation.load(model_name, 'mf6', exe_name, pth) + model = sim.get_model() + disu = model.get_package('disu') + # test switching disu array to internal array + disu.ja = disu.ja.array + # test writing hwva and cl12 arrays out to different locations + disu.hwva = {'filename': 'flow.disu.hwva_new.dat', 'factor': 1.0, + 'data': disu.hwva.array} + disu.cl12 = {'filename': 'flow.disu.cl12_new.dat', 'factor': 1.0, + 'data': disu.cl12.array} + # make temp folder to save simulation sim.simulation_data.mfpath.set_sim_path(run_folder) # write simulation to new location @@ -858,12 +868,12 @@ def test027_timeseriestest(): if __name__ == '__main__': + test006_gwf3() test001a_tharmonic() test001e_uzf_3lay() test003_gwfs_disv() test005_advgw_tidal() test006_2models_mvr() - test006_gwf3() test027_timeseriestest() test036_twrihfb() test045_lake1ss_table() diff --git a/flopy/export/utils.py b/flopy/export/utils.py index 030be51561..86892e68d9 100644 --- a/flopy/export/utils.py +++ b/flopy/export/utils.py @@ -600,7 +600,7 @@ def transient2d_export(f, t2d, **kwargs): if isinstance(f, str) and f.lower().endswith(".shp"): array_dict = {} - for kper in range(t2d.model.modeltime.sim_time.nper): + for kper in range(t2d.model.modeltime.nper): u2d = t2d[kper] name = '{}_{}'.format( shapefile_utils.shape_attr_name(u2d.name), kper + 1) diff --git a/flopy/mf6/coordinates/modelgrid.py b/flopy/mf6/coordinates/modelgrid.py index 3e15dc4e0f..76840f2530 100644 --- a/flopy/mf6/coordinates/modelgrid.py +++ b/flopy/mf6/coordinates/modelgrid.py @@ -433,7 +433,7 @@ def grid_type_consistent(self): def get_connections_array(self): if self.grid_type() == DiscretizationType.DISU: - return np.arange(1, self.num_connections() + 1, 1, np.int) + return np.arange(1, self.num_connections() + 1, 1, np.int32) else: except_str = 'ERROR: Can not get connections arrays for model ' \ '"{}" Only DISU (unstructured) grids ' \ @@ -443,10 +443,10 @@ def get_connections_array(self): def get_horizontal_cross_section_dim_arrays(self): if self.grid_type() == DiscretizationType.DIS: - return [np.arange(1, self.num_rows() + 1, 1, np.int), - np.arange(1, self.num_columns() + 1, 1, np.int)] + return [np.arange(1, self.num_rows() + 1, 1, np.int32), + np.arange(1, self.num_columns() + 1, 1, np.int32)] elif self.grid_type() == DiscretizationType.DISV: - return [np.arange(1, self.num_cells_per_layer() + 1, 1, np.int)] + return [np.arange(1, self.num_cells_per_layer() + 1, 1, np.int32)] elif self.grid_type() == DiscretizationType.DISU: except_str = 'ERROR: Can not get horizontal plane arrays for ' \ 'model "{}" DISU grid. DISU grids do not support ' \ @@ -464,23 +464,23 @@ def get_model_dim(self): def get_model_dim_arrays(self): if self.grid_type() == DiscretizationType.DIS: - return [np.arange(1, self.num_layers() + 1, 1, np.int), - np.arange(1, self.num_rows() + 1, 1, np.int), - np.arange(1, self.num_columns() + 1, 1, np.int)] + return [np.arange(1, self.num_layers() + 1, 1, np.int32), + np.arange(1, self.num_rows() + 1, 1, np.int32), + np.arange(1, self.num_columns() + 1, 1, np.int32)] elif self.grid_type() == DiscretizationType.DISV: - return [np.arange(1, self.num_layers() + 1, 1, np.int), - np.arange(1, self.num_cells_per_layer() + 1, 1, np.int)] + return [np.arange(1, self.num_layers() + 1, 1, np.int32), + np.arange(1, self.num_cells_per_layer() + 1, 1, np.int32)] elif self.grid_type() == DiscretizationType.DISU: - return [np.arange(1, self.num_cells() + 1, 1, np.int)] + return [np.arange(1, self.num_cells() + 1, 1, np.int32)] def get_row_array(self): - return np.arange(1, self.num_rows() + 1, 1, np.int) + return np.arange(1, self.num_rows() + 1, 1, np.int32) def get_column_array(self): - return np.arange(1, self.num_columns() + 1, 1, np.int) + return np.arange(1, self.num_columns() + 1, 1, np.int32) def get_layer_array(self): - return np.arange(1, self.num_layers() + 1, 1, np.int) + return np.arange(1, self.num_layers() + 1, 1, np.int32) def get_horizontal_cross_section_dim_names(self): if self.grid_type() == DiscretizationType.DIS: diff --git a/flopy/mf6/data/dfn/gwf-disu.dfn b/flopy/mf6/data/dfn/gwf-disu.dfn index 623bdc2914..d65575983e 100644 --- a/flopy/mf6/data/dfn/gwf-disu.dfn +++ b/flopy/mf6/data/dfn/gwf-disu.dfn @@ -111,6 +111,7 @@ reader readarray longname grid connectivity description is a list of cell number (n) followed by its connecting cell numbers (m) for each of the m cells connected to cell n. The number of values to provide for cell n is IAC(n). This list is sequentially provided for the first to the last cell. The first value in the list must be cell n itself, and the remaining cells must be listed in an increasing order (sorted from lowest number to highest). Note that the cell and its connections are only supplied for the GWF cells and their connections to the other GWF cells. Also note that the JA list input may be divided such that every node and its connectivity list can be on a separate line for ease in readability of the file. To further ease readability of the file, the node number of the cell whose connectivity is subsequently listed, may be expressed as a negative number, the sign of which is subsequently converted to positive by the code. numeric_index true +jagged_array iac block connectiondata name ihc @@ -119,6 +120,7 @@ shape (nja) reader readarray longname connection type description is an index array indicating the direction between node n and all of its m connections. If IHC = 0 then cell n and cell m are connected in the vertical direction. Cell n overlies cell m if the cell number for n is less than m; cell m overlies cell n if the cell number for m is less than n. If IHC = 1 then cell n and cell m are connected in the horizontal direction. If IHC = 2 then cell n and cell m are connected in the horizontal direction, and the connection is vertically staggered. A vertically staggered connection is one in which a cell is horizontally connected to more than one cell in a horizontal connection. +jagged_array iac block connectiondata name cl12 @@ -127,6 +129,7 @@ shape (nja) reader readarray longname connection lengths description is the array containing connection lengths between the center of cell n and the shared face with each adjacent m cell. +jagged_array iac block connectiondata name hwva @@ -135,6 +138,7 @@ shape (nja) reader readarray longname connection lengths description is a symmetric array of size NJA. For horizontal connections, entries in HWVA are the horizontal width perpendicular to flow. For vertical connections, entries in HWVA are the vertical area for flow. Thus, values in the HWVA array contain dimensions of both length and area. Entries in the HWVA array have a one-to-one correspondence with the connections specified in the JA array. Likewise, there is a one-to-one correspondence between entries in the HWVA array and entries in the IHC array, which specifies the connection type (horizontal or vertical). Entries in the HWVA array must be symmetric; the program will terminate with an error if the value for HWVA for an n to m connection does not equal the value for HWVA for the corresponding n to m connection. +jagged_array iac block connectiondata name angldegx @@ -144,6 +148,7 @@ shape (nja) reader readarray longname angle of face normal to connection description is the angle (in degrees) between the horizontal x-axis and the outward normal to the face between a cell and its connecting cells. The angle varies between zero and 360.0 degrees, where zero degrees points in the positive x-axis direction, and 90 degrees points in the positive y-axis direction. ANGLDEGX is only needed if horizontal anisotropy is specified in the NPF Package, if the XT3D option is used in the NPF Package, or if the SAVE\_SPECIFIC\_DISCHARGE option is specifed in the NPF Package. ANGLDEGX does not need to be specified if these conditions are not met. ANGLDEGX is of size NJA; values specified for vertical connections and for the diagonal position are not used. Note that ANGLDEGX is read in degrees, which is different from MODFLOW-USG, which reads a similar variable (ANGLEX) in radians. +jagged_array iac # --------------------- gwf disu vertices --------------------- diff --git a/flopy/mf6/data/mfdata.py b/flopy/mf6/data/mfdata.py index c6ada632f9..2d6bb963fc 100644 --- a/flopy/mf6/data/mfdata.py +++ b/flopy/mf6/data/mfdata.py @@ -448,7 +448,10 @@ def _get_internal_formatting_string(self, layer): if storage.data_structure_type != DataStructureType.recarray: int_format.append('FACTOR') if layer_storage.factor is not None: - int_format.append(str(layer_storage.factor)) + if data_type == DatumType.integer: + int_format.append(str(int(layer_storage.factor))) + else: + int_format.append(str(layer_storage.factor)) else: if data_type == DatumType.double_precision: int_format.append('1.0') @@ -475,8 +478,12 @@ def _get_external_formatting_string(self, layer, ext_file_action): ext_format = ['OPEN/CLOSE', "'{}'".format(ext_file_path)] if storage.data_structure_type != DataStructureType.recarray: if layer_storage.factor is not None: + data_type = self.structure.get_datum_type(return_enum_type=True) ext_format.append('FACTOR') - ext_format.append(str(layer_storage.factor)) + if data_type == DatumType.integer: + ext_format.append(str(int(layer_storage.factor))) + else: + ext_format.append(str(layer_storage.factor)) if layer_storage.binary: ext_format.append('(BINARY)') if layer_storage.iprn is not None: diff --git a/flopy/mf6/data/mfdataarray.py b/flopy/mf6/data/mfdataarray.py index 0cc353f790..4074fd9f29 100644 --- a/flopy/mf6/data/mfdataarray.py +++ b/flopy/mf6/data/mfdataarray.py @@ -565,6 +565,10 @@ def get_file_entry(self, layer=None, inspect.stack()[0][3], type_, value_, traceback_, None, self._simulation_data.debug, ex) + if self.structure.data_item_structures[0].numeric_index or \ + self.structure.data_item_structures[0].is_cellid: + # for cellid and numeric indices convert from 0 base to 1 based + data = abs(data) + 1 file_entry_array.append('{}{}{}{}\n'.format(indent, self.structure.name, indent, @@ -680,9 +684,8 @@ def _get_file_entry_layer(self, layer, data_indent, storage_type, # internal data header + data format_str = self._get_internal_formatting_string(layer).upper() lay_str = self._get_data_layer_string(layer, data_indent).upper() - file_entry = '{}{}{}\n{}{}'.format(file_entry, indent_string, - format_str, indent_string, - lay_str) + file_entry = '{}{}{}\n{}'.format(file_entry, indent_string, + format_str, lay_str) elif storage_type == DataStorageType.internal_constant: # constant data try: @@ -728,8 +731,6 @@ def _get_file_entry_layer(self, layer, data_indent, storage_type, return file_entry def _get_data_layer_string(self, layer, data_indent): - layer_data_string = [''] - line_data_count = 0 # iterate through data layer try: data = self._get_storage_obj().get_data(layer, False) @@ -744,42 +745,10 @@ def _get_data_layer_string(self, layer, data_indent): inspect.stack()[0][3], type_, value_, traceback_, comment, self._simulation_data.debug, ex) - data_iter = datautil.PyListUtil.next_item(data) - indent_str = self._simulation_data.indent_string - for item, last_item, new_list, nesting_change in data_iter: - # increment data/layer counts - line_data_count += 1 - try: - data_lyr = to_string(item, self._data_type, - self._simulation_data, - self._data_dimensions) - except Exception as ex: - type_, value_, traceback_ = sys.exc_info() - comment = 'Could not convert data "{}" of type "{}" to a ' \ - 'string.'.format(item, self._data_type) - raise MFDataException(self.structure.get_model(), - self.structure.get_package(), - self._path, - 'converting data', - self.structure.name, - inspect.stack()[0][3], type_, - value_, traceback_, comment, - self._simulation_data.debug, ex) - layer_data_string[-1] = '{}{}{}'.format(layer_data_string[-1], - indent_str, - data_lyr) - if self._simulation_data.wrap_multidim_arrays and \ - (line_data_count == self._simulation_data. - max_columns_of_data or last_item): - layer_data_string.append('{}'.format(data_indent)) - line_data_count = 0 - if len(layer_data_string) > 0: - # clean up the text at the end of the array - layer_data_string[-1] = layer_data_string[-1].strip() - if len(layer_data_string) == 1: - return '{}{}\n'.format(data_indent, layer_data_string[0].rstrip()) - else: - return '\n'.join(layer_data_string) + file_access = MFFileAccessArray(self.structure, self._data_dimensions, + self._simulation_data, self._path, + self._current_key) + return file_access.get_data_string(data, self._data_type, data_indent) def _resolve_layer_index(self, layer, allow_multiple_layers=False): # handle layered vs non-layered data diff --git a/flopy/mf6/data/mfdatalist.py b/flopy/mf6/data/mfdatalist.py index ed11ba3b56..3841d77d67 100644 --- a/flopy/mf6/data/mfdatalist.py +++ b/flopy/mf6/data/mfdatalist.py @@ -178,7 +178,7 @@ def to_array(self, kper=0, mask=False): raise Exception("MfList: something bad happened") for name, arr in arrays.items(): - cnt = np.zeros(shape, dtype=np.float) + cnt = np.zeros(shape, dtype=np.float64) #print(name,kper) for sp_rec in sarr: if sp_rec is not None: diff --git a/flopy/mf6/data/mfdatascalar.py b/flopy/mf6/data/mfdatascalar.py index 310c9e5409..e493f17fe2 100644 --- a/flopy/mf6/data/mfdatascalar.py +++ b/flopy/mf6/data/mfdatascalar.py @@ -80,17 +80,17 @@ def plotable(self): @property def dtype(self): if self.structure.type == DatumType.double_precision: - return np.float32 + return np.float64 elif self.structure.type == DatumType.integer: - return np.int + return np.int32 elif self.structure.type == DatumType.recarray or \ self.structure.type == DatumType.record or \ self.structure.type == DatumType.repeating_record: for data_item_struct in self.structure.data_item_structures: if data_item_struct.type == DatumType.double_precision: - return np.float32 + return np.float64 elif data_item_struct.type == DatumType.integer: - return np.int + return np.int32 return None def has_data(self): @@ -169,7 +169,7 @@ def set_data(self, data): def add_one(self): datum_type = self.structure.get_datum_type() - if datum_type == int or datum_type == np.int: + if datum_type == int or datum_type == np.int32: if self._get_storage_obj().get_data() is None: try: self._get_storage_obj().set_data(1) diff --git a/flopy/mf6/data/mfdatastorage.py b/flopy/mf6/data/mfdatastorage.py index 5a27bef22a..cdbf1d0388 100644 --- a/flopy/mf6/data/mfdatastorage.py +++ b/flopy/mf6/data/mfdatastorage.py @@ -1092,11 +1092,12 @@ def store_external(self, file_path, layer=None, multiplier=None, self.data_dimensions.structure, self.data_dimensions, self._simulation_data, self._data_path, self._stress_period) + str_layered = self.data_dimensions.structure.layered file_access.write_binary_file( data, fp, text, self._model_or_sim.modeldiscrit, self._model_or_sim.modeltime, stress_period=self._stress_period, precision='double', - write_multi_layer=(layer is None)) + write_multi_layer=(layer is None and str_layered)) else: file_access = MFFileAccessArray( self.data_dimensions.structure, self.data_dimensions, @@ -1573,7 +1574,7 @@ def _build_full_data(self, apply_multiplier=False): if dimensions[0] < 0: return None all_none = True - np_data_type = self.data_dimensions.structure.get_datum_type() + np_data_type = self.data_dimensions.structure.get_datum_type() full_data = np.full(dimensions, np.nan, self.data_dimensions.structure.get_datum_type(True)) is_aux = self.data_dimensions.structure.name == 'aux' @@ -1675,7 +1676,9 @@ def _fill_const_layer(self, layer): if data_dimensions[0] < 0: return ls.data_const_value else: - return np.full(data_dimensions, ls.data_const_value[0]) + data_type = self.data_dimensions.structure. \ + get_datum_type(numpy_type=True) + return np.full(data_dimensions, ls.data_const_value[0], data_type) def _is_type(self, data_item, data_type): if data_type == DatumType.string or data_type == DatumType.keyword: diff --git a/flopy/mf6/data/mfdatautil.py b/flopy/mf6/data/mfdatautil.py index f11edcc557..70336aee6f 100644 --- a/flopy/mf6/data/mfdatautil.py +++ b/flopy/mf6/data/mfdatautil.py @@ -3,7 +3,7 @@ from copy import deepcopy from ..mfbase import MFDataException, FlopyException from .mfstructure import DatumType -from ...utils.datautil import PyListUtil +from ...utils.datautil import PyListUtil, DatumUtil import struct @@ -122,6 +122,8 @@ def to_string(val, data_type, sim_data, data_dim, is_cellid=False, else: return sim_data.sci_format_str.format(val) elif is_cellid or (possible_cellid and isinstance(val, tuple)): + if DatumUtil.is_int(val): + return str(val + 1) if len(val) > 0 and val[0] == 'none': # handle case that cellid is 'none' return val[0] @@ -526,7 +528,7 @@ def _build_layer(self, data_type, data_storage_type, default_value, data_type) elif data_storage_type == DataStorageType.internal_constant: if default_value is None: - if data_type == np.int: + if data_type == np.int32: data = 0 else: data = 0.0 diff --git a/flopy/mf6/data/mffileaccess.py b/flopy/mf6/data/mffileaccess.py index 34fbc208f1..1aca8008c7 100644 --- a/flopy/mf6/data/mffileaccess.py +++ b/flopy/mf6/data/mffileaccess.py @@ -167,6 +167,7 @@ def __init__(self, structure, data_dimensions, simulation_data, path, def write_binary_file(self, data, fname, text, modelgrid=None, modeltime=None, stress_period=0, precision='double', write_multi_layer=False): + data = self._resolve_cellid_numbers_to_file(data) fd = self._open_ext_file(fname, binary=True, write=True) if write_multi_layer: for layer, value in enumerate(data): @@ -254,31 +255,7 @@ def write_text_file(self, data, fp, data_type, data_size): self.structure.name, inspect.stack()[0][3], type_, value_, traceback_, message, self._simulation_data.debug) - current_size = 0 - for data_item in MultiListIter(data, True): - if data_item[2] and current_size > 0: - # new list/dimension, add appropriate formatting to - # the file - fd.write('\n') - fd.write('{} '.format(to_string(data_item[0], data_type, - self._simulation_data, - self._data_dimensions))) - current_size += 1 - if current_size != data_size: - message = 'Not enough data for "{}" provided for file' \ - ' {}. Expected data size is {}, actual data ' \ - 'size is' \ - '{}.'.format(self.structure.path, fd.name, - data_size, current_size) - type_, value_, traceback_ = sys.exc_info() - fd.close() - raise MFDataException( - self._data_dimensions.structure.get_model(), - self._data_dimensions.structure.get_package(), - self._data_dimensions.structure.path, - 'storing external data', self.structure.name, - inspect.stack()[0][3], type_, value_, traceback_, - message, self._simulation_data.debug) + fd.write(self.get_data_string(data, data_type, '')) fd.close() def read_binary_data_from_file(self, fname, data_shape, data_size, @@ -289,7 +266,7 @@ def read_binary_data_from_file(self, fname, data_shape, data_size, numpy_type, name = self.datum_to_numpy_type(data_type) header_dtype = bf.BinaryHeader.set_dtype( bintype=self._get_bintype(modelgrid), - precision=name) + precision='double') if read_multi_layer and len(data_shape) > 1: all_data = np.empty(data_shape, numpy_type) headers = [] @@ -308,10 +285,70 @@ def read_binary_data_from_file(self, fname, data_shape, data_size, fd.close() return bin_data + def get_data_string(self, data, data_type, data_indent=''): + layer_data_string = ['{}'.format(data_indent)] + line_data_count = 0 + indent_str = self._simulation_data.indent_string + data_iter = datautil.PyListUtil.next_item(data) + is_cellid = self.structure.data_item_structures[0].numeric_index or \ + self.structure.data_item_structures[0].is_cellid + + jag_arr = self.structure.data_item_structures[0].jagged_array + jagged_def = None + jagged_def_index = 0 + if jag_arr is not None: + # get jagged array definition + jagged_def_path = self._path[0:-1] + (jag_arr,) + if jagged_def_path in self._simulation_data.mfdata: + jagged_def = self._simulation_data.mfdata[jagged_def_path].array + + for item, last_item, new_list, nesting_change in data_iter: + # increment data/layer counts + line_data_count += 1 + try: + data_lyr = to_string(item, data_type, + self._simulation_data, + self._data_dimensions, is_cellid) + except Exception as ex: + type_, value_, traceback_ = sys.exc_info() + comment = 'Could not convert data "{}" of type "{}" to a ' \ + 'string.'.format(item, data_type) + raise MFDataException(self.structure.get_model(), + self.structure.get_package(), + self._path, + 'converting data', + self.structure.name, + inspect.stack()[0][3], type_, + value_, traceback_, comment, + self._simulation_data.debug, ex) + layer_data_string[-1] = '{}{}{}'.format(layer_data_string[-1], + indent_str, + data_lyr) + + if jagged_def is not None: + if line_data_count == jagged_def[jagged_def_index]: + layer_data_string.append('{}'.format(data_indent)) + line_data_count = 0 + jagged_def_index += 1 + else: + if self._simulation_data.wrap_multidim_arrays and \ + (line_data_count == self._simulation_data. + max_columns_of_data or last_item): + layer_data_string.append('{}'.format(data_indent)) + line_data_count = 0 + if len(layer_data_string) > 0: + # clean up the text at the end of the array + layer_data_string[-1] = layer_data_string[-1].strip() + if len(layer_data_string) == 1: + return '{}{}\n'.format(data_indent, layer_data_string[0].rstrip()) + else: + return '\n'.join(layer_data_string) + def _read_binary_file_layer(self, fd, fname, header_dtype, numpy_type, data_size, data_shape): header_data = np.fromfile(fd, dtype=header_dtype, count=1) data = np.fromfile(fd, dtype=numpy_type, count=data_size) + data = self._resolve_cellid_numbers_from_file(data) if data.size != data_size: message = 'Binary file {} does not contain expected data. ' \ 'Expected array size {} but found size ' \ @@ -364,6 +401,7 @@ def read_text_data_from_file(self, data_size, data_type, data_dim, layer, self._simulation_data.debug) data_out = np.fromiter(data_raw, dtype=data_type, count=data_size) + data_out = self._resolve_cellid_numbers_from_file(data_out) if close_file: fd.close() @@ -585,6 +623,24 @@ def _load_layer(self, layer, layer_size, storage, arr_line, file_handle, value_, traceback_, comment, self._simulation_data.debug, ex) + def _is_cellid_or_numeric_index(self): + if self.structure.data_item_structures[0].numeric_index or \ + self.structure.data_item_structures[0].is_cellid: + return True + return False + + def _resolve_cellid_numbers_to_file(self, data): + if self._is_cellid_or_numeric_index(): + return abs(data) + 1 + else: + return data + + def _resolve_cellid_numbers_from_file(self, data): + if self._is_cellid_or_numeric_index(): + return abs(data) - 1 + else: + return data + def _resolve_data_shape(self, data, layer_shape, storage): try: dimensions = storage.get_data_dimensions(layer_shape) @@ -678,10 +734,7 @@ def _build_data_array(self, data, modelgrid, precision): return np.array(data_list, dtype=header) def _get_header(self, modelgrid, precision): - if precision.lower() == 'double': - np_flt_type = np.float64 - else: - np_flt_type = np.float32 + np_flt_type = np.float64 header = [] int_cellid_indexes = {} ext_cellid_indexes = {} @@ -703,7 +756,7 @@ def _get_header(self, modelgrid, precision): if aux_var_names is not None: for aux_var_name in aux_var_names[0]: if aux_var_name.lower() != 'auxiliary': - header.append((aux_var_name, np.float64)) + header.append((aux_var_name, np_flt_type)) ext_index += 1 return header, int_cellid_indexes, ext_cellid_indexes diff --git a/flopy/mf6/data/mfstructure.py b/flopy/mf6/data/mfstructure.py index 87d5a48aba..fbffd56b27 100644 --- a/flopy/mf6/data/mfstructure.py +++ b/flopy/mf6/data/mfstructure.py @@ -64,7 +64,7 @@ def __init__(self): self.common = os.path.join(self.dfndir, 'common.dfn') # FIX: Transport - multi packages are hard coded self.multi_package = {'gwfmvr': 0, 'exggwfgwf': 0, 'gwfchd': 0, - 'gwfrch': 0, + 'gwfrch': 0, 'gwfwel': 0, 'gwfdrn': 0, 'gwfriv': 0, 'utlobs': 0, 'utlts': 0, 'utltas': 0} @@ -778,6 +778,8 @@ def __init__(self): self.construct_data = None self.parameter_name = None self.one_per_pkg = False + self.jagged_array = None + def set_value(self, line, common): arr_line = line.strip().split() @@ -920,6 +922,8 @@ def set_value(self, line, common): self.parameter_name = arr_line[1] elif arr_line[0] == 'one_per_pkg': self.one_per_pkg = bool(arr_line[1]) + elif arr_line[0] == 'jagged_array': + self.jagged_array = arr_line[1] def get_type_string(self): return '[{}]'.format(self.type_string) @@ -1595,9 +1599,9 @@ def get_datum_type(self, numpy_type=False, return_enum_type=False): else: if numpy_type: if var_type[0] == DatumType.double_precision: - return np.float + return np.float64 elif var_type[0] == DatumType.integer: - return np.int + return np.int32 else: return np.object else: diff --git a/flopy/mf6/mfbase.py b/flopy/mf6/mfbase.py index 2c93999a86..a95eedc45b 100644 --- a/flopy/mf6/mfbase.py +++ b/flopy/mf6/mfbase.py @@ -597,10 +597,6 @@ def get_package(self, name=None): if name.lower() in self.package_name_dict: return self.package_name_dict[name.lower()] - # search for package key - if name.lower() in self.package_key_dict: - return self.package_key_dict[name.lower()] - # search for package type if name.lower() in self.package_type_dict: if len(self.package_type_dict[name.lower()]) == 0: @@ -610,6 +606,10 @@ def get_package(self, name=None): else: return self.package_type_dict[name.lower()] + # search for package key + if name.lower() in self.package_key_dict: + return self.package_key_dict[name.lower()] + # search for partial package name for pp in self._packagelist: if pp.package_name is not None: diff --git a/flopy/mf6/mfpackage.py b/flopy/mf6/mfpackage.py index 3995afd49b..40289ffef4 100644 --- a/flopy/mf6/mfpackage.py +++ b/flopy/mf6/mfpackage.py @@ -928,6 +928,7 @@ def _write_block(self, fd, block_header, ext_file_action): inspect.stack()[0][3], type_, value_, traceback_, message, self._simulation_data.debug) + # write data sets for key, dataset in self.datasets.items(): try: diff --git a/flopy/mf6/modflow/mfgwfdisu.py b/flopy/mf6/modflow/mfgwfdisu.py index eb441fd2aa..2089b86757 100644 --- a/flopy/mf6/modflow/mfgwfdisu.py +++ b/flopy/mf6/modflow/mfgwfdisu.py @@ -198,16 +198,17 @@ class ModflowGwfdisu(mfpackage.MFPackage): ["block connectiondata", "name iac", "type integer", "shape (nodes)", "reader readarray"], ["block connectiondata", "name ja", "type integer", - "shape (nja)", "reader readarray", "numeric_index true"], + "shape (nja)", "reader readarray", "numeric_index true", + "jagged_array iac"], ["block connectiondata", "name ihc", "type integer", - "shape (nja)", "reader readarray"], + "shape (nja)", "reader readarray", "jagged_array iac"], ["block connectiondata", "name cl12", "type double precision", - "shape (nja)", "reader readarray"], + "shape (nja)", "reader readarray", "jagged_array iac"], ["block connectiondata", "name hwva", "type double precision", - "shape (nja)", "reader readarray"], + "shape (nja)", "reader readarray", "jagged_array iac"], ["block connectiondata", "name angldegx", "type double precision", "optional true", "shape (nja)", - "reader readarray"], + "reader readarray", "jagged_array iac"], ["block vertices", "name vertices", "type recarray iv xv yv", "reader urword", "optional false"], ["block vertices", "name iv", "type integer", "in_record true", diff --git a/flopy/mf6/modflow/mfsimulation.py b/flopy/mf6/modflow/mfsimulation.py index 10eb7b1578..aa7f75a9fb 100644 --- a/flopy/mf6/modflow/mfsimulation.py +++ b/flopy/mf6/modflow/mfsimulation.py @@ -1,1534 +1,1535 @@ -""" -mfsimulation module. contains the MFSimulation class - - -""" -import errno, sys, inspect -import collections -import os.path -from ...mbase import run_model -from ..mfbase import PackageContainer, MFFileMgmt, ExtFileAction, \ - PackageContainerType, MFDataException, FlopyException, \ - VerbosityLevel -from ..mfpackage import MFPackage -from ..data.mfstructure import DatumType -from ..data import mfstructure -from ..utils import binaryfile_utils -from ..utils import mfobservation -from ..modflow import mfnam, mfims, mftdis, mfgwfgnc, mfgwfmvr -from ..data.mfdatautil import MFComment - - -class SimulationDict(collections.OrderedDict): - """ - Class containing custom dictionary for MODFLOW simulations. Behaves as an - OrderedDict with some additional features described below. - - Parameters - ---------- - path : MFFileMgmt - object containing path information for the simulation - - Methods - ------- - output_keys : (print_keys: boolean) : list - returns a list of output data keys the dictionary supports for output - data, print_keys allows those keys to be printed to output. - input_keys : () - prints all input data keys - observation_keys : () - prints observation keys - keys : () - print all keys, input and output - plot : (key : string, **kwargs) - plots data with key 'key' using **kwargs for plot options - shapefile : (key : string, **kwargs) - create shapefile from data with key 'key' and with additional fields - in **kwargs - """ - def __init__(self, path, *args): - self._path = path - collections.OrderedDict.__init__(self) - - def __getitem__(self, key): - # check if the key refers to a binary output file, or an observation - # output file, if so override the dictionary request and call output - # requester classes - - # FIX: Transport - Include transport output files - if key[1] in ('CBC', 'HDS', 'DDN', 'UCN'): - val = binaryfile_utils.MFOutput(self, self._path, key) - return val.data - - elif key[-1] == 'Observations': - val = mfobservation.MFObservation(self, self._path, key) - return val.data - - val = collections.OrderedDict.__getitem__(self, key) - return val - - def __setitem__(self, key, val): - collections.OrderedDict.__setitem__(self, key, val) - - def find_in_path(self, key_path, key_leaf): - key_path_size = len(key_path) - for key, item in self.items(): - if key[:key_path_size] == key_path: - if key[-1] == key_leaf: - # found key_leaf as a key in the dictionary - return item, None - if not isinstance(item, MFComment): - data_item_index = 0 - data_item_structures = item.structure.data_item_structures - for data_item_struct in data_item_structures: - if data_item_struct.name == key_leaf: - # found key_leaf as a data item name in the data in - # the dictionary - return item, data_item_index - if data_item_struct.type != DatumType.keyword: - data_item_index += 1 - return None, None - - def output_keys(self, print_keys=True): - # get keys to request binary output - x = binaryfile_utils.MFOutputRequester.getkeys(self, self._path, - print_keys=print_keys) - return [key for key in x.dataDict] - - def input_keys(self): - # get keys to request input ie. package data - for key in self: - print(key) - - def observation_keys(self): - # get keys to request observation file output - mfobservation.MFObservationRequester.getkeys(self, self._path) - - def keys(self): - # overrides the built in keys to print all keys, input and output - self.input_keys() - try: - self.output_keys() - except OSError as e: - if e.errno == errno.EEXIST: - pass - try: - self.observation_keys() - except KeyError: - pass - - -class MFSimulationData(object): - """ - Class containing MODFLOW simulation data and file formatting data. - - Parameters - ---------- - path : string - path on disk to the simulation - - Attributes - ---------- - indent_string : string - string used to define how much indent to use (file formatting) - internal_formatting : list - list defining string to use for internal formatting - external_formatting : list - list defining string to use for external formatting - open_close_formatting : list - list defining string to use for open/close - max_columns_of_data : int - maximum columns of data before line wraps - wrap_multidim_arrays : bool - whether to wrap line for multi-dimensional arrays at the end of a - row/column/layer - float_precision : int - number of decimal points to write for a floating point number - float_characters : int - number of characters a floating point number takes up - sci_note_upper_thres : float - numbers greater than this threshold are written in scientific notation - sci_note_lower_thres : float - numbers less than this threshold are written in scientific notation - mfpath : MFFileMgmt - file path location information for the simulation - model_dimensions : OrderedDict - dictionary containing discretization information for each model - mfdata : SimulationDict - custom dictionary containing all model data for the simulation - """ - def __init__(self, path): - # --- formatting variables --- - self.indent_string = ' ' - self.constant_formatting = ['constant', ''] - self.max_columns_of_data = 20 - self.wrap_multidim_arrays = True - self.float_precision = 8 - self.float_characters = 15 - self._sci_note_upper_thres = 100000 - self._sci_note_lower_thres = 0.001 - self.fast_write = True - self.comments_on = False - self.auto_set_sizes = True - self.debug = False - self.verbose = True - self.verbosity_level = VerbosityLevel.normal - - self._update_str_format() - - # --- file path --- - self.mfpath = MFFileMgmt(path) - - # --- ease of use variables to make working with modflow input and - # output data easier --- model dimension class for each model - self.model_dimensions = collections.OrderedDict() - - # --- model data --- - self.mfdata = SimulationDict(self.mfpath) - - # --- temporary variables --- - # other external files referenced - self.referenced_files = collections.OrderedDict() - - def set_sci_note_upper_thres(self, value): - self._sci_note_upper_thres = value - self._update_str_format() - - def set_sci_note_lower_thres(self, value): - self._sci_note_lower_thres = value - self._update_str_format() - - def _update_str_format(self): - self.reg_format_str = '{:.%dE}' % \ - self.float_precision - self.sci_format_str = '{:%d.%df' \ - '}' % (self.float_characters, - self.float_precision) - - -class MFSimulation(PackageContainer): - """ - MODFLOW Simulation Class. Entry point into any MODFLOW simulation. - - Parameters - ---------- - sim_name : string - name of the simulation. - version : string - MODFLOW version - exe_name : string - relative path to MODFLOW executable from the simulation working folder - sim_ws : string - path to simulation working folder - verbosity_level : int - verbosity level of standard output - 0 : no standard output - 1 : standard error/warning messages with some informational messages - 2 : verbose mode with full error/warning/informational messages. - this is ideal for debugging - - Attributes - ---------- - sim_name : string - name of the simulation - models : OrderedDict - all models in the simulation - exchanges : list - all exchange packages in the simulation - imsfiles : list - all ims packages in the simulation - mfdata : OrderedDict - all variables defined in the simulation. the key for a variable is - defined as a tuple. for "simulation level" packages the tuple - starts with the package type, followed by the block name, followed - by the variable name ("TDIS", "DIMENSIONS", "nper"). for "model level" - packages the tuple starts with the model name, followed by the package - name, followed by the block name, followed by the variable name ( - "MyModelName", "DIS6", "OPTIONS", "length_units"). - name_file : MFPackage - simulation name file - tdis_file - simulation tdis file - - Methods - ------- - load : (sim_name : string, version : string, exe_name : string, - sim_ws : string, strict : boolean, - verbosity_level : VerbosityLevel) : - MFSimulation - a class method that loads a simulation from files - write_simulation - writes the simulation to files - set_sim_path : (path : string) - set the file path to the root simulation folder and updates all model - file paths - get_model : (model_name : string) - : [MFModel] - returns the models in the simulation with a given model name, name file - name, or model type - add_model : (model : MFModel, sln_group : integer) - add model to the simulation - remove_model : (model_name : string) - remove model from the simulation - get_package : (type : string) - returns a simulation package based on package type - add_package : (package : MFPackage) - adds a simulation package to the simulation - remove_package : (package_name : string) - removes package from the simulation. package_name can be the - package's name, type, or package object to be removed from - the model - is_valid : () : boolean - checks the validity of the solution and all of its models and packages - - See Also - -------- - - Notes - ----- - - Examples - -------- - - >>> s = flopy6.mfsimulation.load('my simulation', 'simulation.nam') - - """ - def __init__(self, sim_name='sim', version='mf6', - exe_name='mf6.exe', sim_ws='.', - verbosity_level=1): - super(MFSimulation, self).__init__(MFSimulationData(sim_ws), sim_name) - self.simulation_data.verbosity_level = self._resolve_verbosity_level( - verbosity_level) - # verify metadata - fpdata = mfstructure.MFStructure() - if not fpdata.valid: - excpt_str = 'Invalid package metadata. Unable to load MODFLOW ' \ - 'file structure metadata.' - raise FlopyException(excpt_str) - - # initialize - self.dimensions = None - self.type = 'Simulation' - - self.version = version - self.exe_name = exe_name - self._models = collections.OrderedDict() - self._tdis_file = None - self._exchange_files = collections.OrderedDict() - self._ims_files = collections.OrderedDict() - self._ghost_node_files = {} - self._mover_files = {} - self._other_files = collections.OrderedDict() - self.structure = fpdata.sim_struct - - self._exg_file_num = {} - self._gnc_file_num = 0 - self._mvr_file_num = 0 - - self.simulation_data.mfpath.set_last_accessed_path() - - # build simulation name file - self.name_file = mfnam.ModflowNam(self, filename='mfsim.nam') - - # try to build directory structure - sim_path = self.simulation_data.mfpath.get_sim_path() - if not os.path.isdir(sim_path): - try: - os.makedirs(sim_path) - except OSError as e: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.quiet.value: - print('An error occurred when trying to create the ' - 'directory {}: {}'.format(sim_path, e.strerror)) - - # set simulation validity initially to false since the user must first - # add at least one model to the simulation and fill out the name and - # tdis files - self.valid = False - - def __getattr__(self, item): - """ - __getattr__ - used to allow for getting models and packages as if - they are attributes - - Parameters - ---------- - item : str - model or package name - - - Returns - ------- - md : Model or package object - Model or package object of type :class:flopy6.mfmodel or - :class:flopy6.mfpackage - - """ - - models = [] - if item in self.structure.model_types: - # get all models of this type - for model in self._models.values(): - if model.model_type == item: - models.append(model) - - if len(models) > 0: - return models - elif item in self._models: - return self.get_model(item) - else: - return self.get_package(item) - - def __repr__(self): - return self._get_data_str(True) - - def __str__(self): - return self._get_data_str(False) - - def _get_data_str(self, formal): - file_mgt = self.simulation_data.mfpath - data_str = 'sim_name = {}\nsim_path = {}\nexe_name = ' \ - '{}\n\n'.format(self.name, file_mgt.get_sim_path(), - self.exe_name) - - for package in self._packagelist: - pk_str = package._get_data_str(formal, False) - if formal: - if len(pk_str.strip()) > 0: - data_str = '{}###################\nPackage {}\n' \ - '###################\n\n' \ - '{}\n'.format(data_str, package._get_pname(), - pk_str) - else: - if len(pk_str.strip()) > 0: - data_str = '{}###################\nPackage {}\n' \ - '###################\n\n' \ - '{}\n'.format(data_str, package._get_pname(), - pk_str) - for model in self._models.values(): - if formal: - mod_repr = repr(model) - if len(mod_repr.strip()) > 0: - data_str = '{}@@@@@@@@@@@@@@@@@@@@\nModel {}\n' \ - '@@@@@@@@@@@@@@@@@@@@\n\n' \ - '{}\n'.format(data_str, model.name, mod_repr) - else: - mod_str = str(model) - if len(mod_str.strip()) > 0: - data_str = '{}@@@@@@@@@@@@@@@@@@@@\nModel {}\n' \ - '@@@@@@@@@@@@@@@@@@@@\n\n' \ - '{}\n'.format(data_str, model.name, mod_str) - return data_str - - @property - def model_names(self): - """ - Returns a list of model names associated with this simulation - """ - return self._models.keys() - - @classmethod - def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe', - sim_ws='.', strict=True, verbosity_level=1): - """ - Load an existing model. - - Parameters - ---------- - sim_name : string - name of the simulation. - version : string - MODFLOW version - exe_name : string - relative path to MODFLOW executable from the simulation working - folder - sim_ws : string - path to simulation working folder - strict : boolean - strict enforcement of file formatting - verbosity_level : int - verbosity level of standard output - 0 : no standard output - 1 : standard error/warning messages with some informational - messages - 2 : verbose mode with full error/warning/informational - messages. this is ideal for debugging - Returns - ------- - sim : MFSimulation object - - Examples - -------- - >>> s = flopy6.mfsimulation.load('my simulation') - """ - # initialize - instance = cls(sim_name, version, exe_name, sim_ws, verbosity_level) - verbosity_level = instance.simulation_data.verbosity_level - - if verbosity_level.value >= VerbosityLevel.normal.value: - print('loading simulation...') - - # load simulation name file - if verbosity_level.value >= VerbosityLevel.normal.value: - print(' loading simulation name file...') - instance.name_file.load(strict) - - # load TDIS file - tdis_pkg = 'tdis{}'.format(mfstructure.MFStructure(). - get_version_string()) - tdis_attr = getattr(instance.name_file, tdis_pkg) - instance._tdis_file = mftdis.ModflowTdis(instance, - filename=tdis_attr.get_data()) - - instance._tdis_file._filename = instance.simulation_data.mfdata[ - ('nam', 'timing', tdis_pkg)].get_data() - if verbosity_level.value >= VerbosityLevel.normal.value: - print(' loading tdis package...') - instance._tdis_file.load(strict) - - # load models - try: - model_recarray = instance.simulation_data.mfdata[('nam', 'models', - 'models')] - models = model_recarray.get_data() - except MFDataException as mfde: - message = 'Error occurred while loading model names from the ' \ - 'simulation name file.' - raise MFDataException(mfdata_except=mfde, - model=instance.name, - package='nam', - message=message) - for item in models: - # resolve model working folder and name file - path, name_file = os.path.split(item[1]) - model_obj = PackageContainer.model_factory(item[0][:-1].lower()) - # load model - if verbosity_level.value >= VerbosityLevel.normal.value: - print(' loading model {}...'.format(item[0].lower())) - instance._models[item[2]] = model_obj.load( - instance, - instance.structure.model_struct_objs[item[0].lower()], item[2], - name_file, version, exe_name, strict, path) - - # load exchange packages and dependent packages - try: - exchange_recarray = instance.name_file.exchanges - has_exch_data = exchange_recarray.has_data() - except MFDataException as mfde: - message = 'Error occurred while loading exchange names from the ' \ - 'simulation name file.' - raise MFDataException(mfdata_except=mfde, - model=instance.name, - package='nam', - message=message) - if has_exch_data: - try: - exch_data = exchange_recarray.get_data() - except MFDataException as mfde: - message = 'Error occurred while loading exchange names from the ' \ - 'simulation name file.' - raise MFDataException(mfdata_except=mfde, - model=instance.name, - package='nam', - message=message) - for exgfile in exch_data: - # get exchange type by removing numbers from exgtype - exchange_type = ''.join([char for char in exgfile[0] if - not char.isdigit()]).upper() - # get exchange number for this type - if not exchange_type in instance._exg_file_num: - exchange_file_num = 0 - instance._exg_file_num[exchange_type] = 1 - else: - exchange_file_num = instance._exg_file_num[exchange_type] - instance._exg_file_num[exchange_type] += 1 - - exchange_name = '{}_EXG_{}'.format(exchange_type, - exchange_file_num) - # find package class the corresponds to this exchange type - package_obj = instance.package_factory( - exchange_type.replace('-', '').lower(), '') - if not package_obj: - message = 'An error occurred while loading the ' \ - 'simulation name file. Invalid exchange type ' \ - '"{}" specified.'.format(exchange_type) - type_, value_, traceback_ = sys.exc_info() - raise MFDataException(instance.name, - 'nam', - 'nam', - 'loading simulation name file', - exchange_recarray.structure.name, - inspect.stack()[0][3], - type_, value_, traceback_, message, - instance._simulation_data.debug) - - # build and load exchange package object - exchange_file = package_obj(instance, exgtype=exgfile[0], - exgmnamea=exgfile[2], - exgmnameb=exgfile[3], - filename=exgfile[1], - pname=exchange_name, - loading_package=True) - if verbosity_level.value >= VerbosityLevel.normal.value: - print(' loading exchange package {}..' - '.'.format(exchange_file._get_pname())) - exchange_file.load(strict) - instance._exchange_files[exgfile[1]] = exchange_file - - # load simulation packages - solution_recarray = instance.simulation_data.mfdata[('nam', - 'solutiongroup', - 'solutiongroup' - )] - - try: - solution_group_dict = solution_recarray.get_data() - except MFDataException as mfde: - message = 'Error occurred while loading solution groups from ' \ - 'the simulation name file.' - raise MFDataException(mfdata_except=mfde, - model=instance.name, - package='nam', - message=message) - for solution_group in solution_group_dict.values(): - for solution_info in solution_group: - ims_file = mfims.ModflowIms(instance, filename=solution_info[1], - pname=solution_info[2]) - if verbosity_level.value >= VerbosityLevel.normal.value: - print(' loading ims package {}..' - '.'.format(ims_file._get_pname())) - ims_file.load(strict) - - instance.simulation_data.mfpath.set_last_accessed_path() - return instance - - def load_package(self, ftype, fname, pname, strict, ref_path, - dict_package_name=None, parent_package=None): - """ - loads a package from a file - - Parameters - ---------- - ftype : string - the file type - fname : string - the name of the file containing the package input - pname : string - the user-defined name for the package - strict : bool - strict mode when loading the file - ref_path : string - path to the file. uses local path if set to None - dict_package_name : string - package name for dictionary lookup - parent_package : MFPackage - parent package - - Examples - -------- - """ - if ftype == 'gnc': - if fname not in self._ghost_node_files: - # get package type from parent package - if parent_package: - package_abbr = parent_package.package_abbr[0:3] - else: - package_abbr = 'GWF' - # build package name and package - gnc_name = '{}-GNC_{}'.format(package_abbr, self._gnc_file_num) - ghost_node_file = mfgwfgnc.ModflowGwfgnc(self, filename=fname, - pname=gnc_name, - parent_file= - parent_package, - loading_package=True) - ghost_node_file.load(strict) - self._ghost_node_files[fname] = ghost_node_file - self._gnc_file_num += 1 - return ghost_node_file - elif ftype == 'mvr': - if fname not in self._mover_files: - # Get package type from parent package - if parent_package: - package_abbr = parent_package.package_abbr[0:3] - else: - package_abbr = 'GWF' - # build package name and package - mvr_name = '{}-MVR_{}'.format(package_abbr, self._mvr_file_num) - mover_file = mfgwfmvr.ModflowGwfmvr(self, filename=fname, - pname=mvr_name, - parent_file=parent_package, - loading_package=True) - mover_file.load(strict) - self._mover_files[fname] = mover_file - self._mvr_file_num += 1 - return mover_file - else: - # create package - package_obj = self.package_factory(ftype, '') - package = package_obj(self, filename=fname, pname=dict_package_name, - add_to_package_list=False, - parent_file=parent_package, - loading_package=True) - # verify that this is a utility package - utl_struct = mfstructure.MFStructure().sim_struct.utl_struct_objs - if package.package_type in utl_struct: - package.load(strict) - self._other_files[package.filename] = package - # register child package with the simulation - self._add_package(package, package.path) - if parent_package is not None: - # register child package with the parent package - parent_package._add_package(package, package.path) - else: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: Unsupported file type {} for ' - 'simulation.'.format(package.package_type)) - return package - - def register_ims_package(self, ims_file, model_list): - """ - registers an ims package with the simulation - - Parameters - ---------- - ims_file : MFPackage - ims package to register - model_list : list of strings - list of models using the ims package to be registered - - Examples - -------- - """ - if isinstance(model_list, str): - model_list = [model_list] - - if not isinstance(ims_file, mfims.ModflowIms): - comment = 'Parameter "ims_file" is not a valid ims file. ' \ - 'Expected type ModflowIms, but type "{}" was given' \ - '.'.format(type(ims_file)) - type_, value_, traceback_ = sys.exc_info() - raise MFDataException(None, - 'ims', - '', - 'registering ims package', - '', - inspect.stack()[0][3], type_, - value_, - traceback_, comment, - self.simulation_data.debug) - - in_simulation = False - pkg_with_same_name = None - for file in self._ims_files.values(): - if file is ims_file: - in_simulation = True - if file.package_name == ims_file.package_name and \ - file != ims_file: - pkg_with_same_name = file - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: ims package with name {} already exists. ' - 'New ims package will replace old package' - '.'.format(file.package_name)) - self._remove_package(self._ims_files[file.filename]) - del self._ims_files[file.filename] - # register ims package - if not in_simulation: - self._add_package(ims_file, self._get_package_path(ims_file)) - # do not allow an ims package to be registered twice with the - # same simulation - if not in_simulation: - # create unique file/package name - if ims_file.package_name is None: - file_num = len(self._ims_files) - 1 - ims_file.package_name = 'ims_{}'.format(file_num) - if ims_file.filename in self._ims_files: - ims_file.filename = MFFileMgmt.unique_file_name( - ims_file.filename, self._ims_files) - # add ims package to simulation - self._ims_files[ims_file.filename] = ims_file - - # If ims file is being replaced, replace ims filename in solution group - if pkg_with_same_name is not None and \ - self._is_in_solution_group(pkg_with_same_name.filename, 1): - # change existing solution group to reflect new ims file - self._replace_ims_in_solution_group(pkg_with_same_name.filename, - 1, ims_file.filename) - # only allow an ims package to be registered to one solution group - elif model_list is not None: - ims_in_group = self._is_in_solution_group(ims_file.filename, 1) - # add solution group to the simulation name file - solution_recarray = self.name_file.solutiongroup - solution_group_list = solution_recarray.get_active_key_list() - if len(solution_group_list) == 0: - solution_group_num = 0 - else: - solution_group_num = solution_group_list[-1][0] - - if ims_in_group: - self._append_to_ims_solution_group(ims_file.filename, - model_list) - else: - if self.name_file.mxiter.get_data(solution_group_num) is None: - self.name_file.mxiter.add_transient_key(solution_group_num) - - # associate any models in the model list to this simulation file - version_string = mfstructure.MFStructure().get_version_string() - ims_pkg = 'ims{}'.format(version_string) - new_record = [ims_pkg, ims_file.filename] - for model in model_list: - new_record.append(model) - try: - solution_recarray.append_list_as_record(new_record, - solution_group_num) - except MFDataException as mfde: - message = 'Error occurred while updating the ' \ - 'simulation name file with the ims package ' \ - 'file "{}".'.format(ims_file.filename) - raise MFDataException(mfdata_except=mfde, - package='nam', - message=message) - - def write_simulation(self, - ext_file_action=ExtFileAction.copy_relative_paths, - silent=False): - """ - writes the simulation to files - - Parameters - ---------- - ext_file_action : ExtFileAction - defines what to do with external files when the simulation path - has changed. defaults to copy_relative_paths which copies only - files with relative paths, leaving files defined by absolute - paths fixed. - silent : bool - writes out the simulation in silent mode (verbosity_level = 0) - Examples - -------- - """ - saved_verb_lvl = self.simulation_data.verbosity_level - if silent: - self.simulation_data.verbosity_level = VerbosityLevel.quiet - - # write simulation name file - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('writing simulation...') - print(' writing simulation name file...') - self.name_file.write(ext_file_action=ext_file_action) - - # write TDIS file - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print(' writing simulation tdis package...') - self._tdis_file.write(ext_file_action=ext_file_action) - - # write ims files - for ims_file in self._ims_files.values(): - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print(' writing ims package {}...'.format( - ims_file._get_pname())) - ims_file.write(ext_file_action=ext_file_action) - - # write exchange files - for exchange_file in self._exchange_files.values(): - exchange_file.write() - if hasattr(exchange_file, 'gnc_filerecord') and \ - exchange_file.gnc_filerecord.has_data(): - try: - gnc_file = exchange_file.gnc_filerecord.get_data()[0][0] - except MFDataException as mfde: - message = 'An error occurred while retrieving the ghost ' \ - 'node file record from exchange file ' \ - '"{}".'.format(exchange_file.filename) - raise MFDataException(mfdata_except=mfde, - package=exchange_file._get_pname(), - message=message) - if gnc_file in self._ghost_node_files: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print(' writing gnc package {}...'.format( - self._ghost_node_files[gnc_file]._get_pname())) - self._ghost_node_files[gnc_file].write(ext_file_action= - ext_file_action) - else: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: Ghost node file {} not loaded prior to' - ' writing. File will not be written' - '.'.format(gnc_file)) - if hasattr(exchange_file, 'mvr_filerecord') and \ - exchange_file.mvr_filerecord.has_data(): - try: - mvr_file = exchange_file.mvr_filerecord.get_data()[0][0] - except MFDataException as mfde: - message = 'An error occurred while retrieving the mover ' \ - 'file record from exchange file ' \ - '"{}".'.format(exchange_file.filename) - raise MFDataException(mfdata_except=mfde, - package=exchange_file._get_pname(), - message=message) - - if mvr_file in self._mover_files: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print(' writing mvr package {}...'.format( - self._mover_files[mvr_file]._get_pname())) - self._mover_files[mvr_file].write(ext_file_action= - ext_file_action) - else: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: Mover file {} not loaded prior to ' - 'writing. File will not be ' - 'written.'.format(mvr_file)) - - # write other packages - for pp in self._other_files.values(): - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print(' writing package {}...'.format(pp._get_pname())) - pp.write(ext_file_action=ext_file_action) - - # FIX: model working folder should be model name file folder - - # write models - for model in self._models.values(): - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print(' writing model {}...'.format(model.name)) - model.write(ext_file_action=ext_file_action) - - if ext_file_action == ExtFileAction.copy_relative_paths: - # move external files with relative paths - num_files_copied = self.simulation_data.mfpath.copy_files() - elif ext_file_action == ExtFileAction.copy_all: - # move all external files - num_files_copied = self.simulation_data.mfpath.copy_files( - copy_relative_only=False) - else: - num_files_copied = 0 - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.verbose.value and num_files_copied > 0: - print('INFORMATION: {} external files copied'.format( - num_files_copied)) - self.simulation_data.mfpath.set_last_accessed_path() - - if silent: - self.simulation_data.verbosity_level = saved_verb_lvl - - def set_sim_path(self, path): - self.simulation_data.mfpath.set_sim_path(path) - - def run_simulation(self, silent=None, pause=False, report=False, - normal_msg='normal termination', - use_async=False, cargs=None): - """ - Run the simulation. - """ - if silent is None: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - silent = False - else: - silent = True - return run_model(self.exe_name, None, - self.simulation_data.mfpath.get_sim_path(), - silent=silent, pause=pause, report=report, - normal_msg=normal_msg, use_async=use_async, cargs=cargs) - - def delete_output_files(self): - """ - Delete simulation output files. - """ - output_req = binaryfile_utils.MFOutputRequester - output_file_keys = output_req.getkeys(self.simulation_data.mfdata, - self.simulation_data.mfpath, - False) - for path in output_file_keys.binarypathdict.values(): - if os.path.isfile(path): - os.remove(path) - - def remove_package(self, package_name): - if isinstance(package_name, MFPackage): - packages = [package_name] - else: - packages = self.get_package(package_name) - if not isinstance(packages, list): - packages = [packages] - for package in packages: - if self._tdis_file is not None and \ - package.path == self._tdis_file.path: - self._tdis_file = None - if package.filename in self._exchange_files: - del self._exchange_files[package.filename] - if package.filename in self._ims_files: - del self._ims_files[package.filename] - if package.filename in self._ghost_node_files: - del self._ghost_node_files[package.filename] - if package.filename in self._mover_files: - del self._mover_files[package.filename] - if package.filename in self._other_files : - del self._other_files[package.filename] - - self._remove_package(package) - - @property - def model_dict(self): - return self._models.copy() - - def get_model(self, model_name=None): - """ - Load an existing model. - - Parameters - ---------- - model_name : string - name of model to get - - Returns - ------- - model : MFModel - - Examples - -------- - """ - if model_name is None: - for model in self._models.values(): - return model - return self._models[model_name] - - def get_exchange_file(self, filename): - """ - get a specified exchange file - - Parameters - ---------- - filename : string - name of exchange file to get - - Returns - ------- - exchange package : MFPackage - - Examples - -------- - """ - if filename in self._exchange_files: - return self._exchange_files[filename] - else: - excpt_str = 'Exchange file "{}" can not be found' \ - '.'.format(filename) - raise FlopyException(excpt_str) - - def get_mvr_file(self, filename): - """ - get a specified mover file - - Parameters - ---------- - filename : string - name of mover file to get - - Returns - ------- - mover package : MFPackage - - Examples - -------- - """ - if filename in self._mover_files: - return self._mover_files[filename] - else: - excpt_str = 'MVR file "{}" can not be ' \ - 'found.'.format(filename) - raise FlopyException(excpt_str) - - def get_gnc_file(self, filename): - """ - get a specified gnc file - - Parameters - ---------- - filename : string - name of gnc file to get - - Returns - ------- - gnc package : MFPackage - - Examples - -------- - """ - if filename in self._ghost_node_files: - return self._ghost_node_files[filename] - else: - excpt_str = 'GNC file "{}" can not be ' \ - 'found.'.format(filename) - raise FlopyException(excpt_str) - - def register_exchange_file(self, package): - """ - register an exchange package file with the simulation - - Parameters - ---------- - package : MFPackage - exchange package object to register - - Examples - -------- - """ - if package.filename not in self._exchange_files: - exgtype = package.exgtype - exgmnamea = package.exgmnamea - exgmnameb = package.exgmnameb - - if exgtype is None or exgmnamea is None or exgmnameb is None: - excpt_str = 'Exchange packages require that exgtype, ' \ - 'exgmnamea, and exgmnameb are specified.' - raise FlopyException(excpt_str) - - self._exchange_files[package.filename] = package - try: - exchange_recarray_data = self.name_file.exchanges.get_data() - except MFDataException as mfde: - message = 'An error occurred while retrieving exchange ' \ - 'data from the simulation name file. The error ' \ - 'occurred while registering exchange file ' \ - '"{}".'.format(package.filename) - raise MFDataException(mfdata_except=mfde, - package=package._get_pname(), - message=message) - if exchange_recarray_data is not None: - for index, exchange in zip(range(0, - len(exchange_recarray_data)), - exchange_recarray_data): - if exchange[1] == package.filename: - # update existing exchange - exchange_recarray_data[index][0] = exgtype - exchange_recarray_data[index][2] = exgmnamea - exchange_recarray_data[index][3] = exgmnameb - ex_recarray = self.name_file.exchanges - try: - ex_recarray.set_data(exchange_recarray_data) - except MFDataException as mfde: - message = 'An error occurred while setting ' \ - 'exchange data in the simulation name ' \ - 'file. The error occurred while ' \ - 'registering the following ' \ - 'values (exgtype, filename, ' \ - 'exgmnamea, exgmnameb): "{} {} {}' \ - '{}".'.format(exgtype, package.filename, - exgmnamea, exgmnameb) - raise MFDataException(mfdata_except=mfde, - package=package._get_pname(), - message=message) - return - try: - # add new exchange - self.name_file.exchanges.append_data([(exgtype, - package.filename, - exgmnamea, - exgmnameb)]) - except MFDataException as mfde: - message = 'An error occurred while setting exchange data ' \ - 'in the simulation name file. The error occurred ' \ - 'while registering the following values (exgtype, ' \ - 'filename, exgmnamea, exgmnameb): "{} {} {}' \ - '{}".'.format(exgtype, package.filename, exgmnamea, - exgmnameb) - raise MFDataException(mfdata_except=mfde, - package=package._get_pname(), - message=message) - if package.dimensions is None: - # resolve exchange package dimensions object - package.dimensions = package.create_package_dimensions() - - def register_package(self, package, add_to_package_list=True, - set_package_name=True, set_package_filename=True): - """ - register a package file with the simulation - - Parameters - ---------- - package : MFPackage - package to register - add_to_package_list : bool - add package to lookup list - set_package_name : bool - produce a package name for this package - set_package_filename : bool - produce a filename for this package - - Returns - ------- - (path : tuple, package structure : MFPackageStructure) - - Examples - -------- - """ - package.container_type = [PackageContainerType.simulation] - path = self._get_package_path(package) - if add_to_package_list and package.package_type.lower != 'nam': - pname = None - if package.package_name is not None: - pname = package.package_name.lower() - if package.package_type.lower() == 'tdis' and self._tdis_file is \ - not None and self._tdis_file in self._packagelist: - # tdis package already exists. there can be only one tdis - # package. remove existing tdis package - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: tdis package already exists. Replacing ' - 'existing tdis package.') - self._remove_package(self._tdis_file) - elif package.package_type.lower() == 'gnc' and \ - package.filename in self._ghost_node_files and \ - self._ghost_node_files[package.filename] in self._packagelist: - # gnc package with same file name already exists. remove old - # gnc package - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: gnc package with name {} already exists. ' - 'Replacing existing gnc package' - '.'.format(pname)) - self._remove_package(self._ghost_node_files[package.filename]) - del self._ghost_node_files[package.filename] - elif package.package_type.lower() == 'mvr' and \ - package.filename in self._mover_files and \ - self._mover_files[package.filename] in self._packagelist: - # mvr package with same file name already exists. remove old - # mvr package - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: mvr package with name {} already exists. ' - 'Replacing existing mvr package' - '.'.format(pname)) - self._remove_package(self._mover_files[package.filename]) - del self._mover_files[package.filename] - elif package.package_type.lower() != 'ims' and pname in \ - self.package_name_dict: - if self.simulation_data.verbosity_level.value >= \ - VerbosityLevel.normal.value: - print('WARNING: Package with name {} already exists. ' - 'Replacing existing package' - '.'.format(package.package_name.lower())) - self._remove_package(self.package_name_dict[pname]) - if package.package_type.lower() != 'ims': - # all but ims packages get added here. ims packages are - # added during ims package registration - self._add_package(package, path) - if package.package_type.lower() == 'nam': - return path, self.structure.name_file_struct_obj - elif package.package_type.lower() == 'tdis': - self._tdis_file = package - struct_root = mfstructure.MFStructure() - tdis_pkg = 'tdis{}'.format(struct_root.get_version_string()) - tdis_attr = getattr(self.name_file, tdis_pkg) - try: - tdis_attr.set_data(package.filename) - except MFDataException as mfde: - message = 'An error occurred while setting the tdis package ' \ - 'file name "{}". The error occurred while ' \ - 'registering the tdis package with the ' \ - 'simulation'.format(package.filename) - raise MFDataException(mfdata_except=mfde, - package=package._get_pname(), - message=message) - return path, self.structure.package_struct_objs[ - package.package_type.lower()] - elif package.package_type.lower() == 'gnc': - if package.filename not in self._ghost_node_files: - self._ghost_node_files[package.filename] = package - self._gnc_file_num += 1 - elif self._ghost_node_files[package.filename] != package: - # auto generate a unique file name and register it - file_name = MFFileMgmt.unique_file_name(package.filename, - self._ghost_node_files) - package.filename = file_name - self._ghost_node_files[file_name] = package - elif package.package_type.lower() == 'mvr': - if package.filename not in self._mover_files: - self._mover_files[package.filename] = package - else: - # auto generate a unique file name and register it - file_name = MFFileMgmt.unique_file_name(package.filename, - self._mover_files) - package.filename = file_name - self._mover_files[file_name] = package - elif package.package_type.lower() == 'ims': - # default behavior is to register the ims package with the first - # unregistered model - unregistered_models = [] - for model in self._models: - model_registered = self._is_in_solution_group(model, 2) - if not model_registered: - unregistered_models.append(model) - if unregistered_models: - self.register_ims_package(package, unregistered_models) - else: - self.register_ims_package(package, None) - return path, self.structure.package_struct_objs[ - package.package_type.lower()] - else: - self._other_files[package.filename] = package - - if package.package_type.lower() in self.structure.package_struct_objs: - return path, self.structure.package_struct_objs[ - package.package_type.lower()] - elif package.package_type.lower() in self.structure.utl_struct_objs: - return path, self.structure.utl_struct_objs[ - package.package_type.lower()] - else: - excpt_str = 'Invalid package type "{}". Unable to register ' \ - 'package.'.format(package.package_type) - print(excpt_str) - raise FlopyException(excpt_str) - - def register_model(self, model, model_type, model_name, model_namefile): - """ - add a model to the simulation. - - Parameters - ---------- - model : MFModel - model object to add to simulation - sln_group : string - solution group of model - - Returns - ------- - model_structure_object : MFModelStructure - - Examples - -------- - """ - - # get model structure from model type - if model_type not in self.structure.model_struct_objs: - message = 'Invalid model type: "{}".'.format(model_type) - type_, value_, traceback_ = sys.exc_info() - raise MFDataException(model.name, - '', model.name, - 'registering model', 'sim', - inspect.stack()[0][3], - type_, value_, traceback_, message, - self.simulation_data.debug) - - # add model - self._models[model_name] = model - - # update simulation name file - self.name_file.models.append_list_as_record([model_type, - model_namefile, - model_name]) - - if len(self._ims_files) > 0: - # register model with first ims file found - first_ims_key = next(iter(self._ims_files)) - self.register_ims_package(self._ims_files[first_ims_key], - model_name) - - return self.structure.model_struct_objs[model_type] - - def get_ims_package(self, key): - if key in self._ims_files: - return self._ims_files[key] - return None - - def remove_model(self, model_name): - """ - remove a model from the simulation. - - Parameters - ---------- - model_name : string - model name to remove from simulation - - Examples - -------- - """ - - # Remove model - del self._models[model_name] - - # TODO: Fully implement this - # Update simulation name file - - def is_valid(self): - """ - check all packages and models in the simulation to verify validity - - Returns - ---------- - valid : boolean - simulation validity - - Examples - -------- - """ - - # name file valid - if not self.name_file.is_valid(): - return False - - # tdis file valid - if not self._tdis_file.is_valid(): - return False - - # exchanges valid - for exchange in self._exchange_files: - if not exchange.is_valid(): - return False - - # ims files valid - for imsfile in self._ims_files.values(): - if not imsfile.is_valid(): - return False - - # a model exists - if not self._models: - return False - - # models valid - for key in self._models: - if not self._models[key].is_valid(): - return False - - # each model has an imsfile - - return True - - @staticmethod - def _resolve_verbosity_level(verbosity_level): - if verbosity_level == 0: - return VerbosityLevel.quiet - elif verbosity_level == 1: - return VerbosityLevel.normal - elif verbosity_level == 2: - return VerbosityLevel.verbose - else: - return verbosity_level - - @staticmethod - def _get_package_path(package): - if package.parent_file is not None: - return (package.parent_file.path) + (package.package_type,) - else: - return (package.package_type,) - - def _append_to_ims_solution_group(self, ims_file, new_models): - solution_recarray = self.name_file.solutiongroup - for solution_group_num in solution_recarray.get_active_key_list(): - try: - rec_array = solution_recarray.get_data(solution_group_num[0]) - except MFDataException as mfde: - message = 'An error occurred while getting solution group' \ - '"{}" from the simulation name file' \ - '.'.format(solution_group_num[0]) - raise MFDataException(mfdata_except=mfde, - package='nam', - message=message) - new_array = [] - for index, record in enumerate(rec_array): - new_record = [] - rec_model_dict = {} - for index, item in enumerate(record): - if record[1] == ims_file or item not in new_models: - new_record.append(item) - if index > 1: - rec_model_dict[item] = 1 - - if record[1] == ims_file: - for model in new_models: - if model not in rec_model_dict: - new_record.append(model) - - new_array.append(tuple(new_record)) - solution_recarray.set_data(new_array, - solution_group_num[0]) - - def _replace_ims_in_solution_group(self, item, index, new_item): - solution_recarray = self.name_file.solutiongroup - for solution_group_num in solution_recarray.get_active_key_list(): - try: - rec_array = solution_recarray.get_data(solution_group_num[0]) - except MFDataException as mfde: - message = 'An error occurred while getting solution group' \ - '"{}" from the simulation name file. The error ' \ - 'occurred while replacing IMS file "{}" with "{}"' \ - 'at index "{}"'.format(solution_group_num[0], - item, new_item, index) - raise MFDataException(mfdata_except=mfde, - package='nam', - message=message) - if rec_array is not None: - for rec_item in rec_array: - if rec_item[index] == item: - rec_item[index] = new_item - - def _is_in_solution_group(self, item, index): - solution_recarray = self.name_file.solutiongroup - for solution_group_num in solution_recarray.get_active_key_list(): - try: - rec_array = solution_recarray.get_data(solution_group_num[0]) - except MFDataException as mfde: - message = 'An error occurred while getting solution group' \ - '"{}" from the simulation name file. The error ' \ - 'occurred while verifying file "{}" at index "{}" ' \ - 'is in the simulation name file' \ - '.'.format(solution_group_num[0], item, index) - raise MFDataException(mfdata_except=mfde, - package='nam', - message=message) - - if rec_array is not None: - for rec_item in rec_array: - if rec_item[index] == item: - return True - return False - - - def plot(self, model_list=None, SelPackList=None, **kwargs): - """ - Method to plot a whole simulation or a series of models - that are part of a simualtion - - Parameters: - model_list: (list) list of model names to plot, if none - all models will be plotted - SelPackList: (list) list of package names to plot, if none - all packages will be plotted - - kwargs: - filename_base : str - Base file name that will be used to automatically generate file - names for output image files. Plots will be exported as image - files if file_name_base is not None. (default is None) - file_extension : str - Valid matplotlib.pyplot file extension for savefig(). Only used - if filename_base is not None. (default is 'png') - mflay : int - MODFLOW zero-based layer number to return. If None, then all - all layers will be included. (default is None) - kper : int - MODFLOW zero-based stress period number to return. - (default is zero) - key : str - MfList dictionary key. (default is None) - - - Returns: - axes: (list) matplotlib.pyplot.axes objects - """ - from flopy.plot.plotutil import PlotUtilities - - axes = PlotUtilities._plot_simulation_helper(self, - model_list=model_list, - SelPackList=SelPackList, - **kwargs) +""" +mfsimulation module. contains the MFSimulation class + + +""" +import errno, sys, inspect +import collections +import os.path +from ...mbase import run_model +from ..mfbase import PackageContainer, MFFileMgmt, ExtFileAction, \ + PackageContainerType, MFDataException, FlopyException, \ + VerbosityLevel +from ..mfpackage import MFPackage +from ..data.mfstructure import DatumType +from ..data import mfstructure +from ..utils import binaryfile_utils +from ..utils import mfobservation +from ..modflow import mfnam, mfims, mftdis, mfgwfgnc, mfgwfmvr +from ..data.mfdatautil import MFComment + + +class SimulationDict(collections.OrderedDict): + """ + Class containing custom dictionary for MODFLOW simulations. Behaves as an + OrderedDict with some additional features described below. + + Parameters + ---------- + path : MFFileMgmt + object containing path information for the simulation + + Methods + ------- + output_keys : (print_keys: boolean) : list + returns a list of output data keys the dictionary supports for output + data, print_keys allows those keys to be printed to output. + input_keys : () + prints all input data keys + observation_keys : () + prints observation keys + keys : () + print all keys, input and output + plot : (key : string, **kwargs) + plots data with key 'key' using **kwargs for plot options + shapefile : (key : string, **kwargs) + create shapefile from data with key 'key' and with additional fields + in **kwargs + """ + def __init__(self, path, *args): + self._path = path + collections.OrderedDict.__init__(self) + + def __getitem__(self, key): + # check if the key refers to a binary output file, or an observation + # output file, if so override the dictionary request and call output + # requester classes + + # FIX: Transport - Include transport output files + if key[1] in ('CBC', 'HDS', 'DDN', 'UCN'): + val = binaryfile_utils.MFOutput(self, self._path, key) + return val.data + + elif key[-1] == 'Observations': + val = mfobservation.MFObservation(self, self._path, key) + return val.data + + val = collections.OrderedDict.__getitem__(self, key) + return val + + def __setitem__(self, key, val): + collections.OrderedDict.__setitem__(self, key, val) + + def find_in_path(self, key_path, key_leaf): + key_path_size = len(key_path) + for key, item in self.items(): + if key[:key_path_size] == key_path: + if key[-1] == key_leaf: + # found key_leaf as a key in the dictionary + return item, None + if not isinstance(item, MFComment): + data_item_index = 0 + data_item_structures = item.structure.data_item_structures + for data_item_struct in data_item_structures: + if data_item_struct.name == key_leaf: + # found key_leaf as a data item name in the data in + # the dictionary + return item, data_item_index + if data_item_struct.type != DatumType.keyword: + data_item_index += 1 + return None, None + + def output_keys(self, print_keys=True): + # get keys to request binary output + x = binaryfile_utils.MFOutputRequester.getkeys(self, self._path, + print_keys=print_keys) + return [key for key in x.dataDict] + + def input_keys(self): + # get keys to request input ie. package data + for key in self: + print(key) + + def observation_keys(self): + # get keys to request observation file output + mfobservation.MFObservationRequester.getkeys(self, self._path) + + def keys(self): + # overrides the built in keys to print all keys, input and output + self.input_keys() + try: + self.output_keys() + except OSError as e: + if e.errno == errno.EEXIST: + pass + try: + self.observation_keys() + except KeyError: + pass + + +class MFSimulationData(object): + """ + Class containing MODFLOW simulation data and file formatting data. + + Parameters + ---------- + path : string + path on disk to the simulation + + Attributes + ---------- + indent_string : string + string used to define how much indent to use (file formatting) + internal_formatting : list + list defining string to use for internal formatting + external_formatting : list + list defining string to use for external formatting + open_close_formatting : list + list defining string to use for open/close + max_columns_of_data : int + maximum columns of data before line wraps + wrap_multidim_arrays : bool + whether to wrap line for multi-dimensional arrays at the end of a + row/column/layer + float_precision : int + number of decimal points to write for a floating point number + float_characters : int + number of characters a floating point number takes up + sci_note_upper_thres : float + numbers greater than this threshold are written in scientific notation + sci_note_lower_thres : float + numbers less than this threshold are written in scientific notation + mfpath : MFFileMgmt + file path location information for the simulation + model_dimensions : OrderedDict + dictionary containing discretization information for each model + mfdata : SimulationDict + custom dictionary containing all model data for the simulation + """ + def __init__(self, path): + # --- formatting variables --- + self.indent_string = ' ' + self.constant_formatting = ['constant', ''] + self.max_columns_of_data = 20 + self.wrap_multidim_arrays = True + self.float_precision = 8 + self.float_characters = 15 + self._sci_note_upper_thres = 100000 + self._sci_note_lower_thres = 0.001 + self.fast_write = True + self.comments_on = False + self.auto_set_sizes = True + self.debug = False + self.verbose = True + self.verbosity_level = VerbosityLevel.normal + + self._update_str_format() + + # --- file path --- + self.mfpath = MFFileMgmt(path) + + # --- ease of use variables to make working with modflow input and + # output data easier --- model dimension class for each model + self.model_dimensions = collections.OrderedDict() + + # --- model data --- + self.mfdata = SimulationDict(self.mfpath) + + # --- temporary variables --- + # other external files referenced + self.referenced_files = collections.OrderedDict() + + def set_sci_note_upper_thres(self, value): + self._sci_note_upper_thres = value + self._update_str_format() + + def set_sci_note_lower_thres(self, value): + self._sci_note_lower_thres = value + self._update_str_format() + + def _update_str_format(self): + self.reg_format_str = '{:.%dE}' % \ + self.float_precision + self.sci_format_str = '{:%d.%df' \ + '}' % (self.float_characters, + self.float_precision) + + +class MFSimulation(PackageContainer): + """ + MODFLOW Simulation Class. Entry point into any MODFLOW simulation. + + Parameters + ---------- + sim_name : string + name of the simulation. + version : string + MODFLOW version + exe_name : string + relative path to MODFLOW executable from the simulation working folder + sim_ws : string + path to simulation working folder + verbosity_level : int + verbosity level of standard output + 0 : no standard output + 1 : standard error/warning messages with some informational messages + 2 : verbose mode with full error/warning/informational messages. + this is ideal for debugging + + Attributes + ---------- + sim_name : string + name of the simulation + models : OrderedDict + all models in the simulation + exchanges : list + all exchange packages in the simulation + imsfiles : list + all ims packages in the simulation + mfdata : OrderedDict + all variables defined in the simulation. the key for a variable is + defined as a tuple. for "simulation level" packages the tuple + starts with the package type, followed by the block name, followed + by the variable name ("TDIS", "DIMENSIONS", "nper"). for "model level" + packages the tuple starts with the model name, followed by the package + name, followed by the block name, followed by the variable name ( + "MyModelName", "DIS6", "OPTIONS", "length_units"). + name_file : MFPackage + simulation name file + tdis_file + simulation tdis file + + Methods + ------- + load : (sim_name : string, version : string, exe_name : string, + sim_ws : string, strict : boolean, + verbosity_level : VerbosityLevel) : + MFSimulation + a class method that loads a simulation from files + write_simulation + writes the simulation to files + set_sim_path : (path : string) + set the file path to the root simulation folder and updates all model + file paths + get_model : (model_name : string) + : [MFModel] + returns the models in the simulation with a given model name, name file + name, or model type + add_model : (model : MFModel, sln_group : integer) + add model to the simulation + remove_model : (model_name : string) + remove model from the simulation + get_package : (type : string) + returns a simulation package based on package type + add_package : (package : MFPackage) + adds a simulation package to the simulation + remove_package : (package_name : string) + removes package from the simulation. package_name can be the + package's name, type, or package object to be removed from + the model + is_valid : () : boolean + checks the validity of the solution and all of its models and packages + + See Also + -------- + + Notes + ----- + + Examples + -------- + + >>> s = flopy6.mfsimulation.load('my simulation', 'simulation.nam') + + """ + def __init__(self, sim_name='sim', version='mf6', + exe_name='mf6.exe', sim_ws='.', + verbosity_level=1): + super(MFSimulation, self).__init__(MFSimulationData(sim_ws), sim_name) + self.simulation_data.verbosity_level = self._resolve_verbosity_level( + verbosity_level) + # verify metadata + fpdata = mfstructure.MFStructure() + if not fpdata.valid: + excpt_str = 'Invalid package metadata. Unable to load MODFLOW ' \ + 'file structure metadata.' + raise FlopyException(excpt_str) + + # initialize + self.dimensions = None + self.type = 'Simulation' + + self.version = version + self.exe_name = exe_name + self._models = collections.OrderedDict() + self._tdis_file = None + self._exchange_files = collections.OrderedDict() + self._ims_files = collections.OrderedDict() + self._ghost_node_files = {} + self._mover_files = {} + self._other_files = collections.OrderedDict() + self.structure = fpdata.sim_struct + + self._exg_file_num = {} + self._gnc_file_num = 0 + self._mvr_file_num = 0 + + self.simulation_data.mfpath.set_last_accessed_path() + + # build simulation name file + self.name_file = mfnam.ModflowNam(self, filename='mfsim.nam') + + # try to build directory structure + sim_path = self.simulation_data.mfpath.get_sim_path() + if not os.path.isdir(sim_path): + try: + os.makedirs(sim_path) + except OSError as e: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.quiet.value: + print('An error occurred when trying to create the ' + 'directory {}: {}'.format(sim_path, e.strerror)) + + # set simulation validity initially to false since the user must first + # add at least one model to the simulation and fill out the name and + # tdis files + self.valid = False + + def __getattr__(self, item): + """ + __getattr__ - used to allow for getting models and packages as if + they are attributes + + Parameters + ---------- + item : str + model or package name + + + Returns + ------- + md : Model or package object + Model or package object of type :class:flopy6.mfmodel or + :class:flopy6.mfpackage + + """ + + models = [] + if item in self.structure.model_types: + # get all models of this type + for model in self._models.values(): + if model.model_type == item: + models.append(model) + + if len(models) > 0: + return models + elif item in self._models: + return self.get_model(item) + else: + return self.get_package(item) + + def __repr__(self): + return self._get_data_str(True) + + def __str__(self): + return self._get_data_str(False) + + def _get_data_str(self, formal): + file_mgt = self.simulation_data.mfpath + data_str = 'sim_name = {}\nsim_path = {}\nexe_name = ' \ + '{}\n\n'.format(self.name, file_mgt.get_sim_path(), + self.exe_name) + + for package in self._packagelist: + pk_str = package._get_data_str(formal, False) + if formal: + if len(pk_str.strip()) > 0: + data_str = '{}###################\nPackage {}\n' \ + '###################\n\n' \ + '{}\n'.format(data_str, package._get_pname(), + pk_str) + else: + if len(pk_str.strip()) > 0: + data_str = '{}###################\nPackage {}\n' \ + '###################\n\n' \ + '{}\n'.format(data_str, package._get_pname(), + pk_str) + for model in self._models.values(): + if formal: + mod_repr = repr(model) + if len(mod_repr.strip()) > 0: + data_str = '{}@@@@@@@@@@@@@@@@@@@@\nModel {}\n' \ + '@@@@@@@@@@@@@@@@@@@@\n\n' \ + '{}\n'.format(data_str, model.name, mod_repr) + else: + mod_str = str(model) + if len(mod_str.strip()) > 0: + data_str = '{}@@@@@@@@@@@@@@@@@@@@\nModel {}\n' \ + '@@@@@@@@@@@@@@@@@@@@\n\n' \ + '{}\n'.format(data_str, model.name, mod_str) + return data_str + + @property + def model_names(self): + """ + Returns a list of model names associated with this simulation + """ + return self._models.keys() + + @classmethod + def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe', + sim_ws='.', strict=True, verbosity_level=1): + """ + Load an existing model. + + Parameters + ---------- + sim_name : string + name of the simulation. + version : string + MODFLOW version + exe_name : string + relative path to MODFLOW executable from the simulation working + folder + sim_ws : string + path to simulation working folder + strict : boolean + strict enforcement of file formatting + verbosity_level : int + verbosity level of standard output + 0 : no standard output + 1 : standard error/warning messages with some informational + messages + 2 : verbose mode with full error/warning/informational + messages. this is ideal for debugging + Returns + ------- + sim : MFSimulation object + + Examples + -------- + >>> s = flopy6.mfsimulation.load('my simulation') + """ + # initialize + instance = cls(sim_name, version, exe_name, sim_ws, verbosity_level) + verbosity_level = instance.simulation_data.verbosity_level + + if verbosity_level.value >= VerbosityLevel.normal.value: + print('loading simulation...') + + # load simulation name file + if verbosity_level.value >= VerbosityLevel.normal.value: + print(' loading simulation name file...') + instance.name_file.load(strict) + + # load TDIS file + tdis_pkg = 'tdis{}'.format(mfstructure.MFStructure(). + get_version_string()) + tdis_attr = getattr(instance.name_file, tdis_pkg) + instance._tdis_file = mftdis.ModflowTdis(instance, + filename=tdis_attr.get_data()) + + instance._tdis_file._filename = instance.simulation_data.mfdata[ + ('nam', 'timing', tdis_pkg)].get_data() + if verbosity_level.value >= VerbosityLevel.normal.value: + print(' loading tdis package...') + instance._tdis_file.load(strict) + + # load models + try: + model_recarray = instance.simulation_data.mfdata[('nam', 'models', + 'models')] + models = model_recarray.get_data() + except MFDataException as mfde: + message = 'Error occurred while loading model names from the ' \ + 'simulation name file.' + raise MFDataException(mfdata_except=mfde, + model=instance.name, + package='nam', + message=message) + for item in models: + # resolve model working folder and name file + path, name_file = os.path.split(item[1]) + model_obj = PackageContainer.model_factory(item[0][:-1].lower()) + # load model + if verbosity_level.value >= VerbosityLevel.normal.value: + print(' loading model {}...'.format(item[0].lower())) + instance._models[item[2]] = model_obj.load( + instance, + instance.structure.model_struct_objs[item[0].lower()], item[2], + name_file, version, exe_name, strict, path) + + # load exchange packages and dependent packages + try: + exchange_recarray = instance.name_file.exchanges + has_exch_data = exchange_recarray.has_data() + except MFDataException as mfde: + message = 'Error occurred while loading exchange names from the ' \ + 'simulation name file.' + raise MFDataException(mfdata_except=mfde, + model=instance.name, + package='nam', + message=message) + if has_exch_data: + try: + exch_data = exchange_recarray.get_data() + except MFDataException as mfde: + message = 'Error occurred while loading exchange names from the ' \ + 'simulation name file.' + raise MFDataException(mfdata_except=mfde, + model=instance.name, + package='nam', + message=message) + for exgfile in exch_data: + # get exchange type by removing numbers from exgtype + exchange_type = ''.join([char for char in exgfile[0] if + not char.isdigit()]).upper() + # get exchange number for this type + if not exchange_type in instance._exg_file_num: + exchange_file_num = 0 + instance._exg_file_num[exchange_type] = 1 + else: + exchange_file_num = instance._exg_file_num[exchange_type] + instance._exg_file_num[exchange_type] += 1 + + exchange_name = '{}_EXG_{}'.format(exchange_type, + exchange_file_num) + # find package class the corresponds to this exchange type + package_obj = instance.package_factory( + exchange_type.replace('-', '').lower(), '') + if not package_obj: + message = 'An error occurred while loading the ' \ + 'simulation name file. Invalid exchange type ' \ + '"{}" specified.'.format(exchange_type) + type_, value_, traceback_ = sys.exc_info() + raise MFDataException(instance.name, + 'nam', + 'nam', + 'loading simulation name file', + exchange_recarray.structure.name, + inspect.stack()[0][3], + type_, value_, traceback_, message, + instance._simulation_data.debug) + + # build and load exchange package object + exchange_file = package_obj(instance, exgtype=exgfile[0], + exgmnamea=exgfile[2], + exgmnameb=exgfile[3], + filename=exgfile[1], + pname=exchange_name, + loading_package=True) + if verbosity_level.value >= VerbosityLevel.normal.value: + print(' loading exchange package {}..' + '.'.format(exchange_file._get_pname())) + exchange_file.load(strict) + instance._exchange_files[exgfile[1]] = exchange_file + + # load simulation packages + solution_recarray = instance.simulation_data.mfdata[('nam', + 'solutiongroup', + 'solutiongroup' + )] + + try: + solution_group_dict = solution_recarray.get_data() + except MFDataException as mfde: + message = 'Error occurred while loading solution groups from ' \ + 'the simulation name file.' + raise MFDataException(mfdata_except=mfde, + model=instance.name, + package='nam', + message=message) + for solution_group in solution_group_dict.values(): + for solution_info in solution_group: + ims_file = mfims.ModflowIms(instance, filename=solution_info[1], + pname=solution_info[2]) + if verbosity_level.value >= VerbosityLevel.normal.value: + print(' loading ims package {}..' + '.'.format(ims_file._get_pname())) + ims_file.load(strict) + + instance.simulation_data.mfpath.set_last_accessed_path() + return instance + + def load_package(self, ftype, fname, pname, strict, ref_path, + dict_package_name=None, parent_package=None): + """ + loads a package from a file + + Parameters + ---------- + ftype : string + the file type + fname : string + the name of the file containing the package input + pname : string + the user-defined name for the package + strict : bool + strict mode when loading the file + ref_path : string + path to the file. uses local path if set to None + dict_package_name : string + package name for dictionary lookup + parent_package : MFPackage + parent package + + Examples + -------- + """ + if ftype == 'gnc': + if fname not in self._ghost_node_files: + # get package type from parent package + if parent_package: + package_abbr = parent_package.package_abbr[0:3] + else: + package_abbr = 'GWF' + # build package name and package + gnc_name = '{}-GNC_{}'.format(package_abbr, self._gnc_file_num) + ghost_node_file = mfgwfgnc.ModflowGwfgnc(self, filename=fname, + pname=gnc_name, + parent_file= + parent_package, + loading_package=True) + ghost_node_file.load(strict) + self._ghost_node_files[fname] = ghost_node_file + self._gnc_file_num += 1 + return ghost_node_file + elif ftype == 'mvr': + if fname not in self._mover_files: + # Get package type from parent package + if parent_package: + package_abbr = parent_package.package_abbr[0:3] + else: + package_abbr = 'GWF' + # build package name and package + mvr_name = '{}-MVR_{}'.format(package_abbr, self._mvr_file_num) + mover_file = mfgwfmvr.ModflowGwfmvr(self, filename=fname, + pname=mvr_name, + parent_file=parent_package, + loading_package=True) + mover_file.load(strict) + self._mover_files[fname] = mover_file + self._mvr_file_num += 1 + return mover_file + else: + # create package + package_obj = self.package_factory(ftype, '') + package = package_obj(self, filename=fname, pname=dict_package_name, + add_to_package_list=False, + parent_file=parent_package, + loading_package=True) + # verify that this is a utility package + utl_struct = mfstructure.MFStructure().sim_struct.utl_struct_objs + if package.package_type in utl_struct: + package.load(strict) + self._other_files[package.filename] = package + # register child package with the simulation + self._add_package(package, package.path) + if parent_package is not None: + # register child package with the parent package + parent_package._add_package(package, package.path) + else: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: Unsupported file type {} for ' + 'simulation.'.format(package.package_type)) + return package + + def register_ims_package(self, ims_file, model_list): + """ + registers an ims package with the simulation + + Parameters + ---------- + ims_file : MFPackage + ims package to register + model_list : list of strings + list of models using the ims package to be registered + + Examples + -------- + """ + if isinstance(model_list, str): + model_list = [model_list] + + if not isinstance(ims_file, mfims.ModflowIms): + comment = 'Parameter "ims_file" is not a valid ims file. ' \ + 'Expected type ModflowIms, but type "{}" was given' \ + '.'.format(type(ims_file)) + type_, value_, traceback_ = sys.exc_info() + raise MFDataException(None, + 'ims', + '', + 'registering ims package', + '', + inspect.stack()[0][3], type_, + value_, + traceback_, comment, + self.simulation_data.debug) + + in_simulation = False + pkg_with_same_name = None + for file in self._ims_files.values(): + if file is ims_file: + in_simulation = True + if file.package_name == ims_file.package_name and \ + file != ims_file: + pkg_with_same_name = file + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: ims package with name {} already exists. ' + 'New ims package will replace old package' + '.'.format(file.package_name)) + self._remove_package(self._ims_files[file.filename]) + del self._ims_files[file.filename] + # register ims package + if not in_simulation: + self._add_package(ims_file, self._get_package_path(ims_file)) + # do not allow an ims package to be registered twice with the + # same simulation + if not in_simulation: + # create unique file/package name + if ims_file.package_name is None: + file_num = len(self._ims_files) - 1 + ims_file.package_name = 'ims_{}'.format(file_num) + if ims_file.filename in self._ims_files: + ims_file.filename = MFFileMgmt.unique_file_name( + ims_file.filename, self._ims_files) + # add ims package to simulation + self._ims_files[ims_file.filename] = ims_file + + # If ims file is being replaced, replace ims filename in solution group + if pkg_with_same_name is not None and \ + self._is_in_solution_group(pkg_with_same_name.filename, 1): + # change existing solution group to reflect new ims file + self._replace_ims_in_solution_group(pkg_with_same_name.filename, + 1, ims_file.filename) + # only allow an ims package to be registered to one solution group + elif model_list is not None: + ims_in_group = self._is_in_solution_group(ims_file.filename, 1) + # add solution group to the simulation name file + solution_recarray = self.name_file.solutiongroup + solution_group_list = solution_recarray.get_active_key_list() + if len(solution_group_list) == 0: + solution_group_num = 0 + else: + solution_group_num = solution_group_list[-1][0] + + if ims_in_group: + self._append_to_ims_solution_group(ims_file.filename, + model_list) + else: + if self.name_file.mxiter.get_data(solution_group_num) is None: + self.name_file.mxiter.add_transient_key(solution_group_num) + + # associate any models in the model list to this simulation file + version_string = mfstructure.MFStructure().get_version_string() + ims_pkg = 'ims{}'.format(version_string) + new_record = [ims_pkg, ims_file.filename] + for model in model_list: + new_record.append(model) + try: + solution_recarray.append_list_as_record(new_record, + solution_group_num) + except MFDataException as mfde: + message = 'Error occurred while updating the ' \ + 'simulation name file with the ims package ' \ + 'file "{}".'.format(ims_file.filename) + raise MFDataException(mfdata_except=mfde, + package='nam', + message=message) + + def write_simulation(self, + ext_file_action=ExtFileAction.copy_relative_paths, + silent=False): + """ + writes the simulation to files + + Parameters + ---------- + ext_file_action : ExtFileAction + defines what to do with external files when the simulation path + has changed. defaults to copy_relative_paths which copies only + files with relative paths, leaving files defined by absolute + paths fixed. + silent : bool + writes out the simulation in silent mode (verbosity_level = 0) + Examples + -------- + """ + saved_verb_lvl = self.simulation_data.verbosity_level + if silent: + self.simulation_data.verbosity_level = VerbosityLevel.quiet + + # write simulation name file + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('writing simulation...') + print(' writing simulation name file...') + self.name_file.write(ext_file_action=ext_file_action) + + # write TDIS file + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print(' writing simulation tdis package...') + self._tdis_file.write(ext_file_action=ext_file_action) + + # write ims files + for ims_file in self._ims_files.values(): + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print(' writing ims package {}...'.format( + ims_file._get_pname())) + ims_file.write(ext_file_action=ext_file_action) + + # write exchange files + for exchange_file in self._exchange_files.values(): + exchange_file.write() + if hasattr(exchange_file, 'gnc_filerecord') and \ + exchange_file.gnc_filerecord.has_data(): + try: + gnc_file = exchange_file.gnc_filerecord.get_data()[0][0] + except MFDataException as mfde: + message = 'An error occurred while retrieving the ghost ' \ + 'node file record from exchange file ' \ + '"{}".'.format(exchange_file.filename) + raise MFDataException(mfdata_except=mfde, + package=exchange_file._get_pname(), + message=message) + if gnc_file in self._ghost_node_files: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print(' writing gnc package {}...'.format( + self._ghost_node_files[gnc_file]._get_pname())) + self._ghost_node_files[gnc_file].write(ext_file_action= + ext_file_action) + else: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: Ghost node file {} not loaded prior to' + ' writing. File will not be written' + '.'.format(gnc_file)) + if hasattr(exchange_file, 'mvr_filerecord') and \ + exchange_file.mvr_filerecord.has_data(): + try: + mvr_file = exchange_file.mvr_filerecord.get_data()[0][0] + except MFDataException as mfde: + message = 'An error occurred while retrieving the mover ' \ + 'file record from exchange file ' \ + '"{}".'.format(exchange_file.filename) + raise MFDataException(mfdata_except=mfde, + package=exchange_file._get_pname(), + message=message) + + if mvr_file in self._mover_files: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print(' writing mvr package {}...'.format( + self._mover_files[mvr_file]._get_pname())) + self._mover_files[mvr_file].write(ext_file_action= + ext_file_action) + else: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: Mover file {} not loaded prior to ' + 'writing. File will not be ' + 'written.'.format(mvr_file)) + + if ext_file_action == ExtFileAction.copy_relative_paths: + # move external files with relative paths + num_files_copied = self.simulation_data.mfpath.copy_files() + elif ext_file_action == ExtFileAction.copy_all: + # move all external files + num_files_copied = self.simulation_data.mfpath.copy_files( + copy_relative_only=False) + else: + num_files_copied = 0 + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.verbose.value and num_files_copied > 0: + print('INFORMATION: {} external files copied'.format( + num_files_copied)) + + # write other packages + for pp in self._other_files.values(): + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print(' writing package {}...'.format(pp._get_pname())) + pp.write(ext_file_action=ext_file_action) + + # FIX: model working folder should be model name file folder + + # write models + for model in self._models.values(): + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print(' writing model {}...'.format(model.name)) + model.write(ext_file_action=ext_file_action) + + self.simulation_data.mfpath.set_last_accessed_path() + + if silent: + self.simulation_data.verbosity_level = saved_verb_lvl + + def set_sim_path(self, path): + self.simulation_data.mfpath.set_sim_path(path) + + def run_simulation(self, silent=None, pause=False, report=False, + normal_msg='normal termination', + use_async=False, cargs=None): + """ + Run the simulation. + """ + if silent is None: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + silent = False + else: + silent = True + return run_model(self.exe_name, None, + self.simulation_data.mfpath.get_sim_path(), + silent=silent, pause=pause, report=report, + normal_msg=normal_msg, use_async=use_async, cargs=cargs) + + def delete_output_files(self): + """ + Delete simulation output files. + """ + output_req = binaryfile_utils.MFOutputRequester + output_file_keys = output_req.getkeys(self.simulation_data.mfdata, + self.simulation_data.mfpath, + False) + for path in output_file_keys.binarypathdict.values(): + if os.path.isfile(path): + os.remove(path) + + def remove_package(self, package_name): + if isinstance(package_name, MFPackage): + packages = [package_name] + else: + packages = self.get_package(package_name) + if not isinstance(packages, list): + packages = [packages] + for package in packages: + if self._tdis_file is not None and \ + package.path == self._tdis_file.path: + self._tdis_file = None + if package.filename in self._exchange_files: + del self._exchange_files[package.filename] + if package.filename in self._ims_files: + del self._ims_files[package.filename] + if package.filename in self._ghost_node_files: + del self._ghost_node_files[package.filename] + if package.filename in self._mover_files: + del self._mover_files[package.filename] + if package.filename in self._other_files : + del self._other_files[package.filename] + + self._remove_package(package) + + @property + def model_dict(self): + return self._models.copy() + + def get_model(self, model_name=None): + """ + Load an existing model. + + Parameters + ---------- + model_name : string + name of model to get + + Returns + ------- + model : MFModel + + Examples + -------- + """ + if model_name is None: + for model in self._models.values(): + return model + return self._models[model_name] + + def get_exchange_file(self, filename): + """ + get a specified exchange file + + Parameters + ---------- + filename : string + name of exchange file to get + + Returns + ------- + exchange package : MFPackage + + Examples + -------- + """ + if filename in self._exchange_files: + return self._exchange_files[filename] + else: + excpt_str = 'Exchange file "{}" can not be found' \ + '.'.format(filename) + raise FlopyException(excpt_str) + + def get_mvr_file(self, filename): + """ + get a specified mover file + + Parameters + ---------- + filename : string + name of mover file to get + + Returns + ------- + mover package : MFPackage + + Examples + -------- + """ + if filename in self._mover_files: + return self._mover_files[filename] + else: + excpt_str = 'MVR file "{}" can not be ' \ + 'found.'.format(filename) + raise FlopyException(excpt_str) + + def get_gnc_file(self, filename): + """ + get a specified gnc file + + Parameters + ---------- + filename : string + name of gnc file to get + + Returns + ------- + gnc package : MFPackage + + Examples + -------- + """ + if filename in self._ghost_node_files: + return self._ghost_node_files[filename] + else: + excpt_str = 'GNC file "{}" can not be ' \ + 'found.'.format(filename) + raise FlopyException(excpt_str) + + def register_exchange_file(self, package): + """ + register an exchange package file with the simulation + + Parameters + ---------- + package : MFPackage + exchange package object to register + + Examples + -------- + """ + if package.filename not in self._exchange_files: + exgtype = package.exgtype + exgmnamea = package.exgmnamea + exgmnameb = package.exgmnameb + + if exgtype is None or exgmnamea is None or exgmnameb is None: + excpt_str = 'Exchange packages require that exgtype, ' \ + 'exgmnamea, and exgmnameb are specified.' + raise FlopyException(excpt_str) + + self._exchange_files[package.filename] = package + try: + exchange_recarray_data = self.name_file.exchanges.get_data() + except MFDataException as mfde: + message = 'An error occurred while retrieving exchange ' \ + 'data from the simulation name file. The error ' \ + 'occurred while registering exchange file ' \ + '"{}".'.format(package.filename) + raise MFDataException(mfdata_except=mfde, + package=package._get_pname(), + message=message) + if exchange_recarray_data is not None: + for index, exchange in zip(range(0, + len(exchange_recarray_data)), + exchange_recarray_data): + if exchange[1] == package.filename: + # update existing exchange + exchange_recarray_data[index][0] = exgtype + exchange_recarray_data[index][2] = exgmnamea + exchange_recarray_data[index][3] = exgmnameb + ex_recarray = self.name_file.exchanges + try: + ex_recarray.set_data(exchange_recarray_data) + except MFDataException as mfde: + message = 'An error occurred while setting ' \ + 'exchange data in the simulation name ' \ + 'file. The error occurred while ' \ + 'registering the following ' \ + 'values (exgtype, filename, ' \ + 'exgmnamea, exgmnameb): "{} {} {}' \ + '{}".'.format(exgtype, package.filename, + exgmnamea, exgmnameb) + raise MFDataException(mfdata_except=mfde, + package=package._get_pname(), + message=message) + return + try: + # add new exchange + self.name_file.exchanges.append_data([(exgtype, + package.filename, + exgmnamea, + exgmnameb)]) + except MFDataException as mfde: + message = 'An error occurred while setting exchange data ' \ + 'in the simulation name file. The error occurred ' \ + 'while registering the following values (exgtype, ' \ + 'filename, exgmnamea, exgmnameb): "{} {} {}' \ + '{}".'.format(exgtype, package.filename, exgmnamea, + exgmnameb) + raise MFDataException(mfdata_except=mfde, + package=package._get_pname(), + message=message) + if package.dimensions is None: + # resolve exchange package dimensions object + package.dimensions = package.create_package_dimensions() + + def register_package(self, package, add_to_package_list=True, + set_package_name=True, set_package_filename=True): + """ + register a package file with the simulation + + Parameters + ---------- + package : MFPackage + package to register + add_to_package_list : bool + add package to lookup list + set_package_name : bool + produce a package name for this package + set_package_filename : bool + produce a filename for this package + + Returns + ------- + (path : tuple, package structure : MFPackageStructure) + + Examples + -------- + """ + package.container_type = [PackageContainerType.simulation] + path = self._get_package_path(package) + if add_to_package_list and package.package_type.lower != 'nam': + pname = None + if package.package_name is not None: + pname = package.package_name.lower() + if package.package_type.lower() == 'tdis' and self._tdis_file is \ + not None and self._tdis_file in self._packagelist: + # tdis package already exists. there can be only one tdis + # package. remove existing tdis package + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: tdis package already exists. Replacing ' + 'existing tdis package.') + self._remove_package(self._tdis_file) + elif package.package_type.lower() == 'gnc' and \ + package.filename in self._ghost_node_files and \ + self._ghost_node_files[package.filename] in self._packagelist: + # gnc package with same file name already exists. remove old + # gnc package + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: gnc package with name {} already exists. ' + 'Replacing existing gnc package' + '.'.format(pname)) + self._remove_package(self._ghost_node_files[package.filename]) + del self._ghost_node_files[package.filename] + elif package.package_type.lower() == 'mvr' and \ + package.filename in self._mover_files and \ + self._mover_files[package.filename] in self._packagelist: + # mvr package with same file name already exists. remove old + # mvr package + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: mvr package with name {} already exists. ' + 'Replacing existing mvr package' + '.'.format(pname)) + self._remove_package(self._mover_files[package.filename]) + del self._mover_files[package.filename] + elif package.package_type.lower() != 'ims' and pname in \ + self.package_name_dict: + if self.simulation_data.verbosity_level.value >= \ + VerbosityLevel.normal.value: + print('WARNING: Package with name {} already exists. ' + 'Replacing existing package' + '.'.format(package.package_name.lower())) + self._remove_package(self.package_name_dict[pname]) + if package.package_type.lower() != 'ims': + # all but ims packages get added here. ims packages are + # added during ims package registration + self._add_package(package, path) + if package.package_type.lower() == 'nam': + return path, self.structure.name_file_struct_obj + elif package.package_type.lower() == 'tdis': + self._tdis_file = package + struct_root = mfstructure.MFStructure() + tdis_pkg = 'tdis{}'.format(struct_root.get_version_string()) + tdis_attr = getattr(self.name_file, tdis_pkg) + try: + tdis_attr.set_data(package.filename) + except MFDataException as mfde: + message = 'An error occurred while setting the tdis package ' \ + 'file name "{}". The error occurred while ' \ + 'registering the tdis package with the ' \ + 'simulation'.format(package.filename) + raise MFDataException(mfdata_except=mfde, + package=package._get_pname(), + message=message) + return path, self.structure.package_struct_objs[ + package.package_type.lower()] + elif package.package_type.lower() == 'gnc': + if package.filename not in self._ghost_node_files: + self._ghost_node_files[package.filename] = package + self._gnc_file_num += 1 + elif self._ghost_node_files[package.filename] != package: + # auto generate a unique file name and register it + file_name = MFFileMgmt.unique_file_name(package.filename, + self._ghost_node_files) + package.filename = file_name + self._ghost_node_files[file_name] = package + elif package.package_type.lower() == 'mvr': + if package.filename not in self._mover_files: + self._mover_files[package.filename] = package + else: + # auto generate a unique file name and register it + file_name = MFFileMgmt.unique_file_name(package.filename, + self._mover_files) + package.filename = file_name + self._mover_files[file_name] = package + elif package.package_type.lower() == 'ims': + # default behavior is to register the ims package with the first + # unregistered model + unregistered_models = [] + for model in self._models: + model_registered = self._is_in_solution_group(model, 2) + if not model_registered: + unregistered_models.append(model) + if unregistered_models: + self.register_ims_package(package, unregistered_models) + else: + self.register_ims_package(package, None) + return path, self.structure.package_struct_objs[ + package.package_type.lower()] + else: + self._other_files[package.filename] = package + + if package.package_type.lower() in self.structure.package_struct_objs: + return path, self.structure.package_struct_objs[ + package.package_type.lower()] + elif package.package_type.lower() in self.structure.utl_struct_objs: + return path, self.structure.utl_struct_objs[ + package.package_type.lower()] + else: + excpt_str = 'Invalid package type "{}". Unable to register ' \ + 'package.'.format(package.package_type) + print(excpt_str) + raise FlopyException(excpt_str) + + def register_model(self, model, model_type, model_name, model_namefile): + """ + add a model to the simulation. + + Parameters + ---------- + model : MFModel + model object to add to simulation + sln_group : string + solution group of model + + Returns + ------- + model_structure_object : MFModelStructure + + Examples + -------- + """ + + # get model structure from model type + if model_type not in self.structure.model_struct_objs: + message = 'Invalid model type: "{}".'.format(model_type) + type_, value_, traceback_ = sys.exc_info() + raise MFDataException(model.name, + '', model.name, + 'registering model', 'sim', + inspect.stack()[0][3], + type_, value_, traceback_, message, + self.simulation_data.debug) + + # add model + self._models[model_name] = model + + # update simulation name file + self.name_file.models.append_list_as_record([model_type, + model_namefile, + model_name]) + + if len(self._ims_files) > 0: + # register model with first ims file found + first_ims_key = next(iter(self._ims_files)) + self.register_ims_package(self._ims_files[first_ims_key], + model_name) + + return self.structure.model_struct_objs[model_type] + + def get_ims_package(self, key): + if key in self._ims_files: + return self._ims_files[key] + return None + + def remove_model(self, model_name): + """ + remove a model from the simulation. + + Parameters + ---------- + model_name : string + model name to remove from simulation + + Examples + -------- + """ + + # Remove model + del self._models[model_name] + + # TODO: Fully implement this + # Update simulation name file + + def is_valid(self): + """ + check all packages and models in the simulation to verify validity + + Returns + ---------- + valid : boolean + simulation validity + + Examples + -------- + """ + + # name file valid + if not self.name_file.is_valid(): + return False + + # tdis file valid + if not self._tdis_file.is_valid(): + return False + + # exchanges valid + for exchange in self._exchange_files: + if not exchange.is_valid(): + return False + + # ims files valid + for imsfile in self._ims_files.values(): + if not imsfile.is_valid(): + return False + + # a model exists + if not self._models: + return False + + # models valid + for key in self._models: + if not self._models[key].is_valid(): + return False + + # each model has an imsfile + + return True + + @staticmethod + def _resolve_verbosity_level(verbosity_level): + if verbosity_level == 0: + return VerbosityLevel.quiet + elif verbosity_level == 1: + return VerbosityLevel.normal + elif verbosity_level == 2: + return VerbosityLevel.verbose + else: + return verbosity_level + + @staticmethod + def _get_package_path(package): + if package.parent_file is not None: + return (package.parent_file.path) + (package.package_type,) + else: + return (package.package_type,) + + def _append_to_ims_solution_group(self, ims_file, new_models): + solution_recarray = self.name_file.solutiongroup + for solution_group_num in solution_recarray.get_active_key_list(): + try: + rec_array = solution_recarray.get_data(solution_group_num[0]) + except MFDataException as mfde: + message = 'An error occurred while getting solution group' \ + '"{}" from the simulation name file' \ + '.'.format(solution_group_num[0]) + raise MFDataException(mfdata_except=mfde, + package='nam', + message=message) + new_array = [] + for index, record in enumerate(rec_array): + new_record = [] + rec_model_dict = {} + for index, item in enumerate(record): + if record[1] == ims_file or item not in new_models: + new_record.append(item) + if index > 1: + rec_model_dict[item] = 1 + + if record[1] == ims_file: + for model in new_models: + if model not in rec_model_dict: + new_record.append(model) + + new_array.append(tuple(new_record)) + solution_recarray.set_data(new_array, + solution_group_num[0]) + + def _replace_ims_in_solution_group(self, item, index, new_item): + solution_recarray = self.name_file.solutiongroup + for solution_group_num in solution_recarray.get_active_key_list(): + try: + rec_array = solution_recarray.get_data(solution_group_num[0]) + except MFDataException as mfde: + message = 'An error occurred while getting solution group' \ + '"{}" from the simulation name file. The error ' \ + 'occurred while replacing IMS file "{}" with "{}"' \ + 'at index "{}"'.format(solution_group_num[0], + item, new_item, index) + raise MFDataException(mfdata_except=mfde, + package='nam', + message=message) + if rec_array is not None: + for rec_item in rec_array: + if rec_item[index] == item: + rec_item[index] = new_item + + def _is_in_solution_group(self, item, index): + solution_recarray = self.name_file.solutiongroup + for solution_group_num in solution_recarray.get_active_key_list(): + try: + rec_array = solution_recarray.get_data(solution_group_num[0]) + except MFDataException as mfde: + message = 'An error occurred while getting solution group' \ + '"{}" from the simulation name file. The error ' \ + 'occurred while verifying file "{}" at index "{}" ' \ + 'is in the simulation name file' \ + '.'.format(solution_group_num[0], item, index) + raise MFDataException(mfdata_except=mfde, + package='nam', + message=message) + + if rec_array is not None: + for rec_item in rec_array: + if rec_item[index] == item: + return True + return False + + + def plot(self, model_list=None, SelPackList=None, **kwargs): + """ + Method to plot a whole simulation or a series of models + that are part of a simualtion + + Parameters: + model_list: (list) list of model names to plot, if none + all models will be plotted + SelPackList: (list) list of package names to plot, if none + all packages will be plotted + + kwargs: + filename_base : str + Base file name that will be used to automatically generate file + names for output image files. Plots will be exported as image + files if file_name_base is not None. (default is None) + file_extension : str + Valid matplotlib.pyplot file extension for savefig(). Only used + if filename_base is not None. (default is 'png') + mflay : int + MODFLOW zero-based layer number to return. If None, then all + all layers will be included. (default is None) + kper : int + MODFLOW zero-based stress period number to return. + (default is zero) + key : str + MfList dictionary key. (default is None) + + + Returns: + axes: (list) matplotlib.pyplot.axes objects + """ + from flopy.plot.plotutil import PlotUtilities + + axes = PlotUtilities._plot_simulation_helper(self, + model_list=model_list, + SelPackList=SelPackList, + **kwargs) return axes \ No newline at end of file