pyckster 26.2.2__py3-none-any.whl → 26.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyckster/__init__.py +1 -1
- pyckster/core.py +203 -16
- {pyckster-26.2.2.dist-info → pyckster-26.2.3.dist-info}/METADATA +1 -1
- {pyckster-26.2.2.dist-info → pyckster-26.2.3.dist-info}/RECORD +8 -8
- {pyckster-26.2.2.dist-info → pyckster-26.2.3.dist-info}/WHEEL +0 -0
- {pyckster-26.2.2.dist-info → pyckster-26.2.3.dist-info}/entry_points.txt +0 -0
- {pyckster-26.2.2.dist-info → pyckster-26.2.3.dist-info}/licenses/LICENCE +0 -0
- {pyckster-26.2.2.dist-info → pyckster-26.2.3.dist-info}/top_level.txt +0 -0
pyckster/__init__.py
CHANGED
|
@@ -15,7 +15,7 @@ except ImportError:
|
|
|
15
15
|
pass # matplotlib not available, that's fine
|
|
16
16
|
|
|
17
17
|
# Define version and metadata in one place
|
|
18
|
-
__version__ = "26.2.
|
|
18
|
+
__version__ = "26.2.3"
|
|
19
19
|
__author__ = "Sylvain Pasquet"
|
|
20
20
|
__email__ = "sylvain.pasquet@sorbonne-universite.fr"
|
|
21
21
|
__license__ = "GPLv3"
|
pyckster/core.py
CHANGED
|
@@ -11663,7 +11663,7 @@ class MainWindow(QMainWindow):
|
|
|
11663
11663
|
self.skip_header_edit = QSpinBox()
|
|
11664
11664
|
self.skip_header_edit.setRange(0, 1000)
|
|
11665
11665
|
self.skip_header_edit.setValue(0)
|
|
11666
|
-
self.skip_header_edit.setToolTip("Number of
|
|
11666
|
+
self.skip_header_edit.setToolTip("Number of lines to skip from the beginning of the file (counts all lines including comments)")
|
|
11667
11667
|
data_layout.addRow("Skip header lines:", self.skip_header_edit)
|
|
11668
11668
|
|
|
11669
11669
|
self.delimiter_edit = QLineEdit()
|
|
@@ -11738,7 +11738,24 @@ class MainWindow(QMainWindow):
|
|
|
11738
11738
|
successful_imports = 0
|
|
11739
11739
|
failed_imports = []
|
|
11740
11740
|
|
|
11741
|
-
for
|
|
11741
|
+
# Create progress dialog for batch import
|
|
11742
|
+
progress = None
|
|
11743
|
+
if len(params['file_paths']) > 1:
|
|
11744
|
+
progress = QProgressDialog("Importing ASCII matrices...", "Cancel", 0, len(params['file_paths']), self)
|
|
11745
|
+
progress.setWindowTitle("Importing ASCII Matrices")
|
|
11746
|
+
progress.setMinimumDuration(0)
|
|
11747
|
+
progress.setWindowModality(QtCore.Qt.WindowModal)
|
|
11748
|
+
progress.setValue(0)
|
|
11749
|
+
progress.show()
|
|
11750
|
+
QApplication.processEvents()
|
|
11751
|
+
|
|
11752
|
+
for idx, file_path in enumerate(params['file_paths']):
|
|
11753
|
+
if progress:
|
|
11754
|
+
progress.setValue(idx)
|
|
11755
|
+
if progress.wasCanceled():
|
|
11756
|
+
break
|
|
11757
|
+
progress.setLabelText(f"Importing file {idx+1} of {len(params['file_paths'])}...")
|
|
11758
|
+
QApplication.processEvents()
|
|
11742
11759
|
try:
|
|
11743
11760
|
# Load the ASCII matrix
|
|
11744
11761
|
QApplication.processEvents()
|
|
@@ -11760,13 +11777,15 @@ class MainWindow(QMainWindow):
|
|
|
11760
11777
|
delimiter = None # Let numpy handle it
|
|
11761
11778
|
|
|
11762
11779
|
# Load the data with skiprows parameter
|
|
11780
|
+
# Set comments=None to disable automatic comment skipping (numpy default is comments='#')
|
|
11781
|
+
# This ensures skiprows counts ALL lines as documented
|
|
11763
11782
|
try:
|
|
11764
|
-
data_matrix = np.loadtxt(file_path, delimiter=delimiter, skiprows=params['skip_header'])
|
|
11783
|
+
data_matrix = np.loadtxt(file_path, delimiter=delimiter, skiprows=params['skip_header'], comments=None)
|
|
11765
11784
|
except ValueError:
|
|
11766
11785
|
# Try with different delimiters
|
|
11767
11786
|
for delim in [None, ',', '\t', ' ']:
|
|
11768
11787
|
try:
|
|
11769
|
-
data_matrix = np.loadtxt(file_path, delimiter=delim, skiprows=params['skip_header'])
|
|
11788
|
+
data_matrix = np.loadtxt(file_path, delimiter=delim, skiprows=params['skip_header'], comments=None)
|
|
11770
11789
|
break
|
|
11771
11790
|
except ValueError:
|
|
11772
11791
|
continue
|
|
@@ -11781,6 +11800,53 @@ class MainWindow(QMainWindow):
|
|
|
11781
11800
|
if data_matrix.ndim == 1:
|
|
11782
11801
|
data_matrix = data_matrix.reshape(-1, 1)
|
|
11783
11802
|
|
|
11803
|
+
# Auto-detect orientation: number of traces should typically be less than number of time samples
|
|
11804
|
+
# This helps prevent memory overload from incorrect orientation
|
|
11805
|
+
n_rows, n_cols = data_matrix.shape
|
|
11806
|
+
if n_cols > n_rows:
|
|
11807
|
+
# More columns than rows suggests wrong orientation
|
|
11808
|
+
# This would mean more traces than time samples, which is unusual
|
|
11809
|
+
|
|
11810
|
+
# Check if we should use "yes to all" from previous choice
|
|
11811
|
+
if not hasattr(self, '_transpose_yes_to_all'):
|
|
11812
|
+
self._transpose_yes_to_all = False
|
|
11813
|
+
|
|
11814
|
+
if self._transpose_yes_to_all:
|
|
11815
|
+
# Automatically transpose based on previous "yes to all" choice
|
|
11816
|
+
data_matrix = data_matrix.T
|
|
11817
|
+
params['transpose'] = not params['transpose']
|
|
11818
|
+
else:
|
|
11819
|
+
# Ask user
|
|
11820
|
+
msg = QMessageBox()
|
|
11821
|
+
msg.setIcon(QMessageBox.Warning)
|
|
11822
|
+
msg.setWindowTitle("Potential Orientation Issue")
|
|
11823
|
+
msg.setText(
|
|
11824
|
+
f"The matrix has {n_cols} columns and {n_rows} rows.\n\n"
|
|
11825
|
+
f"This would result in {n_cols} traces with {n_rows} time samples each, "
|
|
11826
|
+
f"which is unusual (typically, traces < time samples).\n\n"
|
|
11827
|
+
f"Do you want to transpose the matrix?\n"
|
|
11828
|
+
f"(This would give {n_rows} traces with {n_cols} samples each)"
|
|
11829
|
+
)
|
|
11830
|
+
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
|
11831
|
+
msg.setDefaultButton(QMessageBox.Yes)
|
|
11832
|
+
|
|
11833
|
+
# Add "Yes to All" checkbox for batch imports
|
|
11834
|
+
yes_to_all_checkbox = None
|
|
11835
|
+
if len(params['file_paths']) > 1:
|
|
11836
|
+
yes_to_all_checkbox = QCheckBox("Apply to all remaining files")
|
|
11837
|
+
msg.setCheckBox(yes_to_all_checkbox)
|
|
11838
|
+
|
|
11839
|
+
result = msg.exec_()
|
|
11840
|
+
|
|
11841
|
+
# Check if "Yes to All" was selected
|
|
11842
|
+
if yes_to_all_checkbox and yes_to_all_checkbox.isChecked():
|
|
11843
|
+
self._transpose_yes_to_all = True
|
|
11844
|
+
|
|
11845
|
+
if result == QMessageBox.Yes:
|
|
11846
|
+
data_matrix = data_matrix.T
|
|
11847
|
+
# Update the effective transpose state for display later
|
|
11848
|
+
params['transpose'] = not params['transpose']
|
|
11849
|
+
|
|
11784
11850
|
# Validate the data matrix
|
|
11785
11851
|
if data_matrix.size == 0:
|
|
11786
11852
|
raise ValueError("The loaded matrix is empty")
|
|
@@ -11850,6 +11916,10 @@ class MainWindow(QMainWindow):
|
|
|
11850
11916
|
except Exception as e:
|
|
11851
11917
|
failed_imports.append((os.path.basename(file_path), str(e)))
|
|
11852
11918
|
|
|
11919
|
+
# Close progress dialog
|
|
11920
|
+
if progress:
|
|
11921
|
+
progress.setValue(len(params['file_paths']))
|
|
11922
|
+
|
|
11853
11923
|
# After processing all files, update UI and show results
|
|
11854
11924
|
if successful_imports > 0:
|
|
11855
11925
|
# Select the last imported file
|
|
@@ -11870,9 +11940,57 @@ class MainWindow(QMainWindow):
|
|
|
11870
11940
|
if len(failed_imports) > 5:
|
|
11871
11941
|
error_msg += f"\n\n... and {len(failed_imports) - 5} more"
|
|
11872
11942
|
QMessageBox.warning(self, "Import Complete with Errors", error_msg)
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11943
|
+
elif successful_imports > 0:
|
|
11944
|
+
# Calculate total traces and samples for successful imports
|
|
11945
|
+
total_traces = 0
|
|
11946
|
+
total_samples = 0
|
|
11947
|
+
# Track different configurations
|
|
11948
|
+
config_counts = {} # (n_traces, n_samples): count
|
|
11949
|
+
|
|
11950
|
+
for i in range(len(self.streams) - successful_imports, len(self.streams)):
|
|
11951
|
+
if i >= 0 and i < len(self.trace_position) and self.trace_position[i] is not None:
|
|
11952
|
+
n_traces = len(self.trace_position[i])
|
|
11953
|
+
total_traces += n_traces
|
|
11954
|
+
else:
|
|
11955
|
+
n_traces = 0
|
|
11956
|
+
|
|
11957
|
+
if i >= 0 and i < len(self.n_sample) and self.n_sample[i] is not None:
|
|
11958
|
+
n_samples = self.n_sample[i]
|
|
11959
|
+
total_samples += n_samples
|
|
11960
|
+
else:
|
|
11961
|
+
n_samples = 0
|
|
11962
|
+
|
|
11963
|
+
# Track configuration
|
|
11964
|
+
config = (n_traces, n_samples)
|
|
11965
|
+
config_counts[config] = config_counts.get(config, 0) + 1
|
|
11966
|
+
|
|
11967
|
+
# Show detailed information
|
|
11968
|
+
if successful_imports == 1:
|
|
11969
|
+
# Single file - show detailed info
|
|
11970
|
+
idx = len(self.streams) - 1
|
|
11971
|
+
n_traces = len(self.trace_position[idx]) if idx < len(self.trace_position) and self.trace_position[idx] else 0
|
|
11972
|
+
n_samp = self.n_sample[idx] if idx < len(self.n_sample) and self.n_sample[idx] else 0
|
|
11973
|
+
QMessageBox.information(
|
|
11974
|
+
self, "Import Successful",
|
|
11975
|
+
f"Successfully imported ASCII matrix\n\n"
|
|
11976
|
+
f"Traces: {n_traces}\n"
|
|
11977
|
+
f"Samples per trace: {n_samp}\n"
|
|
11978
|
+
f"Orientation: {'Transposed (traces were in rows)' if params['transpose'] else 'Standard (traces in columns)'}"
|
|
11979
|
+
)
|
|
11980
|
+
else:
|
|
11981
|
+
# Multiple files - show configuration breakdown
|
|
11982
|
+
summary_msg = f"Successfully imported {successful_imports} ASCII file(s)\n\n"
|
|
11983
|
+
|
|
11984
|
+
# Show configuration breakdown
|
|
11985
|
+
if len(config_counts) > 0:
|
|
11986
|
+
for (n_traces, n_samples), count in sorted(config_counts.items()):
|
|
11987
|
+
summary_msg += f"• {count} file(s): {n_traces} traces × {n_samples} samples\n"
|
|
11988
|
+
|
|
11989
|
+
QMessageBox.information(self, "Import Successful", summary_msg.rstrip())
|
|
11990
|
+
|
|
11991
|
+
# Reset transpose yes-to-all flag after batch import completes
|
|
11992
|
+
if hasattr(self, '_transpose_yes_to_all'):
|
|
11993
|
+
delattr(self, '_transpose_yes_to_all')
|
|
11876
11994
|
|
|
11877
11995
|
def ascii_to_obspy_stream(self, data_matrix, params):
|
|
11878
11996
|
"""Convert ASCII matrix to ObsPy Stream object"""
|
|
@@ -11939,7 +12057,24 @@ class MainWindow(QMainWindow):
|
|
|
11939
12057
|
successful_imports = 0
|
|
11940
12058
|
failed_imports = []
|
|
11941
12059
|
|
|
11942
|
-
for
|
|
12060
|
+
# Create progress dialog for batch archive import
|
|
12061
|
+
progress = None
|
|
12062
|
+
if len(archive_paths) > 1:
|
|
12063
|
+
progress = QProgressDialog("Importing archives...", "Cancel", 0, len(archive_paths), self)
|
|
12064
|
+
progress.setWindowTitle("Importing Archives")
|
|
12065
|
+
progress.setMinimumDuration(0)
|
|
12066
|
+
progress.setWindowModality(QtCore.Qt.WindowModal)
|
|
12067
|
+
progress.setValue(0)
|
|
12068
|
+
progress.show()
|
|
12069
|
+
QApplication.processEvents()
|
|
12070
|
+
|
|
12071
|
+
for idx, archive_path in enumerate(archive_paths):
|
|
12072
|
+
if progress:
|
|
12073
|
+
progress.setValue(idx)
|
|
12074
|
+
if progress.wasCanceled():
|
|
12075
|
+
break
|
|
12076
|
+
progress.setLabelText(f"Importing archive {idx+1} of {len(archive_paths)}...")
|
|
12077
|
+
QApplication.processEvents()
|
|
11943
12078
|
try:
|
|
11944
12079
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
11945
12080
|
|
|
@@ -12101,6 +12236,10 @@ class MainWindow(QMainWindow):
|
|
|
12101
12236
|
finally:
|
|
12102
12237
|
QApplication.restoreOverrideCursor()
|
|
12103
12238
|
|
|
12239
|
+
# Close progress dialog
|
|
12240
|
+
if progress:
|
|
12241
|
+
progress.setValue(len(archive_paths))
|
|
12242
|
+
|
|
12104
12243
|
# After processing all files, update UI and show results
|
|
12105
12244
|
if successful_imports > 0:
|
|
12106
12245
|
# Initialize plot labels if they don't exist
|
|
@@ -12119,19 +12258,70 @@ class MainWindow(QMainWindow):
|
|
|
12119
12258
|
# Plot the new data
|
|
12120
12259
|
self.updatePlots()
|
|
12121
12260
|
|
|
12261
|
+
# Calculate total traces and samples for successful imports
|
|
12262
|
+
total_traces = 0
|
|
12263
|
+
total_samples = 0
|
|
12264
|
+
# Track different configurations
|
|
12265
|
+
config_counts = {} # (n_traces, n_samples): count
|
|
12266
|
+
|
|
12267
|
+
if successful_imports > 0:
|
|
12268
|
+
# Count traces and samples from the newly imported files
|
|
12269
|
+
# (last successful_imports files in the lists)
|
|
12270
|
+
for i in range(len(self.streams) - successful_imports, len(self.streams)):
|
|
12271
|
+
if i >= 0 and i < len(self.trace_position) and self.trace_position[i] is not None:
|
|
12272
|
+
n_traces = len(self.trace_position[i])
|
|
12273
|
+
total_traces += n_traces
|
|
12274
|
+
else:
|
|
12275
|
+
n_traces = 0
|
|
12276
|
+
|
|
12277
|
+
if i >= 0 and i < len(self.n_sample) and self.n_sample[i] is not None:
|
|
12278
|
+
n_samples = self.n_sample[i]
|
|
12279
|
+
total_samples += n_samples * (len(self.trace_position[i]) if self.trace_position[i] else 0)
|
|
12280
|
+
else:
|
|
12281
|
+
n_samples = 0
|
|
12282
|
+
|
|
12283
|
+
# Track configuration
|
|
12284
|
+
config = (n_traces, n_samples)
|
|
12285
|
+
config_counts[config] = config_counts.get(config, 0) + 1
|
|
12286
|
+
|
|
12122
12287
|
# Show import summary
|
|
12123
12288
|
if failed_imports:
|
|
12124
|
-
error_msg = f"Successfully imported {successful_imports} archive(s).\n\
|
|
12289
|
+
error_msg = f"Successfully imported {successful_imports} archive(s).\n\n"
|
|
12290
|
+
|
|
12291
|
+
# Show configuration breakdown
|
|
12292
|
+
if len(config_counts) > 0:
|
|
12293
|
+
for (n_traces, n_samples), count in sorted(config_counts.items()):
|
|
12294
|
+
error_msg += f"• {count} archive(s): {n_traces} traces × {n_samples} samples\n"
|
|
12295
|
+
|
|
12296
|
+
error_msg += "\n"
|
|
12297
|
+
error_msg += f"Failed imports ({len(failed_imports)}):\n"
|
|
12125
12298
|
for fname, error in failed_imports[:5]: # Show first 5 errors
|
|
12126
12299
|
error_msg += f"\n• {fname}: {error}"
|
|
12127
12300
|
if len(failed_imports) > 5:
|
|
12128
12301
|
error_msg += f"\n\n... and {len(failed_imports) - 5} more"
|
|
12129
12302
|
QMessageBox.warning(self, "Import Complete with Errors", error_msg)
|
|
12130
12303
|
elif successful_imports > 0:
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
f"Successfully imported {successful_imports} ASCII archive(s)"
|
|
12134
|
-
|
|
12304
|
+
if len(archive_paths) > 1:
|
|
12305
|
+
# Batch import summary
|
|
12306
|
+
summary_msg = f"Successfully imported {successful_imports} ASCII archive(s)\n\n"
|
|
12307
|
+
|
|
12308
|
+
# Show configuration breakdown
|
|
12309
|
+
if len(config_counts) > 0:
|
|
12310
|
+
for (n_traces, n_samples), count in sorted(config_counts.items()):
|
|
12311
|
+
summary_msg += f"• {count} archive(s): {n_traces} traces × {n_samples} samples\n"
|
|
12312
|
+
|
|
12313
|
+
QMessageBox.information(self, "Import Successful", summary_msg.rstrip())
|
|
12314
|
+
else:
|
|
12315
|
+
# Single archive - detailed info
|
|
12316
|
+
idx = len(self.streams) - 1
|
|
12317
|
+
n_traces = len(self.trace_position[idx]) if idx < len(self.trace_position) and self.trace_position[idx] else 0
|
|
12318
|
+
n_samp = self.n_sample[idx] if idx < len(self.n_sample) and self.n_sample[idx] else 0
|
|
12319
|
+
QMessageBox.information(
|
|
12320
|
+
self, "Import Successful",
|
|
12321
|
+
f"Imported {os.path.basename(archive_paths[0])}\n\n"
|
|
12322
|
+
f"Traces: {n_traces}\n"
|
|
12323
|
+
f"Samples per trace: {n_samp}"
|
|
12324
|
+
)
|
|
12135
12325
|
|
|
12136
12326
|
#######################################
|
|
12137
12327
|
# File loading and processing functions
|
|
@@ -12461,9 +12651,6 @@ class MainWindow(QMainWindow):
|
|
|
12461
12651
|
# Close progress dialog
|
|
12462
12652
|
progress.setValue(len(fileNames_new))
|
|
12463
12653
|
|
|
12464
|
-
# Success - dialogs removed for smoother workflow
|
|
12465
|
-
# Files are now visible in the file list
|
|
12466
|
-
|
|
12467
12654
|
self.sortFiles() # Sort the files based on the file names
|
|
12468
12655
|
self.updateFileListDisplay() # Update the file list display
|
|
12469
12656
|
self.sortFileList() # Sort the file list widget
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
pyckster/__init__.py,sha256=
|
|
1
|
+
pyckster/__init__.py,sha256=qESM7e1M2mtkZ_qclsH4wVv39yjHCQrkPg1gO0xze7E,905
|
|
2
2
|
pyckster/__main__.py,sha256=zv3AGVKorKo2tgWOEIcVnkDbp15eepSqka3IoWH_adU,406
|
|
3
3
|
pyckster/auto_picking.py,sha256=fyZiOj0Ib-SB_oxsKnUszECHbOjo4JE23JVQILGYZco,12754
|
|
4
4
|
pyckster/bayesian_inversion.py,sha256=kdnKOlAZ0JlYLipuFDHlwS7dU8LtI-0aMb90bYpEHhE,163523
|
|
5
|
-
pyckster/core.py,sha256=
|
|
5
|
+
pyckster/core.py,sha256=fcrnR7L18Gwc5AiXj5aWpdqRnjPDZbyWOFSyg0FIsa8,1250792
|
|
6
6
|
pyckster/dispersion_stack_viewer.py,sha256=7Dh2e1tSct062D7Qh6nNrMdJcqKWcJvDIv84V8sC6C8,12645
|
|
7
7
|
pyckster/inversion_app.py,sha256=ovM44oYBFsvfKxO7rjjThUhkJnLDLZZ0R6ZVp-5r66E,60676
|
|
8
8
|
pyckster/inversion_manager.py,sha256=P8i1fqUJKMWkd-9PoDmNtmQuKglGKTeSuptUUA57D-8,15393
|
|
@@ -17,9 +17,9 @@ pyckster/surface_wave_profiling.py,sha256=L9KidhKmfGvVoPZjf6us3c49VB7VPB_VcsDqRx
|
|
|
17
17
|
pyckster/sw_utils.py,sha256=-2CpQ9BkmUHaMBrNy2qXx1R-g9qPX8D9igKi_G-iRHE,13213
|
|
18
18
|
pyckster/tab_factory.py,sha256=NlCIC6F8BrEu7a8BYOJJdWy5ftpX_zKDLj7SbcwBbh8,14519
|
|
19
19
|
pyckster/visualization_utils.py,sha256=bgODn21NAQx1FOMPj91kdDd0szKOgUyfZ3cQlyu2PF8,47947
|
|
20
|
-
pyckster-26.2.
|
|
21
|
-
pyckster-26.2.
|
|
22
|
-
pyckster-26.2.
|
|
23
|
-
pyckster-26.2.
|
|
24
|
-
pyckster-26.2.
|
|
25
|
-
pyckster-26.2.
|
|
20
|
+
pyckster-26.2.3.dist-info/licenses/LICENCE,sha256=-uaAIm20JrJKoMdCdn2GlFQfNU4fbsHWK3eh4kIQ_Ec,35143
|
|
21
|
+
pyckster-26.2.3.dist-info/METADATA,sha256=lUsnI0wr-TluggRLaC0MF5rUyFfm-VMhZ0H0YK5uZk0,4567
|
|
22
|
+
pyckster-26.2.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
23
|
+
pyckster-26.2.3.dist-info/entry_points.txt,sha256=yrOQx1wHi84rbxX_ZYtYaVcK3EeuRhHRQDZRc8mB0NI,100
|
|
24
|
+
pyckster-26.2.3.dist-info/top_level.txt,sha256=eaihhwhEmlysgdZE4HmELFdSUwlXcMv90YorkjOXujQ,9
|
|
25
|
+
pyckster-26.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|