Skip to content

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

Open

Description

@jsh9

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.)

if isinstance(i, slice):
# return an object of the same class, filled with the sliced data
return self.__class__(self.curves[i])

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

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

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

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

  1. In the slice branch of Multiple_Curves.__getitem__(), copy _filename and _sep to the new object when self has them:
    new = self.__class__(self.curves[i])
    for attr in ('_filename', '_sep'):
        if hasattr(self, attr):
            setattr(new, attr, getattr(self, attr))
    return new
    With this change, mdc[:2].get_all_H4_x_params(save_txt=True) in the example writes H4_x_FKSH14.txt with , as the delimiter. Param_Multi_Layer.__getitem__() can do the same for consistency.
  2. If slices should not keep the file name instead, document that in get_all_HH_x_params() and get_all_H4_x_params().
  3. Add a test that a slice of a file-based Multiple_Damping_Curves keeps _filename and _sep (or, for option 2, that the error message says to pass txt_filename).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions