DASPy-toolbox 1.1.4__tar.gz → 1.1.6__tar.gz

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.
Files changed (30) hide show
  1. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/DASPy_toolbox.egg-info/PKG-INFO +1 -1
  2. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/PKG-INFO +1 -1
  3. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/advanced_tools/channel.py +17 -18
  4. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/basic_tools/preprocessing.py +3 -3
  5. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/basic_tools/visualization.py +20 -10
  6. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/core/collection.py +65 -26
  7. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/core/read.py +26 -5
  8. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/core/section.py +41 -7
  9. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/core/write.py +12 -3
  10. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/setup.py +1 -1
  11. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/DASPy_toolbox.egg-info/SOURCES.txt +0 -0
  12. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/DASPy_toolbox.egg-info/dependency_links.txt +0 -0
  13. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/DASPy_toolbox.egg-info/entry_points.txt +0 -0
  14. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/DASPy_toolbox.egg-info/requires.txt +0 -0
  15. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/DASPy_toolbox.egg-info/top_level.txt +0 -0
  16. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/LICENSE +0 -0
  17. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/README.md +0 -0
  18. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/__init__.py +0 -0
  19. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/advanced_tools/__init__.py +0 -0
  20. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/advanced_tools/decomposition.py +0 -0
  21. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/advanced_tools/denoising.py +0 -0
  22. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/advanced_tools/fdct.py +0 -0
  23. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/advanced_tools/strain2vel.py +0 -0
  24. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/basic_tools/__init__.py +0 -0
  25. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/basic_tools/filter.py +0 -0
  26. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/basic_tools/freqattributes.py +0 -0
  27. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/core/__init__.py +0 -0
  28. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/core/dasdatetime.py +0 -0
  29. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/daspy/core/example.pkl +0 -0
  30. {daspy_toolbox-1.1.4 → daspy_toolbox-1.1.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DASPy-toolbox
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing, which comprises classic seismic data processing techniques and Specialized algorithms for DAS applications.
5
5
  Home-page: https://github.com/HMZ-03/DASPy
6
6
  Author: Minzhe Hu, Zefeng Li
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DASPy-toolbox
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing, which comprises classic seismic data processing techniques and Specialized algorithms for DAS applications.
5
5
  Home-page: https://github.com/HMZ-03/DASPy
6
6
  Author: Minzhe Hu, Zefeng Li
@@ -365,24 +365,6 @@ def turning_points(data, data_type='coordinate', thresh=5, depth_info=False,
365
365
  raise ValueError('Data_type should be \'coordinate\' or \'waveform\'.')
366
366
 
367
367
 
368
- def _equally_spacing(dist, dx):
369
- index = [[], []]
370
- residual = [0, abs(dist[0]-dx)]
371
- for i in range(2, len(dist)+1):
372
- res = []
373
- for j in range(i):
374
- res.append(residual[j] + abs(dx - sum(dist[j:i])))
375
- residual.append(min(res))
376
- k = np.argmin(res)
377
- if k > 0:
378
- index.append(index[k] + [k])
379
- else:
380
- index.append(index[k])
381
- # print(index, residual)
382
-
383
- return index[-1]
384
-
385
-
386
368
  def channel_spacing(geometry, depth_info=False):
387
369
  nch = len(geometry)
388
370
  dist = np.zeros(nch - 1)
@@ -456,6 +438,23 @@ def closest_channel_to_point(geometry, points, verbose=False):
456
438
  return channels[closest_index]
457
439
 
458
440
 
441
+ def _equally_spacing(dist, dx):
442
+ index = [[], []]
443
+ residual = [0, abs(dist[0]-dx)]
444
+ for i in range(2, len(dist)+1):
445
+ res = []
446
+ for j in range(i):
447
+ res.append(residual[j] + abs(dx - sum(dist[j:i])))
448
+ residual.append(min(res))
449
+ k = np.argmin(res)
450
+ if k > 0:
451
+ index.append(index[k] + [k])
452
+ else:
453
+ index.append(index[k])
454
+
455
+ return index[-1]
456
+
457
+
459
458
  def equally_spaced_channels(geometry, dx, depth_info=False, verbose=False):
460
459
  """
461
460
  Find equally spaced channel numbers based on known DAS latitude and
@@ -145,14 +145,14 @@ def downsampling(data, xint=None, tint=None, stack=True, lowpass_filter=True):
145
145
  if stack:
146
146
  data_ds = stacking(data, xint)
147
147
  else:
148
- data_ds = data_ds[::xint]
148
+ data_ds = data_ds[::xint].copy()
149
149
  if tint and tint > 1:
150
150
  if lowpass_filter:
151
151
  data_ds = lowpass_cheby_2(data_ds, 1, 1 / 2 / tint)
152
152
  if len(data_ds.shape) == 1:
153
- data_ds = data_ds[::tint]
153
+ data_ds = data_ds[::tint].copy()
154
154
  else:
155
- data_ds = data_ds[:, ::tint]
155
+ data_ds = data_ds[:, ::tint].copy()
156
156
  return data_ds
157
157
 
158
158
 
@@ -1,6 +1,6 @@
1
1
  # Purpose: Plot data
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.1.6
3
+ # Date: 2025.5.20
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import numpy as np
6
6
  import matplotlib.pyplot as plt
@@ -9,11 +9,11 @@ from collections.abc import Sequence
9
9
 
10
10
  def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
11
11
  title=None, transpose=False, t0=0, x0=0, pick=None, f=None, k=None,
12
- t=None, c=None, cmap=None, vmin=None, vmax=None, dB=False,
13
- xmode='distance', tmode='time', xlim=None, ylim=None, xlog=False,
14
- ylog=False, xinv=False, yinv=False, xlabel=True, ylabel=True,
15
- xticklabels=True, yticklabels=True, colorbar=True, colorbar_label=None,
16
- savefig=None):
12
+ t=None, c=None, cmap=None, vmin=None, vmin_per=None, vmax=None,
13
+ vmax_per=None, dB=False, xmode='distance', tmode='time', xlim=None,
14
+ ylim=None, xlog=False, ylog=False, xinv=False, yinv=False, xlabel=True,
15
+ ylabel=True, xticklabels=True, yticklabels=True, colorbar=True,
16
+ colorbar_label=None, savefig=None):
17
17
  """
18
18
  Plot several types of 2-D seismological data.
19
19
 
@@ -40,6 +40,8 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
40
40
  :param cmap: str or Colormap. The Colormap instance or registered colormap
41
41
  name used to map scalar data to colors.
42
42
  :param vmin, vmax: Define the data range that the colormap covers.
43
+ :param vmin_per, vmax_per: float. Define the data range that the colormap
44
+ covers by percentile.
43
45
  :param dB: bool. Transfer data unit to dB and take 1 as the reference value.
44
46
  :param xmode: str. 'distance' or 'channel'.
45
47
  :param tmode: str. 'time' or 'sampling'.
@@ -67,7 +69,9 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
67
69
 
68
70
  if obj in ['waveform', 'phasepick']:
69
71
  cmap = 'RdBu' if cmap is None else cmap
70
- vmax = np.percentile(abs(data), 80) if vmax is None else vmax
72
+ if vmax is None:
73
+ vmax_per = 80 if vmax_per is None else vmax_per
74
+ vmax = np.percentile(data, vmax_per)
71
75
  vmin = -vmax if vmin is None else vmin
72
76
  origin = 'upper'
73
77
  if fs is None or tmode == 'sampling':
@@ -98,13 +102,19 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
98
102
  c=pick_color[phase])
99
103
 
100
104
  elif obj in ['spectrum', 'spectrogram', 'fk', 'dispersion']:
101
- if isinstance(data[0,0], (complex, np.complex64)):
105
+ if np.iscomplex(data).any():
102
106
  data = abs(data)
103
107
  if dB:
104
108
  data = 20 * np.log10(data)
105
109
  cmap = 'jet' if cmap is None else cmap
106
- vmax = np.percentile(abs(data), 80) if vmax is None else vmax
107
- vmin = np.percentile(abs(data), 20) if vmin is None else vmin
110
+
111
+ if vmax is None:
112
+ vmax_per = 80 if vmax_per is None else vmax_per
113
+ vmax = np.percentile(data, vmax_per)
114
+ if vmin is None:
115
+ vmin_per = 20 if vmin_per is None else vmin_per
116
+ vmin = np.percentile(data, vmin_per)
117
+
108
118
  if obj == 'spectrum':
109
119
  origin = 'lower'
110
120
  if dx is None or xmode.lower() == 'channel':
@@ -1,8 +1,9 @@
1
1
  # Purpose: Module for handling Collection objects.
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.3.30
3
+ # Date: 2025.5.21
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import os
6
+ import gc
6
7
  import warnings
7
8
  import numpy as np
8
9
  from copy import deepcopy
@@ -12,10 +13,6 @@ from daspy.core.read import read
12
13
  from daspy.core.dasdatetime import DASDateTime
13
14
 
14
15
 
15
- cascade_method = ['time_integration', 'time_differential', 'downsampling',
16
- 'bandpass', 'bandstop', 'lowpass', 'highpass',
17
- 'lowpass_cheby_2']
18
-
19
16
  class Collection(object):
20
17
  def __init__(self, fpath, ftype=None, flength=None, meta_from_file=True,
21
18
  timeinfo_slice=slice(None), timeinfo_format=None,
@@ -265,8 +262,18 @@ class Collection(object):
265
262
  kwargs_list.append(kwargs)
266
263
  return method_list, kwargs_list
267
264
 
265
+ def _kwargs_initialization(method_list, kwargs_list):
266
+ for j, method in enumerate(method_list):
267
+ if method == 'time_integration':
268
+ kwargs_list[j]['c'] = 0
269
+ elif method == 'time_differential':
270
+ kwargs_list[j]['prepend'] = 0
271
+ elif method in ['bandpass', 'bandstop', 'lowpass',
272
+ 'highpass', 'lowpass_cheby_2']:
273
+ kwargs_list[j]['zi'] = 0
274
+
268
275
  def process(self, operations, savepath='./processed', merge=1,
269
- suffix='_pro', ftype=None, **read_kwargs):
276
+ suffix='_pro', ftype=None, dtype=None, **read_kwargs):
270
277
  """
271
278
  :param operations: list. Each element of operations list should be [str
272
279
  of method name, dict of kwargs].
@@ -274,8 +281,10 @@ class Collection(object):
274
281
  :param merge: int or str. int for merge several processed files into 1.
275
282
  'all' for merge all files.
276
283
  :param suffix: str. Suffix for processed files.
277
- :param ftype: None or str. None for automatic detection, or 'pkl',
278
- 'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy', 'npy'.
284
+ :param ftype: None or str. File format for saving. None for automatic
285
+ detection, or 'pkl', 'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy',
286
+ 'npy'.
287
+ :param dtype: str. The data type of the saved data.
279
288
  :param read_kwargs: dict. Paramters for read function.
280
289
  """
281
290
  if not os.path.exists(savepath):
@@ -283,36 +292,35 @@ class Collection(object):
283
292
  method_list, kwargs_list = self._optimize_for_continuity(operations)
284
293
  if merge == 'all' or merge > len(self):
285
294
  merge = len(self)
286
- for i in tqdm(range(0, len(self))):
295
+ for i in tqdm(range(len(self))):
287
296
  f = self[i]
288
297
  if os.path.getsize(f) == 0:
289
- for j, method in enumerate(method_list):
290
- if method == 'time_integration':
291
- kwargs_list[j]['c'] = 0
292
- elif method == 'time_differential':
293
- kwargs_list[j]['prepend'] = 0
294
- elif method in ['bandpass', 'bandstop', 'lowpass',
295
- 'highpass', 'lowpass_cheby_2']:
296
- kwargs_list[j]['zi'] = 0
298
+ self._kwargs_initialization(method_list, kwargs_list)
299
+ continue
300
+ try:
301
+ sec = read(f, ftype=self.ftype, **read_kwargs)
302
+ except Exception as e:
303
+ warnings.warn(f'Error reading {f}: {e}')
304
+ self._kwargs_initialization(method_list, kwargs_list)
297
305
  continue
298
- sec = read(f, ftype=self.ftype, **read_kwargs)
299
306
  for j, method in enumerate(method_list):
300
307
  if method in ['taper', 'cosine_taper']:
301
- if not ((i==0 and kwargs_list[j] != 'right') or
302
- (i == len(self) - 1 and kwargs_list[j] != 'left')):
308
+ if not ((i==0 and kwargs_list[j]['side'] != 'right') or
309
+ (i == len(self) - 1 and kwargs_list[j]['side'] !=
310
+ 'left')):
303
311
  continue
304
- out = eval(f'sec.{method}')(**kwargs_list[j])
312
+ out = getattr(sec, method)(**kwargs_list[j])
305
313
  if method == 'time_integration':
306
- kwargs_list[j]['c'] = sec.data[:, -1]
314
+ kwargs_list[j]['c'] = sec.data[:, -1].copy()
307
315
  elif method == 'time_differential':
308
- kwargs_list[j]['prepend'] = sec.data[:, -1]
316
+ kwargs_list[j]['prepend'] = sec.data[:, -1].copy()
309
317
  elif method in ['bandpass', 'bandstop', 'lowpass', 'highpass',
310
318
  'lowpass_cheby_2']:
311
319
  kwargs_list[j]['zi'] = out
312
320
 
313
- if i % merge == 0:
321
+ if i % merge == 0:
314
322
  if i != 0:
315
- sec_merge.save(filepath)
323
+ sec_merge.save(filepath, dtype=dtype)
316
324
  sec_merge = sec
317
325
  f0, f1 = os.path.splitext(os.path.basename(f))
318
326
  if ftype is not None:
@@ -320,4 +328,35 @@ class Collection(object):
320
328
  filepath = os.path.join(savepath, f0+suffix+f1)
321
329
  else:
322
330
  sec_merge += sec
323
- sec_merge.save(filepath)
331
+ del sec
332
+ gc.collect()
333
+ sec_merge.save(filepath, dtype=dtype)
334
+
335
+ # Dynamically add methods for cascade_methods
336
+ def _create_cascade_method(method_name):
337
+ def cascade_method(self, savepath='./processed', merge=1,
338
+ suffix=f'_{method_name}', ftype=None, dtype=None,
339
+ **kwargs):
340
+ """
341
+ Automatically generated method for {method_name}.
342
+ Applies the {method_name} operation to the data and saves the result.
343
+
344
+ :param savepath: str. Path to save processed files.
345
+ :param merge: int or str. int for merge several processed files into 1.
346
+ 'all' for merge all files.
347
+ :param suffix: str. Suffix for processed files.
348
+ :param ftype: None or str. None for automatic detection, or 'pkl',
349
+ 'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy', 'npy'.
350
+ :param kwargs: dict. Parameters for the {method_name} operation.
351
+ :param dtype: str. The data type of the saved data.
352
+ """
353
+ operations = [[method_name, kwargs]]
354
+ self.process(operations, savepath=savepath, merge=merge, suffix=suffix,
355
+ ftype=ftype, dtype=dtype)
356
+ return cascade_method
357
+
358
+
359
+ for method in ['time_integration', 'time_differential', 'downsampling',
360
+ 'bandpass', 'bandstop', 'lowpass', 'highpass',
361
+ 'lowpass_cheby_2']:
362
+ setattr(Collection, method, _create_cascade_method(method))
@@ -1,6 +1,6 @@
1
1
  # Purpose: Module for reading DAS data.
2
2
  # Author: Minzhe Hu
3
- # Date: 2024.11.20
3
+ # Date: 2025.5.21
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  # Partially modified from
6
6
  # https://github.com/RobbinLuo/das-toolkit/blob/main/DasTools/DasPrep.py
@@ -10,6 +10,7 @@ import pickle
10
10
  import numpy as np
11
11
  import h5py
12
12
  import segyio
13
+ from typing import Union
13
14
  from pathlib import Path
14
15
  from nptdms import TdmsFile
15
16
  from daspy.core.section import Section
@@ -17,7 +18,7 @@ from daspy.core.dasdatetime import DASDateTime, utc
17
18
 
18
19
 
19
20
  def read(fname=None, output_type='section', ftype=None, headonly=False,
20
- **kwargs):
21
+ dtype=None, **kwargs) -> Union[Section, tuple]:
21
22
  """
22
23
  Read a .pkl/.pickle, .tdms, .h5/.hdf5, .segy/.sgy file.
23
24
 
@@ -34,6 +35,7 @@ def read(fname=None, output_type='section', ftype=None, headonly=False,
34
35
  :param ch1: int. The first channel required.
35
36
  :param ch2: int. The last channel required (not included).
36
37
  :param dch: int. Channel step.
38
+ :param dtype: str. The data type of the returned data.
37
39
  :return: An instance of daspy.Section, or numpy.array for data and a
38
40
  dictionary for metadata.
39
41
  """
