From 1fe51578309a693fae45d843e50aa6f9c86ad70c Mon Sep 17 00:00:00 2001 From: scottrp <45947939+scottrp@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:02:25 -0700 Subject: [PATCH] fix(str and repr): better repr and str output for transient data with multiple blocks (#2058) (#2102) * fix(str and repr): improved display string for str and repr data with multiple blocks * fix(str and repr) * fix(str/repr): pandas list str/repr fix --- flopy/mf6/data/mfdata.py | 22 +++++++- flopy/mf6/data/mfdataarray.py | 8 ++- flopy/mf6/data/mfdatalist.py | 8 ++- flopy/mf6/data/mfdataplist.py | 99 +++++++++++++++++++++++++-------- flopy/mf6/data/mfdatascalar.py | 8 ++- flopy/mf6/data/mfdatastorage.py | 16 ++++++ 6 files changed, 129 insertions(+), 32 deletions(-) diff --git a/flopy/mf6/data/mfdata.py b/flopy/mf6/data/mfdata.py index 2f992d8a8f..bd24c5dc7a 100644 --- a/flopy/mf6/data/mfdata.py +++ b/flopy/mf6/data/mfdata.py @@ -269,10 +269,26 @@ def __init__( self._cached_model_grid = None def __repr__(self): - return repr(self._get_storage_obj()) + if isinstance(self._data_storage, dict): + stor_size = len(self._data_storage) + else: + stor_size = 1 + if stor_size <= 1: + return repr(self._get_storage_obj(first_record=True)) + else: + rpr = repr(self._get_storage_obj(first_record=True)) + return f"{rpr}...\nand {stor_size - 1} additional data blocks" def __str__(self): - return str(self._get_storage_obj()) + if isinstance(self._data_storage, dict): + stor_size = len(self._data_storage) + else: + stor_size = 1 + if stor_size <= 1: + return str(self._get_storage_obj(first_record=True)) + else: + st = str(self._get_storage_obj(first_record=True)) + return f"{st}...\nand {stor_size - 1} additional data blocks" @property def path(self): @@ -531,7 +547,7 @@ def _get_aux_var_name(self, aux_var_index): # TODO: Verify that this works for multi-dimensional layering return aux_var_names[0][aux_var_index[0] + 1] - def _get_storage_obj(self): + def _get_storage_obj(self, first_record=False): return self._data_storage diff --git a/flopy/mf6/data/mfdataarray.py b/flopy/mf6/data/mfdataarray.py index 2825747a33..8befe29c93 100644 --- a/flopy/mf6/data/mfdataarray.py +++ b/flopy/mf6/data/mfdataarray.py @@ -1293,7 +1293,7 @@ def _new_storage( data_path=self._path, ) - def _get_storage_obj(self): + def _get_storage_obj(self, first_record=False): return self._data_storage def _set_storage_obj(self, storage): @@ -2020,7 +2020,11 @@ def _new_storage( def _set_storage_obj(self, storage): self._data_storage[self._current_key] = storage - def _get_storage_obj(self): + def _get_storage_obj(self, first_record=False): + if first_record and isinstance(self._data_storage, dict): + for value in self._data_storage.values(): + return value + return None if ( self._current_key is None or self._current_key not in self._data_storage diff --git a/flopy/mf6/data/mfdatalist.py b/flopy/mf6/data/mfdatalist.py index d1e4e123bb..e66146d484 100644 --- a/flopy/mf6/data/mfdatalist.py +++ b/flopy/mf6/data/mfdatalist.py @@ -1391,7 +1391,7 @@ def _new_storage(self, stress_period=0): data_path=self._path, ) - def _get_storage_obj(self): + def _get_storage_obj(self, first_record=False): return self._data_storage def plot( @@ -2046,7 +2046,11 @@ def update_record(self, record, key_index, key=0): def _new_storage(self, stress_period=0): return {} - def _get_storage_obj(self): + def _get_storage_obj(self, first_record=False): + if first_record and isinstance(self._data_storage, dict): + for value in self._data_storage.values(): + return value + return None if ( self._current_key is None or self._current_key not in self._data_storage diff --git a/flopy/mf6/data/mfdataplist.py b/flopy/mf6/data/mfdataplist.py index 95460bf288..45fdbde4a1 100644 --- a/flopy/mf6/data/mfdataplist.py +++ b/flopy/mf6/data/mfdataplist.py @@ -67,6 +67,55 @@ def __init__(self): self.data_storage_type = None self.modified = False + def __repr__(self): + return self.get_data_str(True) + + def __str__(self): + return self.get_data_str(False) + + def _get_header_str(self): + header_list = [] + if self.data_storage_type == DataStorageType.external_file: + header_list.append(f"open/close {self.fname}") + else: + header_list.append("internal") + if self.iprn is not None: + header_list.append(f"iprn {self.iprn}") + if len(header_list) > 0: + return ", ".join(header_list) + else: + return "" + + def get_data_str(self, formal): + data_str = "" + layer_str = "" + if self.data_storage_type == DataStorageType.internal_array: + if self.internal_data is not None: + header = self._get_header_str() + if formal: + data_str = "{}{}{{{}}}\n({})\n".format( + data_str, + layer_str, + header, + repr(self.internal_data), + ) + else: + data_str = "{}{}{{{}}}\n({})\n".format( + data_str, + layer_str, + header, + str(self.internal_data), + ) + elif self.data_storage_type == DataStorageType.external_file: + header = self._get_header_str() + data_str = "{}{}{{{}}}\n({})\n".format( + data_str, + layer_str, + header, + "External data not displayed", + ) + return data_str + def get_record(self): rec = {} if self.internal_data is not None: @@ -726,7 +775,7 @@ def set_data(self, data, autofill=False, check_data=True, append=False): self._simulation_data.debug, ) - data_storage = self._get_storage() + data_storage = self._get_storage_obj() if append: # append data to existing dataframe current_data = self._get_dataframe() @@ -742,7 +791,7 @@ def set_data(self, data, autofill=False, check_data=True, append=False): def has_modified_ext_data(self): """check to see if external data has been modified since last read""" - data_storage = self._get_storage() + data_storage = self._get_storage_obj() return ( data_storage.data_storage_type == DataStorageType.external_file and data_storage.internal_data is not None @@ -750,7 +799,7 @@ def has_modified_ext_data(self): def binary_ext_data(self): """check for binary data""" - data_storage = self._get_storage() + data_storage = self._get_storage_obj() return data_storage.binary def to_array(self, kper=0, mask=False): @@ -792,7 +841,7 @@ def set_record(self, record, autofill=False, check_data=True): """ if isinstance(record, dict): - data_storage = self._get_storage() + data_storage = self._get_storage_obj() if "filename" in record: data_storage.set_external(record["filename"]) if "binary" in record: @@ -851,9 +900,9 @@ def append_data(self, data): """ try: self._resync() - if self._get_storage() is None: + if self._get_storage_obj() is None: self._data_storage = self._new_storage() - data_storage = self._get_storage() + data_storage = self._get_storage_obj() if ( data_storage.data_storage_type == DataStorageType.internal_array @@ -952,7 +1001,7 @@ def store_internal( Verify data prior to storing """ - storage = self._get_storage() + storage = self._get_storage_obj() # check if data is already stored external if ( storage is None @@ -999,7 +1048,7 @@ def store_as_external_file( """ # only store data externally (do not subpackage info) if self.structure.construct_package is None: - storage = self._get_storage() + storage = self._get_storage_obj() # check if data is already stored external if ( replace_existing_external @@ -1030,7 +1079,7 @@ def store_as_external_file( def external_file_name(self): """Returns external file name, or None if this is not external data.""" - storage = self._get_storage() + storage = self._get_storage_obj() if storage is None: return None if ( @@ -1191,15 +1240,15 @@ def _save_binary_data(self, fd_data_file, data): fd_data_file, self._model_or_sim.modeldiscrit, ) - data_storage = self._get_storage() + data_storage = self._get_storage_obj() data_storage.internal_data = None def has_data(self, key=None): """Returns whether this MFList has any data associated with it.""" try: - if self._get_storage() is None: + if self._get_storage_obj() is None: return False - return self._get_storage().has_data() + return self._get_storage_obj().has_data() except Exception as ex: type_, value_, traceback_ = sys.exc_info() raise MFDataException( @@ -1281,7 +1330,7 @@ def load( next data line : str """ - data_storage = self._get_storage() + data_storage = self._get_storage_obj() data_storage.modified = False # parse first line to determine if this is internal or external data datautil.PyListUtil.reset_delimiter_used() @@ -1338,7 +1387,7 @@ def load( def _new_storage(self): return {"Data": PandasListStorage()} - def _get_storage(self): + def _get_storage_obj(self, first_record=False): return self._data_storage["Data"] def _get_id_fields(self, data_frame): @@ -1484,7 +1533,7 @@ def _get_data(self): def _get_dataframe(self): """get and return dataframe for this list data""" - data_storage = self._get_storage() + data_storage = self._get_storage_obj() if data_storage is None or data_storage.data_storage_type is None: block_exists = self._block.header_exists( self._current_key, self.path @@ -1551,9 +1600,9 @@ def _get_record(self, data_frame=False): """ try: - if self._get_storage() is None: + if self._get_storage_obj() is None: return None - record = self._get_storage().get_record() + record = self._get_storage_obj().get_record() except Exception as ex: type_, value_, traceback_ = sys.exc_info() raise MFDataException( @@ -1650,7 +1699,7 @@ def _write_file_entry( ------- result of pandas to_csv call """ - data_storage = self._get_storage() + data_storage = self._get_storage_obj() if data_storage is None: return "" if ( @@ -1751,7 +1800,7 @@ def _get_file_path(self): file_path : file path to data """ - data_storage = self._get_storage() + data_storage = self._get_storage_obj() if data_storage.fname is None: return None if self._model_or_sim.type == "model": @@ -1989,11 +2038,11 @@ def store_as_external_file( self._cache_model_grid = True for sp in self._data_storage.keys(): self._current_key = sp - storage = self._get_storage() + storage = self._get_storage_obj() if storage.internal_size == 0: storage.internal_data = self.get_dataframe(sp) if storage.internal_size > 0 and ( - self._get_storage().data_storage_type + self._get_storage_obj().data_storage_type != DataStorageType.external_file or replace_existing_external ): @@ -2027,7 +2076,7 @@ def store_internal( for sp in self._data_storage.keys(): self._current_key = sp if ( - self._get_storage().data_storage_type + self._get_storage_obj().data_storage_type == DataStorageType.external_file ): super().store_internal( @@ -2482,7 +2531,11 @@ def update_record(self, record, key_index, key=0): def _new_storage(self): return {} - def _get_storage(self): + def _get_storage_obj(self, first_record=False): + if first_record and isinstance(self._data_storage, dict): + for value in self._data_storage.values(): + return value + return None if ( self._current_key is None or self._current_key not in self._data_storage diff --git a/flopy/mf6/data/mfdatascalar.py b/flopy/mf6/data/mfdatascalar.py index ca6e7e63a6..c9ab08183c 100644 --- a/flopy/mf6/data/mfdatascalar.py +++ b/flopy/mf6/data/mfdatascalar.py @@ -661,7 +661,7 @@ def _new_storage(self, stress_period=0): data_path=self._path, ) - def _get_storage_obj(self): + def _get_storage_obj(self, first_record=False): return self._data_storage def plot(self, filename_base=None, file_extension=None, **kwargs): @@ -911,7 +911,11 @@ def load( def _new_storage(self, stress_period=0): return {} - def _get_storage_obj(self): + def _get_storage_obj(self, first_record=False): + if first_record and isinstance(self._data_storage, dict): + for value in self._data_storage.values(): + return value + return None if ( self._current_key is None or self._current_key not in self._data_storage diff --git a/flopy/mf6/data/mfdatastorage.py b/flopy/mf6/data/mfdatastorage.py index 4285b908ec..2f37b36da5 100644 --- a/flopy/mf6/data/mfdatastorage.py +++ b/flopy/mf6/data/mfdatastorage.py @@ -497,6 +497,22 @@ def get_data_str(self, formal): layer_str, self._get_layer_header_str(index), ) + elif storage.data_storage_type == DataStorageType.external_file: + header = self._get_layer_header_str(index) + if self.layered: + data_str = "{}{}{{{}}}\n({})\n".format( + data_str, + layer_str, + header, + "External data not displayed", + ) + else: + data_str = "{}{}{{{}}}\n({})\n".format( + data_str, + layer_str, + header, + "External data not displayed", + ) return data_str def _get_layer_header_str(self, layer):