Repository navigation
Slicing a Multiple_Damping_Curves object drops the original file name and sep, so get_all_HH_x_params()/get_all_H4_x_params() can no longer name or delimit their output #111
Description
Found while reviewing #74. Pre-existing on main (b764057); #74 does not change the behavior.
Problem
Multiple_Curves.__getitem__() builds a slice with self.__class__(self.curves[i]). For Multiple_Damping_Curves, the list branch of __init__() sets _filename = None and _sep = '\t', so a slice forgets the file it came from and its delimiter. get_all_HH_x_params() and get_all_H4_x_params() use _filename to build the default txt_filename / fig_filename, and _sep as the default sep. On a slice, save_txt=True (or save_fig=True) without a file name raises ValueError, and a file read with sep=',' is written with tabs. (#83 is a different slicing bug, about n_layer.)
PySeismoSoil/PySeismoSoil/class_curves.py
Lines 728 to 730 in b764057
| if isinstance(i, slice): | |
| # return an object of the same class, filled with the sliced data | |
| return self.__class__(self.curves[i]) |
PySeismoSoil/PySeismoSoil/class_curves.py
Lines 868 to 880 in b764057
| if isinstance(filename_or_list_of_curves, str): # file name | |
| curves = np.genfromtxt(filename_or_list_of_curves, delimiter=sep) | |
| _, list_of_damping_curves = hlp.extract_from_curve_format(curves) | |
| self._filename = filename_or_list_of_curves | |
| elif isinstance(filename_or_list_of_curves, list): | |
| list_of_damping_curves = filename_or_list_of_curves | |
| self._filename = None | |
| else: | |
| raise TypeError( | |
| 'Unrecognized type for `filename_or_list_of_curves`.' | |
| ) | |
| self._sep = sep |
PySeismoSoil/PySeismoSoil/class_curves.py
Lines 1091 to 1099 in b764057
| if save_fig and fig_filename is None: | |
| fig_filename = self._produce_output_file_name('HH', 'png') | |
| if save_txt: | |
| if txt_filename is None: | |
| txt_filename = self._produce_output_file_name('HH', 'txt') | |
| if sep is None: | |
| sep = self._sep |
PySeismoSoil/PySeismoSoil/class_curves.py
Lines 1210 to 1218 in b764057
| if save_fig and fig_filename is None: | |
| fig_filename = self._produce_output_file_name('H4', 'png') | |
| if save_txt: | |
| if txt_filename is None: | |
| txt_filename = self._produce_output_file_name('H4', 'txt') | |
| if sep is None: | |
| sep = self._sep |
PySeismoSoil/PySeismoSoil/class_curves.py
Lines 1272 to 1282 in b764057
| if self._filename is None: | |
| raise ValueError( | |
| 'Please make sure to create this object from ' | |
| 'a text file, so that there is an original file ' | |
| 'name to work with.', | |
| ) | |
| file_name_ = Path(self._filename).stem | |
| site_name = file_name_[6:] if 'curve_' in file_name_ else file_name_ | |
| return f'{prefix}_x_{site_name}.{extension}' |
Slices of Multiple_GGmax_Curves, HH_Param_Multi_Layer and MKZ_Param_Multi_Layer also reset _filename and _sep, but nothing reads these attributes in those classes, so their behavior does not change.
This may be intended, because a slice is a new object that was not read from a file. If so, the docstrings of txt_filename, fig_filename and sep should say that a sliced object needs them passed explicitly.
import os
import tempfile
import numpy as np
from PySeismoSoil.class_curves import Multiple_Damping_Curves
data = np.genfromtxt('tests/files/curve_FKSH14.txt')
cwd = os.getcwd()
with tempfile.TemporaryDirectory() as tmp_dir:
os.chdir(tmp_dir) # output files without a path are written here
try:
np.savetxt('curve_FKSH14.csv', data, delimiter=',')
mdc = Multiple_Damping_Curves('curve_FKSH14.csv', sep=',')
mdc_slice = mdc[:2]
print(mdc._filename, repr(mdc._sep))
# curve_FKSH14.csv ','
print(mdc_slice._filename, repr(mdc_slice._sep))
# None '\t'
# Automatic output file name: works on the full object, fails on a slice
mdc.get_all_H4_x_params(pop_size=1, n_gen=1, save_txt=True)
print(sorted(os.listdir('.')))
# ['H4_x_FKSH14.txt', 'curve_FKSH14.csv']
try:
mdc_slice.get_all_H4_x_params(pop_size=1, n_gen=1, save_txt=True)
except ValueError as e:
print(f'{type(e).__name__}: {e}')
# ValueError: Please make sure to create this object from a text file, so that there is an original file name to work with.
# Default `sep`: ',' for the full object, '\t' for a slice
mdc[:5].get_all_H4_x_params(
pop_size=1, n_gen=1, save_txt=True, txt_filename='from_slice.txt'
)
with open('H4_x_FKSH14.txt') as f:
print(repr(f.readline()))
# '68498.5,89303.1,265715,0.00158861,0.00157445\n'
with open('from_slice.txt') as f:
print(repr(f.readline()))
# '68498.5\t89303.1\t265715\t0.00158861\t0.00157445\n'
finally:
os.chdir(cwd)Fix
- In the slice branch of
Multiple_Curves.__getitem__(), copy_filenameand_septo the new object whenselfhas them:With this change,new = self.__class__(self.curves[i]) for attr in ('_filename', '_sep'): if hasattr(self, attr): setattr(new, attr, getattr(self, attr)) return new
mdc[:2].get_all_H4_x_params(save_txt=True)in the example writesH4_x_FKSH14.txtwith,as the delimiter.Param_Multi_Layer.__getitem__()can do the same for consistency. - If slices should not keep the file name instead, document that in
get_all_HH_x_params()andget_all_H4_x_params(). - Add a test that a slice of a file-based
Multiple_Damping_Curveskeeps_filenameand_sep(or, for option 2, that the error message says to passtxt_filename).
Activity