def test_loading_from_file(manually_crafted_segy_file): """ Test the method for loading SEG-Y files. """ segy = SegY.load(manually_crafted_segy_file) # repeat tests for TFH assert repr(segy.tfh)[0:800] == ' ' * 800 assert repr(segy.tfh)[800:880] == 'Alle warten auf das Licht'.ljust(80) assert repr( segy.tfh)[880:960] == 'fürchtet euch fürchtet euch nicht'.ljust(80) assert repr( segy.tfh)[960:1040] == 'die Sonne scheint mir aus den Augen'.ljust(80) assert repr( segy.tfh)[1040:1120] == 'sie wird heut Nacht nicht untergehen'.ljust( 80) assert repr( segy.tfh)[1120:1200] == 'und die Welt zählt laut bis zehn'.ljust(80) assert repr(segy.tfh)[1200:1280] == 'Eins'.ljust(80) assert repr(segy.tfh)[1280:1360] == 'Hier kommt die Sonne'.ljust(80) assert repr(segy.tfh)[1360:] == ' ' * 1840 # repeat tests for BFH assert segy.bfh['job_id'] == 666 assert segy.bfh['sample_interval'] == 500 assert segy.bfh['samples_per_trace'] == 512 assert segy.bfh['sample_format'] == 3 # repeat tests for Geometry assert np.alltrue(segy.g.loc[:, 'TRACENO'] == np.arange(1, 25, 1)) assert np.alltrue(segy.g.loc[:, 'fTRACENO'] == np.arange(1, 25, 1)) assert np.alltrue(segy.g.loc[:, 'FFID'] == 375) assert np.alltrue(segy.g.loc[:, 'ELEVSC'] == -100) assert np.alltrue(segy.g.loc[:, 'COORDSC'] == -100) assert np.alltrue(segy.g.loc[:, 'NUMSMP'] == 512) assert np.alltrue(segy.g.loc[:, 'DT'] == 500) assert np.alltrue(segy.g.loc[:, 'YEAR'] == 1984) assert np.alltrue(segy.g.loc[:, 'HOUR'] == 10) assert np.alltrue(segy.g.loc[:, 'MINUTE'] == 51) assert np.alltrue(segy.g.loc[:, 'SOU_X'] == 50) assert np.alltrue(segy.g.loc[:, 'SOU_Y'] == 75) assert np.alltrue(segy.g.loc[:, 'REC_X'] == np.arange(0, 48, 2)) assert np.alltrue(segy.g.loc[:, 'REC_Y'] == -1) assert np.alltrue(segy.g.loc[:, 'CDP_X'] == np.arange(25, 49, 1)) assert np.alltrue(segy.g.loc[:, 'CDP_Y'] == 37) assert np.all(segy.g._df.notna()) # repeat tests for DataMatrix # check the matrix itself, in the _m property of the DM assert segy.dm._m.shape == (24, 512) assert segy.dm._m.dtype == np.int16 assert np.alltrue( segy.dm._m == np.repeat(np.arange(1, 25)[:, np.newaxis], 512, axis=1)) assert segy.dm.dt == 500 assert np.alltrue(segy.dm.t == np.arange(0, 256, 0.5)) # keep the name of the file assert segy.file == manually_crafted_segy_file.split('/')[-1] # empty BFH values are filled automatically assert segy.bfh['no_traces'] == 24
def test_creating_from_matrix(): """ Test the method for creating SegY objects from matrices. """ one = np.ones(shape=(24, 100), dtype=np.float32) segy = SegY.from_matrix(one, sample_interval=1000) # relevant BFH values are filled assert segy.bfh['sample_format'] == 5 assert segy.bfh['sample_interval'] == 1000 assert segy.bfh['samples_per_trace'] == 100 assert segy.bfh['measurement_system'] == 1 assert segy.bfh['byte_offset_of_data'] == 3600 assert segy.bfh['no_traces'] == 24 # relevant trace headers are filled assert np.alltrue(segy.g.loc[:, 'TRACENO'] == np.arange(1, 25, 1)) assert np.alltrue(segy.g.loc[:, 'FFID'] == 1) assert np.alltrue(segy.g.loc[:, 'CHAN'] == np.arange(1, 25, 1)) assert np.alltrue(segy.g.loc[:, 'ELEVSC'] == -100) assert np.alltrue(segy.g.loc[:, 'COORDSC'] == -100) assert np.alltrue(segy.g.loc[:, 'NUMSMP'] == 100) assert np.alltrue(segy.g.loc[:, 'DT'] == 1000) # the traces are from the matrix assert np.alltrue(segy.dm._m == np.ones(shape=(24, 100))) assert segy.dm.dt == 1000 assert np.alltrue(segy.dm.t == np.arange(0, 100, 1)) # for a different matrix two = np.ones(shape=(48, 10), dtype=np.int16) * 2 segy = SegY.from_matrix(two, sample_interval=500) assert segy.bfh['sample_format'] == 3 assert segy.bfh['sample_interval'] == 500 assert segy.bfh['samples_per_trace'] == 10 assert segy.bfh['measurement_system'] == 1 assert segy.bfh['byte_offset_of_data'] == 3600 assert segy.bfh['no_traces'] == 48 assert np.alltrue(segy.g.loc[:, 'TRACENO'] == np.arange(1, 49, 1)) assert np.alltrue(segy.g.loc[:, 'FFID'] == 1) assert np.alltrue(segy.g.loc[:, 'CHAN'] == np.arange(1, 49, 1)) assert np.alltrue(segy.g.loc[:, 'ELEVSC'] == -100) assert np.alltrue(segy.g.loc[:, 'COORDSC'] == -100) assert np.alltrue(segy.g.loc[:, 'NUMSMP'] == 10) assert np.alltrue(segy.g.loc[:, 'DT'] == 500) assert np.alltrue(segy.dm._m == np.ones(shape=(48, 10)) * 2) assert segy.dm.dt == 500 assert np.alltrue(segy.dm.t == np.arange(0, 5, 0.5))
def test_filter(manually_crafted_segy_file): """ Test the filter method of the DataMatrix. """ segy = SegY.load(manually_crafted_segy_file) dm = segy.dm assert isinstance(dm._headers, Geometry) assert dm._m.shape == (24, 512) assert np.alltrue(dm._headers.loc[:, 'REC_X'] == np.arange(0, 48, 2)) new = dm.filter('REC_X', 10, 40, 2) assert isinstance(new._headers, Geometry) assert new._m.shape == (16, 512) assert np.alltrue(new._headers.loc[:, 'REC_X'] == np.arange(10, 42, 2)) new = dm.filter('REC_X', 0, 24, 4) assert isinstance(new._headers, Geometry) # assert new._m.shape == (7, 512) assert np.alltrue(new._headers.loc[:, 'REC_X'] == np.arange(0, 28, 4))
def test_loading_from_little_endian_file( manually_crafted_little_endian_segy_file): """ Test the method for loading SEG-Y files with little-endian byte-order. """ segy = SegY.load(manually_crafted_little_endian_segy_file) assert segy.bfh['job_id'] == 666 assert segy.bfh['sample_interval'] == 500 assert segy.bfh['samples_per_trace'] == 512 assert segy.bfh['sample_format'] == 3 assert np.alltrue(segy.g.loc[:, 'TRACENO'] == np.arange(1, 25, 1)) assert np.alltrue(segy.g.loc[:, 'fTRACENO'] == np.arange(1, 25, 1)) assert np.alltrue(segy.g.loc[:, 'FFID'] == 375) assert np.alltrue(segy.g.loc[:, 'ELEVSC'] == -100) assert np.alltrue(segy.g.loc[:, 'COORDSC'] == -100) assert np.alltrue(segy.g.loc[:, 'NUMSMP'] == 512) assert np.alltrue(segy.g.loc[:, 'DT'] == 500) assert np.alltrue(segy.g.loc[:, 'YEAR'] == 1984) assert np.alltrue(segy.g.loc[:, 'HOUR'] == 10) assert np.alltrue(segy.g.loc[:, 'MINUTE'] == 51) assert np.alltrue(segy.g.loc[:, 'SOU_X'] == 50) assert np.alltrue(segy.g.loc[:, 'SOU_Y'] == 75) assert np.alltrue(segy.g.loc[:, 'REC_X'] == np.arange(0, 48, 2)) assert np.alltrue(segy.g.loc[:, 'REC_Y'] == -1) assert np.alltrue(segy.g.loc[:, 'CDP_X'] == np.arange(25, 49, 1)) assert np.alltrue(segy.g.loc[:, 'CDP_Y'] == 37) assert np.all(segy.g._df.notna()) # repeat tests for DataMatrix # check the matrix itself, in the _m property of the DM assert segy.dm._m.shape == (24, 512) assert segy.dm._m.dtype == np.int16 assert np.alltrue(segy.dm._m == np.repeat( np.arange(1, 48, 2)[:, np.newaxis], 512, axis=1)) assert segy.dm.dt == 500 assert np.alltrue(segy.dm.t == np.arange(0, 256, 0.5)) # keep the name of the file assert segy.file == manually_crafted_little_endian_segy_file.split('/')[-1] # empty BFH values are filled automatically assert segy.bfh['no_traces'] == 24
def test_crop(manually_crafted_segy_file): """ Test the crop method of the DataMatrix. """ segy = SegY.load(manually_crafted_segy_file) dm = segy.dm assert np.alltrue(dm.t == np.arange(0, 256, 0.5)) assert dm._m.shape == (24, 512) new = dm.crop(128) assert np.alltrue(new.t == np.arange(0, 128.5, 0.5)) assert new._m.shape == (24, 257) new = dm.crop(100) assert np.alltrue(new.t == np.arange(0, 100.5, 0.5)) assert new._m.shape == (24, 201) dm.crop(128, inplace=True) assert np.alltrue(dm.t == np.arange(0, 128.5, 0.5)) assert dm._m.shape == (24, 257)
def test_resample(manually_crafted_segy_file): """ Test the resample method of the DataMatrix. """ segy = SegY.load(manually_crafted_segy_file) dm = segy.dm assert repr(dm) == 'DataMatrix: 24 traces, 512 samples, dt=500' assert dm._m.shape == (24, 512) assert np.alltrue(dm.t == np.arange(0, 256, 0.5)) new = dm.resample(1000) assert repr(new) == 'DataMatrix: 24 traces, 256 samples, dt=1000' assert new._m.shape == (24, 256) assert np.alltrue(new.t == np.arange(0, 256, 1)) new = dm.resample(2000) assert repr(new) == 'DataMatrix: 24 traces, 128 samples, dt=2000' assert new._m.shape == (24, 128) assert np.alltrue(new.t == np.arange(0, 256, 2)) with pytest.raises(ValueError): new = dm.resample(256)
def export_for_tesseral(self, x0, x1, base_filename, *, dz=0.01, half_space_depth=100): """ Export model to SEG-Y format for use in Tesseral. Args: x0 : Start of the x axis. x1 : End of the x axis. base_filename : Base file name for resulting SEG-Y files. The parameter name and file extension will be appended automatically. dz: Discretization step for z-axis in m. Default to 1 cm. half_space_depth: How deep should the half-space be in the model. Notes: Creates three SEG-Y files: one with values of Vp, one with values of Vs, and one with values of rho. """ hs = [layer.h for layer in self._layers] z1 = sum(hs) + half_space_depth zz = np.repeat(np.arange(0, z1 + dz, dz)[np.newaxis, :], 2, axis=0) vps = np.empty_like(zz, dtype=np.float32) vss = np.empty_like(zz, dtype=np.float32) rhos = np.empty_like(zz, dtype=np.float32) qs = np.empty_like(zz, dtype=np.float32) if self._layers: vps[:, 0] = self._layers[-1].vp vss[:, 0] = self._layers[-1].vs rhos[:, 0] = self._layers[-1].rho qs[:, 0] = self._layers[-1].q else: vps[:, 0] = self.vp vss[:, 0] = self.vs rhos[:, 0] = self.rho qs[:, 0] = self.q depth = 0 for layer in reversed(self._layers): vps[(zz > depth) & (zz <= depth + layer.h)] = layer.vp vss[(zz > depth) & (zz <= depth + layer.h)] = layer.vs rhos[(zz > depth) & (zz <= depth + layer.h)] = layer.rho qs[(zz > depth) & (zz <= depth + layer.h)] = layer.q depth += layer.h vps[zz > depth] = self.vp vss[zz > depth] = self.vs rhos[zz > depth] = self.rho qs[zz > depth] = self.q vps_sgy = SegY.from_matrix(vps, sample_interval=int(dz * 1000)) vss_sgy = SegY.from_matrix(vss, sample_interval=int(dz * 1000)) rhos_sgy = SegY.from_matrix(rhos, sample_interval=int(dz * 1000)) qs_sgy = SegY.from_matrix(qs, sample_interval=int(dz * 1000)) for sgy in (vps_sgy, vss_sgy, rhos_sgy, qs_sgy): sgy.g.REC_X = [x0, x1] vps_sgy.save(f'{base_filename}_vp.sgy') vss_sgy.save(f'{base_filename}_vs.sgy') rhos_sgy.save(f'{base_filename}_rho.sgy') qs_sgy.save(f'{base_filename}_q.sgy')
def test_saving_to_ibm_file(tmp_path): """ Test the method for saving SegYs to files as IBM floats. """ sgy_path = tmp_path / 'saved_ibm_segy.sgy' s = SegY.from_matrix(np.ones(shape=(24, 512), dtype=np.float32), sample_interval=1000) s.bfh['job_id'] = 666 s.bfh['sample_format'] = 1 # this should trigger saving as IBM s.g.loc[:, 'FFID'] = 981 s.g.loc[:, 'SOU_X'] = 50 s.g.loc[:, 'SOU_Y'] = 75 s.g.loc[:, 'REC_X'] = (s.g.loc[:, 'CHAN'] - 1) * 2 s.g.loc[:, 'REC_Y'] = -1 s.g.loc[:, 'YEAR'] = 1984 s.g.loc[:, 'HOUR'] = 10 s.g.loc[:, 'MINUTE'] = 51 s.g.loc[:, 'CDP_X'] = (s.g.loc[:, 'REC_X'] + s.g.loc[:, 'SOU_X']) / 2 s.g.loc[:, 'CDP_Y'] = 37 s.save(str(sgy_path)) with sgy_path.open('br') as sgy: raw_tfh = sgy.read(3200) raw_bfh = sgy.read(400) raw_data = sgy.read() # TFH assert raw_tfh.decode('cp500') == ' ' * 3200 # BFH assert struct.unpack('>i', raw_bfh[0:4])[0] == 666 # job id assert struct.unpack('>h', raw_bfh[16:18])[0] == 1000 # sample interval assert struct.unpack('>h', raw_bfh[20:22])[0] == 512 # samples per trace assert struct.unpack('>h', raw_bfh[24:26])[0] == 1 # sample format code for i in range(24): raw_th = raw_data[i * (240 + 512 * 4):(i + 1) * 240 + i * 512 * 4] raw_trace = raw_data[(i + 1) * 240 + i * 512 * 4:(i + 1) * (240 + 512 * 4)] # trace header assert struct.unpack('>i', raw_th[0:4])[0] == i + 1 # TRACENO assert struct.unpack( '>i', raw_th[8:12])[0] == 981 # FFID - original field record number assert struct.unpack( '>h', raw_th[68:70] )[0] == -100 # ELEVSC - scalar to apply to all elevations assert struct.unpack( '>h', raw_th[70:72] )[0] == -100 # COORDSC - scalar to apply to all coordinates assert struct.unpack( '>i', raw_th[72:76])[0] == 50 * 100 # SOU_X, with scalar applied assert struct.unpack( '>i', raw_th[76:80])[0] == 75 * 100 # SOU_Y, with scalar applied assert struct.unpack( '>i', raw_th[80:84])[0] == i * 2 * 100 # REC_X, with scalar applied assert struct.unpack( '>i', raw_th[84:88])[0] == -1 * 100 # REC_Y, with scalar applied assert struct.unpack( '>h', raw_th[114:116])[0] == 512 # NUMSMP - number of samples assert struct.unpack( '>h', raw_th[116:118])[0] == 1000 # DT - sample interval in microseconds assert struct.unpack('>h', raw_th[156:158])[0] == 1984 # YEAR assert struct.unpack('>h', raw_th[160:162])[0] == 10 # HOUR assert struct.unpack('>h', raw_th[162:164])[0] == 51 # MINUTE assert struct.unpack('>i', raw_th[180:184])[0] == ( 50 + i * 2) / 2 * 100 # CDP_X, with scalar applied assert struct.unpack( '>i', raw_th[184:188])[0] == 37 * 100 # CDP_Y, with scalar applied # trace trace = gfunc.unpack_ibm32_series(raw_trace, '>') assert np.alltrue(np.array(trace) == 1)