@@ -55,10 +57,13 @@ def read(fname=None, output_type='section', ftype=None, headonly=False,
55
57
  ftype = ftype.replace(*rtp)
56
58
  data, metadata = fun_map[ftype](fname, headonly=headonly, **kwargs)
57
59
 
60
+ if dtype is not None:
61
+ data = data.astype(dtype)
58
62
  if output_type.lower() == 'section':
59
63
  metadata['source'] = Path(fname)
60
64
  metadata['source_type'] = ftype
61
- return Section(data.astype(float), **metadata)
65
+ data[np.isnan(data)] = 0
66
+ return Section(data, **metadata)
62
67
  elif output_type.lower() == 'array':
63
68
  return data, metadata
64
69
 
@@ -184,6 +189,22 @@ def _read_h5(fname, headonly=False, **kwargs):
184
189
  'dx': dx * dch, 'start_channel': ch1,
185
190
  'start_distance': ch1 * dx,
186
191
  'gauge_length': h5_file.get('GaugeLength')[()]}
192
+ elif len(h5_file.keys()) == 3: # OpataSense
193
+ nch = h5_file['data'].shape[1]
194
+ ch1 = kwargs.pop('ch1', 0)
195
+ ch2 = kwargs.pop('ch2', nch)
196
+ dch = kwargs.pop('dch', 1)
197
+ if headonly:
198
+ data = np.zeros_like(h5_file['data'])
199
+ else:
200
+ data = h5_file['data'][ch1:ch2:dch, :]
201
+ dx = (h5_file['x_axis'][-1] - h5_file['x_axis'][0]) / \
202
+ (len(h5_file['x_axis']) - 1)
203
+ fs = (len(h5_file['t_axis']) - 1) / (h5_file['t_axis'][-1] -
204
+ h5_file['t_axis'][0])
205
+ metadata = {'dx': dx, 'fs': fs, 'start_channel': ch1,
206
+ 'start_distance': h5_file['x_axis'][0] + dx * ch1,
207
+ 'start_time': h5_file['t_axis'][0]}
187
208
  elif set(h5_file.keys()) == {'Mapping', 'Acquisition'}: # Silixa/iDAS
188
209
  nch = h5_file['Acquisition/Raw[0]'].attrs['NumberOfLoci']
189
210
  ch1 = kwargs.pop('ch1', 0)
@@ -274,7 +295,7 @@ def _read_h5(fname, headonly=False, **kwargs):
274
295
  ch2 = kwargs.pop('ch2', nch)
275
296
  dch = kwargs.pop('dch', 1)
276
297
  if headonly:
277
- data = np.zeros_like(h5_file['raw_data'])
298
+ data = np.zeros_like(h5_file['data'])
278
299
  else:
279
300
  data = h5_file['data'][ch1:ch2:dch, :]
280
301
  attr = h5_file['data'].attrs
@@ -340,7 +361,7 @@ def _read_h5(fname, headonly=False, **kwargs):
340
361
  else:
341
362
  if len(dataset.shape) == 3: # Febus A1-R
342
363
  data = dataset[:, :, ch1 - start_channel:ch2 - start_channel
343
- :dch].T.reshape(((ch2 - ch1) // dch, -1))
364
+ :dch].reshape((-1, (ch2 - ch1) // dch)).T
344
365
  elif len(dataset.shape) == 2: # Febus A1
345
366
  data = dataset[:, ch1 - start_channel:ch2 - start_channel:
346
367
  dch].T
@@ -1,6 +1,6 @@
1
1
  # Purpose: Module for handling Section objects.
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.4.2
3
+ # Date: 2025.5.15
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import warnings
6
6
  import os
@@ -162,6 +162,10 @@ class Section(object):
162
162
  def nt(self):
163
163
  return self.data.shape[1]
164
164
 
165
+ @property
166
+ def channel_number(self):
167
+ return np.arange(self.nch).astype(int) + self.start_channel
168
+
165
169
  @property
166
170
  def end_channel(self):
167
171
  return self.start_channel + self.nch - 1
@@ -172,7 +176,7 @@ class Section(object):
172
176
 
173
177
  @property
174
178
  def channel_distance(self):
175
- return self.dx * np.arange(self.nch) + self.start_channel
179
+ return self.dx * np.arange(self.nch) + self.start_distance
176
180
 
177
181
  @property
178
182
  def end_distance(self):
@@ -232,10 +236,10 @@ class Section(object):
232
236
  data = np.zeros((max(channel_no) + 1, nt))
233
237
  for i, tr in enumerate(st):
234
238
  data[channel_no[i]] = tr.data
235
-
239
+ # data = data.astype(float)
236
240
  warnings.warn('obspy.core.stream.Stream doesn\'t include channel '
237
241
  'interval. Please set dx manually.')
238
- return cls(data.astype(float), None, fs, start_channel=start_channel,
242
+ return cls(data, None, fs, start_channel=start_channel,
239
243
  start_time=start_time, scale=scale, source=source)
240
244
 
241
245
  @classmethod
@@ -418,7 +422,7 @@ class Section(object):
418
422
  start_channel=self.start_channel, channel_spacing=self.dx,
419
423
  unit=unit)
420
424
 
421
- def save(self, fname=None, ftype=None, keep_format=False):
425
+ def save(self, fname=None, ftype=None, keep_format=False, dtype=None):
422
426
  """
423
427
  Save the instance as a pickle file or update the raw file and resave as
424
428
  new file.
@@ -430,6 +434,7 @@ class Section(object):
430
434
  :param keep_format: bool. If True, we will make a copy of the
431
435
  self.source file and make changes to it. This will strictly preserve
432
436
  the original format, but will cost more IO resources.
437
+ :param dtype: str. The data type of the saved data.
433
438
  """
434
439
  if fname is None:
435
440
  if hasattr(self, 'source'):
@@ -452,9 +457,36 @@ class Section(object):
452
457
  raise ValueError('self.source is not a file.')
453
458
  if ftype != self.source_type:
454
459
  raise ValueError('self.source_type is different from ftype.')
455
- write(self, fname, ftype=ftype, raw_fname=self.source)
460
+ write(self, fname, ftype=ftype, raw_fname=self.source, dtype=dtype)
461
+ else:
462
+ write(self, fname, ftype=ftype, dtype=dtype)
463
+
464
+ return self
465
+
466
+ def concat(self, other, reverse=True):
467
+ """
468
+ Concatenate two sections in space.
469
+
470
+ :param other: Section. Another section to concatenate.
471
+ :param reverse: bool. If True, the start channels of the two are
472
+ connected, and the channel numbers of the original Section instances
473
+ becomes a negative number.
474
+ """
475
+ if isinstance(other, Section):
476
+ assert self.fs == other.fs, 'The sampling rate of the two ' \
477
+ 'sections should be the same.'
478
+ assert self.dx == other.dx, 'The channel interval of the two ' \
479
+ 'sections should be the same.'
480
+ assert self.duration == other.duration, 'The duration of the two ' \
481
+ 'sections should be the same.'
482
+ if reverse:
483
+ self.start_channel = -self.end_channel
484
+ self.start_distance = -self.end_distance
485
+ self.data = np.vstack((self.data[::-1], other.data))
486
+ else:
487
+ self.data = np.vstack((self.data, other.data))
456
488
  else:
457
- write(self, fname, ftype=ftype)
489
+ raise TypeError('The input should be Section.')
458
490
 
459
491
  return self
460
492
 
@@ -495,6 +527,8 @@ class Section(object):
495
527
  :param cmap: str or Colormap. The Colormap instance or registered
496
528
  colormap name used to map scalar data to colors.
497
529
  :param vmin, vmax: Define the data range that the colormap covers.
530
+ :param vmin_per, vmax_per: float. Define the data range that the
531
+ colormap covers by percentile.
498
532
  :param xlim, ylim: Set the x-axis and y-axis view limits.
499
533
  :param dB: bool. Transfer data unit to dB and take 1 as the reference
500
534
  value.
@@ -1,6 +1,6 @@
1
1
  # Purpose: Module for writing DAS data.
2
2
  # Author: Minzhe Hu
3
- # Date: 2024.11.20
3
+ # Date: 2025.5.21
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import os
6
6
  import warnings
@@ -13,12 +13,15 @@ from nptdms import TdmsFile, TdmsWriter, RootObject, GroupObject, ChannelObject
13
13
  from datetime import datetime
14
14
 
15
15
 
16
- def write(sec, fname, ftype=None, raw_fname=None):
16
+ def write(sec, fname, ftype=None, raw_fname=None, dtype=None):
17
17
  fun_map = {'tdms': _write_tdms, 'h5': _write_h5, 'sgy': _write_segy}
18
18
  if ftype is None:
19
19
  ftype = str(fname).lower().split('.')[-1]
20
20
  ftype.replace('hdf5', 'h5')
21
21
  ftype.replace('segy', 'sgy')
22
+ if dtype is not None:
23
+ sec = sec.copy()
24
+ sec.data = sec.data.astype(dtype)
22
25
  if ftype == 'pkl':
23
26
  write_pkl(sec, fname)
24
27
  elif ftype == 'npy':
@@ -175,6 +178,12 @@ def _write_h5(sec, fname, raw_fname=None):
175
178
  if hasattr(sec, 'gauge_length'):
176
179
  _update_h5_dataset(h5_file, '/', 'GaugeLength',
177
180
  sec.gauge_length)
181
+ elif len(h5_file.keys()) == 3:
182
+ _update_h5_dataset(h5_file, '/', 'data', sec.data)
183
+ _update_h5_dataset(h5_file, '/', 'x_axis',
184
+ sec.start_distance + np.arange(sec.nch) * sec.dx)
185
+ _update_h5_dataset(h5_file, '/', 't_axis',
186
+ sec.start_time + np.arange(sec.nt) * sec.dt)
178
187
  elif group == 'Acquisition':
179
188
  h5_file['Acquisition'].attrs['NumberOfLoci'] = sec.nch
180
189
  _update_h5_dataset(h5_file, 'Acquisition/Raw[0]/', 'RawData',
@@ -279,7 +288,7 @@ def _write_segy(sec, fname, raw_fname=None):
279
288
  with segyio.create(fname, spec) as new_file:
280
289
  new_file.header.length = sec.nch
281
290
  new_file.header.segy._filename = fname
282
- new_file.trace = sec.data.astype(np.float32)
291
+ new_file.trace = sec.data # .astype(np.float32)
283
292
  else:
284
293
  with segyio.open(raw_fname, ignore_geometry=True) as raw_file:
285
294
  spec.sorting = raw_file.sorting
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
 
4
4
  setup(
5
- name='DASPy-toolbox', version='1.1.4',
5
+ name='DASPy-toolbox', version='1.1.6',
6
6
  description=(
7
7
  'DASPy is an open-source project dedicated to provide a python package '
8
8
  'for DAS (Distributed Acoustic Sensing) data processing, which '
File without changes
File without changes
File without changes