Skip to content

[BUG] An infinite loop and a few crashes #15

@vyrjana

Description

@vyrjana

First of all, thanks for the continued development of pyDRTtools!

I noticed a few bugs while testing out the latest version (commit 3694b9b).

Infinite loop after clicking the "Hilbert Transform" button.

This is the relevant loop:

while True:
try:
theta_0 = 10**(6*np.random.rand(3, 1)-3)
out_dict_real = BHT.HT_single_est(theta_0, entry.Z_exp.real, entry.A_re, entry.A_H_im, entry.M, N_freqs, N_taus)
theta_0 = out_dict_real['theta']
out_dict_imag = BHT.HT_single_est(theta_0, entry.Z_exp.imag, entry.A_im, entry.A_H_re, entry.M, N_freqs, N_taus)
break
except:
print('Error Occur, Try Another Inital Condition')

The reason is that theta_0 is a numpy.ndarray with more than one dimension, which causes scipy.optimize.minimize to raise a ValueError ('x0' must only have one dimension.) here:
res = minimize(NMLL_fct, theta_0, args=(Z_exp, A, M, N_freqs, N_taus), callback=print_results, options={'gtol': 1E-8, 'disp': True})

Aside from making sure that theta_0 is 1D, you might also wish to narrow down the type(s) of exception(s) that are allowed to keep the loop running instead of using a bare except.

Crashes when closing a save file dialog by canceling

The functions related to the "DRT" and "EIS Regression" export buttons do not properly handle the case where the file dialog is closed by canceling:

If the value assigned to path is an empty string, then a FileNotFoundError will be raised later in the function when trying to write to the path. These crashes only occur if one of the three types of runs (simple, Bayesian, or HT) have been performed due to the if-elif blocks.

The QFileDialog.getSaveFileName returns an empty string if the modal window is closed by canceling, but it can also return an invalid path if the user types in a path (e.g. /tmp/foo/bar on Linux) into the File name input field in the modal window. So, I would recommend something along the lines of:

path, ext = QFileDialog.getSaveFileName(...)
if path == "":
    # The user closed the modal window by canceling.
    return
elif not os.path.isdir(os.path.dirname(path)):
    return
elif os.path.isdir(path):
    return

There might be some edge case that I'm missing here. The user is likely to assume that the file was saved unless they are somehow made aware when the path is invalid. Qt5 has a QErrorMessage class that could be used.

The following instance, which is associated with saving a figure, does not cause a crash, but it does cause a few lines about an empty or null file name to be printed to the terminal:

path, ext = QFileDialog.getSaveFileName(self,'Save file', '', file_choices)

Crash when exporting "EIS Regression" results

This crash occurs when experimental data has been loaded but none of the three types of runs have been performed. The reason for this is that the following if-else block assumes that at least a simple or Bayesian run has been performed.

if self.data.method == 'BHT': # save result for BHT
with open(path, 'w', newline='') as save_file:
writer = csv.writer(save_file)
writer.writerow(['s_res_re', self.data.out_scores['s_res_re'][0],
self.data.out_scores['s_res_re'][1],
self.data.out_scores['s_res_re'][2]])
writer.writerow(['s_res_im', self.data.out_scores['s_res_im'][0],
self.data.out_scores['s_res_im'][1],
self.data.out_scores['s_res_im'][2]])
writer.writerow(['s_mu_re', self.data.out_scores['s_mu_re']])
writer.writerow(['s_mu_im', self.data.out_scores['s_mu_im']])
writer.writerow(['s_HD_re', self.data.out_scores['s_HD_re']])
writer.writerow(['s_HD_im', self.data.out_scores['s_HD_im']])
writer.writerow(['s_JSD_re', self.data.out_scores['s_JSD_re']])
writer.writerow(['s_JSD_im', self.data.out_scores['s_JSD_im']])
writer.writerow(['freq', 'mu_Z_re','mu_Z_im','mu_H_re','mu_H_im',
'Z_H_re_band','Z_H_im_band','Z_H_re_res','Z_H_im_res'])
# save frequency, the fitted impedance and the residual
for n in range(self.data.freq.shape[0]):
writer.writerow([self.data.freq[n], self.data.mu_Z_re[n],
self.data.mu_Z_im[n], self.data.mu_Z_H_im_agm[n],
self.data.band_re_agm[n], self.data.band_im_agm[n],
self.data.res_H_re[n], self.data.res_H_im[n]])
else: # save result for simple and bayesian run
with open(path, 'w', newline='') as save_file:
writer = csv.writer(save_file)
writer.writerow(['freq','mu_Z_re','mu_Z_im','Z_re_res','Z_im_res'])
# save frequency, the fitted impedance and the residual
for n in range(self.data.freq.shape[0]):
writer.writerow([self.data.freq[n], self.data.mu_Z_re[n],
self.data.mu_Z_im[n], self.data.res_re[n],
self.data.res_im[n]])

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions