datagenerator.Geomodels
1import os 2import numpy as np 3from datagenerator.Parameters import Parameters 4from datagenerator.util import next_odd 5from scipy.ndimage import maximum_filter 6 7 8class Geomodel: 9 """ 10 Geomodel 11 -------------------------- 12 The class of the Geomodel object. 13 14 This class contains all the items that make up the Geologic model. 15 16 Parameters 17 ---------- 18 parameters : datagenerator.Parameters 19 Parameter object storing all model parameters. 20 depth_maps : np.ndarray 21 A numpy array containing the depth maps. 22 onlap_horizon_list : list 23 A list of the onlap horizons. 24 facies : np.ndarray 25 The generated facies. 26 27 Returns 28 ------- 29 None 30 """ 31 def __init__( 32 self, 33 parameters: Parameters, 34 depth_maps: np.ndarray, 35 onlap_horizon_list: list, 36 facies: np.ndarray 37 ) -> None: 38 """__init__ 39 40 Initializer for the Geomodel class. 41 42 Parameters 43 ---------- 44 parameters : datagenerator.Parameters 45 Parameter object storing all model parameters. 46 depth_maps : np.ndarray 47 A numpy array containing the depth maps. 48 onlap_horizon_list : list 49 A list of the onlap horizons. 50 facies : np.ndarray 51 The generated facies. 52 """ 53 self.cfg = parameters 54 self.depth_maps = depth_maps 55 self.onlap_horizon_list = onlap_horizon_list 56 self.facies = facies 57 58 # Initialise geomodels 59 cube_shape = ( 60 self.cfg.cube_shape[0], 61 self.cfg.cube_shape[1], 62 self.cfg.cube_shape[2] + self.cfg.pad_samples, 63 ) 64 self.geologic_age = self.cfg.hdf_init("geologic_age_prefault", shape=cube_shape) 65 self.onlap_segments = self.cfg.hdf_init( 66 "onlap_segments_prefault", shape=cube_shape 67 ) 68 self.faulted_lithology = self.cfg.hdf_init( 69 "lithology_prefault", shape=cube_shape 70 ) 71 self.geomodel_ng = self.cfg.hdf_init("net_to_gross_prefault", shape=cube_shape) 72 self.faulted_depth = self.cfg.hdf_init("depth_prefault", shape=cube_shape) 73 self.faulted_depth_randomised = self.cfg.hdf_init( 74 "depth_randomised_prefault", shape=cube_shape 75 ) 76 77 # Channel volumes 78 if self.cfg.include_channels: 79 self.floodplain_shale = None 80 self.channel_fill = None 81 self.shale_channel_drape = None 82 self.levee = None 83 self.crevasse = None 84 self.channel_segments = None 85 86 def build_unfaulted_geomodels(self): 87 """ 88 Build unfaulted geomodels. 89 -------------------------- 90 A method that builds unfaulted geomodels. 91 92 This method does the following: 93 94 * Build geologic age cube from depth horizons 95 * Build onlap segmentation cube 96 * If channels are turned on, use fluvsim fortran code to build: 97 - floodplain shale cube 98 - channel fill cube 99 - shale channel drape 100 - levee cube 101 - crevasse cube 102 103 Parameters 104 ---------- 105 None 106 107 Returns 108 ------- 109 None 110 """ 111 self.geologic_age[:] = self.create_geologic_age_3d_from_infilled_horizons( 112 self.depth_maps 113 ) 114 self.onlap_segments[:] = self.insert_onlap_surfaces() 115 # if self.cfg.include_channels: 116 # floodplain_shale, channel_fill, shale_channel_drape, levee, crevasse = self.build_channel_cubes() 117 # self.floodplain_shale = self.vertical_anti_alias_filter_simple(floodplain_shale) 118 # self.channel_fill = self.vertical_anti_alias_filter_simple(channel_fill) 119 # self.shale_channel_drape = self.vertical_anti_alias_filter_simple(shale_channel_drape) 120 # self.levee = self.vertical_anti_alias_filter_simple(levee) 121 # self.crevasse = self.vertical_anti_alias_filter_simple(crevasse) 122 123 def infilled_cube_shape(self): 124 cube_shape = ( 125 self.cfg.cube_shape[0], 126 self.cfg.cube_shape[1], 127 (self.cfg.pad_samples + self.cfg.cube_shape[2]) * self.cfg.infill_factor, 128 ) 129 return cube_shape 130 131 def build_meshgrid(self): 132 """ 133 Build Meshgrid 134 -------------------------- 135 Creates a meshgrid using the data in the Geomodel. 136 137 Parameters 138 ---------- 139 None 140 141 Returns 142 ------- 143 meshgrid : np.darray 144 A meshgrid of the data in the Geomodel. 145 """ 146 return np.meshgrid( 147 range(self.cfg.cube_shape[0]), 148 range(self.cfg.cube_shape[1]), 149 sparse=False, 150 indexing="ij", 151 ) 152 153 def create_geologic_age_3d_from_infilled_horizons(self, depth_maps, verbose=False): 154 """ 155 Create geologic age 3d model from infilled horizons. 156 -------------------------- 157 Creates cube containing a geologic age model from a horizon stack. 158 159 - depth_maps have units like ft or ms 160 - geologic age has arbitrary units where 'age' is same as horizon index 161 162 Parameters 163 ---------- 164 depth_maps : np.ndarray 165 THe depth maps to use to generate the geologic age model. 166 verbose : bool 167 The level of verbosity in the logs 168 169 Returns 170 ------- 171 returns : age : np.ndarray 172 The geologic age model. 173 """ 174 if self.cfg.verbose: 175 print("\nCreating Geologic Age volume from unfaulted depth maps") 176 cube_shape = self.infilled_cube_shape() 177 # ensure that first depth_map has zeros at all X,Y locations 178 if not np.all(depth_maps[:, :, 0] == 0.0): 179 depth_maps_temp = np.dstack((np.zeros(cube_shape[:2], "float"), depth_maps)) 180 else: 181 depth_maps_temp = depth_maps.copy() 182 183 if verbose: 184 print("\n\n ... inside create_geologic_age_3D_from_infilled_horizons ") 185 print( 186 " ... depth_maps min/mean/max, cube_shape = {} {} {} {}".format( 187 depth_maps[:, :, :-1].min(), 188 depth_maps[:, :, :-1].mean(), 189 depth_maps[:, :, :-1].max(), 190 cube_shape, 191 ) 192 ) 193 194 # create geologic age cube 195 age_range = np.linspace(0.0, float(cube_shape[2] - 1), cube_shape[2]) 196 age = np.zeros(cube_shape, "float") 197 for i in range(cube_shape[0]): 198 for j in range(cube_shape[1]): 199 index_max_geo_age = np.argmax( 200 depth_maps_temp[i, j, :].clip(0.0, float(cube_shape[2] - 1)) 201 ) 202 age[i, j, :] = np.interp( 203 age_range, 204 depth_maps_temp[i, j, : int(index_max_geo_age)], 205 np.arange(index_max_geo_age), 206 ) 207 208 if self.cfg.verbose: 209 print(f" ... age.shape = {age.shape}") 210 print( 211 f" ... age min/mean/max = {age[:].min()}, {age[:].mean():.1f}, {age[:].max()}" 212 ) 213 print( 214 " ... finished create_geologic_age_3D_from_infilled_horizons ...\n" 215 ) 216 217 from datagenerator.util import plot_xsection 218 from datagenerator.util import plot_voxels_not_in_regular_layers 219 220 plot_xsection( 221 age[:], 222 depth_maps, 223 cfg=self.cfg, 224 line_num=int(cube_shape[0] / 2), 225 title=f"relative geologic age interpolated from horizons\nprior to faulting\nInline:" 226 f" {int(cube_shape[0] / 2)}", 227 png_name="QC_plot__unfaulted_relative_geologic_age.png", 228 ) 229 230 # Analyse voxels not in regular layers 231 plot_voxels_not_in_regular_layers( 232 volume=age[:], 233 threshold=0.0, 234 cfg=self.cfg, 235 title="Example Trav through 3D model\nhistogram of raw layer values", 236 png_name="QC_plot__histogram_raw_Layers_1.png", 237 ) 238 239 return self.vertical_anti_alias_filter_simple(age) 240 241 def vertical_anti_alias_filter_simple(self, cube) -> np.ndarray: 242 """ 243 Simple vertical anti alias filter 244 -------------------------- 245 Applies a simple version of the vertical anti-alias filter. 246 247 Parameters 248 ---------- 249 cube : np.ndarray 250 The cube to apply the filter to. 251 Returns 252 ------- 253 filtered_cube : np.ndarray 254 The anti-aliased filtered cube. 255 """ 256 if self.cfg.verbose: 257 print("Applying simple vertical anti-alias filter") 258 filtered_cube = cube[..., :: self.cfg.infill_factor] 259 return filtered_cube 260 261 def vertical_anti_alias_filter(self, cube): 262 """ 263 Vertical anti alias filter 264 -------------------------- 265 Applies a vertical anti-alias filter. 266 267 Parameters 268 ---------- 269 cube : np.ndarray 270 The cube to apply the filter to. 271 Returns 272 ------- 273 filtered_cube : np.ndarray 274 The anti-aliased filtered cube. 275 """ 276 infill_factor_o2_odd = next_odd(int(self.cfg.infill_factor + 1) / 2) 277 max_filt = maximum_filter(cube, size=(1, 1, infill_factor_o2_odd + 2)) 278 filtered_cube = self.vertical_anti_alias_filter_simple(max_filt) 279 return filtered_cube 280 281 def plot_channels(self, channel_segments): 282 """ 283 Plot Channels 284 ------------- 285 286 Plot the channel segments. 287 288 Parameters 289 ---------- 290 channel_segments : np.ndarray 291 An array of the channel segments. 292 293 Returns 294 ------- 295 None 296 """ 297 from datagenerator.util import plot_voxels_not_in_regular_layers 298 from datagenerator.util import find_line_with_most_voxels 299 from datagenerator.util import import_matplotlib 300 301 plt = import_matplotlib() 302 303 # find inline with the most channel voxels 304 inline_index = find_line_with_most_voxels( 305 channel_segments, (1000, 2000), self.cfg 306 ) 307 plt.close(1) 308 plt.figure(1, figsize=(10, 7)) 309 plt.clf() 310 plt.title( 311 "Example Trav through 3D model\nLayers filled with Channels Facies Codes" 312 ) 313 plt.imshow( 314 np.fliplr( 315 np.rot90( 316 channel_segments[ 317 inline_index, 318 :, 319 : self.cfg.cube_shape[2] * self.cfg.infill_factor - 1, 320 ], 321 3, 322 ) 323 ), 324 aspect="auto", 325 cmap="jet", 326 ) 327 plt.colorbar() 328 plt.ylim((self.geologic_age.shape[-1], 0)) 329 for i in range(0, self.depth_maps.shape[-1], 5): 330 plt.plot( 331 range(self.cfg.cube_shape[0]), 332 self.depth_maps[inline_index, :, i], 333 "k-", 334 lw=0.3, 335 ) 336 image_path = os.path.join( 337 self.cfg.work_subfolder, "QC_plot__LayersFilledWith_ChannelsFacies.png" 338 ) 339 plt.savefig(image_path, format="png") 340 plt.close() 341 342 # analyze voxel values not in regular layers 343 title = "Example Trav through 3D model\nhistogram of voxels related to channel facies / before faulting" 344 pname = "QC_plot__Channels__histogram_beforeFaulting.png" 345 plot_voxels_not_in_regular_layers( 346 channel_segments, 200.0, title, pname, self.cfg 347 ) 348 349 title = "Example Trav through 3D model\nhistogram of raw layer values\nShould NOT see channel facies" 350 pname = "QC_plot__histogram_raw_Layers_2.png" 351 plot_voxels_not_in_regular_layers( 352 self.geologic_age, 0.0, title, pname, self.cfg 353 ) 354 355 def insert_onlap_surfaces(self): 356 """ 357 Insert onlap surfaces 358 ------------- 359 360 Insert onlap surfaces into the geomodel. 361 362 Parameters 363 ---------- 364 None 365 366 Returns 367 ------- 368 onlap_segments : np.ndarray 369 An array of the onlap segments. 370 """ 371 if self.cfg.verbose: 372 print( 373 "\n\n ... create 3D (pre-faulting) labels for tilting episodes" 374 "\n ... reminder: tilting events were added at horizons {}\n".format( 375 self.onlap_horizon_list 376 ) 377 ) 378 379 # get work_cube_shape from cfg parameters 380 cube_shape = self.infilled_cube_shape() 381 382 # make sure 383 if self.cfg.verbose: 384 print("\n\n ... inside insertOnlap3Dsurface_prefault ") 385 print( 386 " ... depth_maps min/mean/max, cube_shape = {} {} {} {}".format( 387 self.depth_maps[:].min(), 388 self.depth_maps[:].mean(), 389 self.depth_maps[:].max(), 390 cube_shape, 391 ) 392 ) 393 394 # create 3D cube to hold segmentation results 395 onlap_segments = np.zeros(cube_shape, "float32") 396 397 # create grids with grid indices 398 ii, jj = np.meshgrid( 399 range(cube_shape[0]), range(cube_shape[1]), sparse=False, indexing="ij" 400 ) 401 402 # loop through horizons in 'onlaps_horizon_list' 403 if len(self.onlap_horizon_list) == 0: 404 return self.vertical_anti_alias_filter_simple(onlap_segments) 405 406 sorted_onlaps_horizon_list = self.onlap_horizon_list 407 sorted_onlaps_horizon_list.sort() 408 409 voxel_count = 0 410 for ihorizon in sorted_onlaps_horizon_list: 411 412 # insert onlap label in voxels around horizon 413 shallow_map = self.depth_maps[:, :, ihorizon].copy() 414 shallow_depth_map_integer = (shallow_map + 0.5).astype("int") 415 416 for k in range( 417 -int(self.cfg.infill_factor * 1.5), 418 int(self.cfg.infill_factor * 1.5) + 1, 419 ): 420 421 sublayer_ii = ii 422 sublayer_jj = jj 423 sublayer_depth_map = k + shallow_depth_map_integer 424 425 sublayer_depth_map = np.clip(sublayer_depth_map, 0, cube_shape[2] - 1) 426 sublayer = onlap_segments[sublayer_ii, sublayer_jj, sublayer_depth_map] 427 voxel_count += sublayer.flatten().shape[0] 428 429 del sublayer 430 431 onlap_segments[sublayer_ii, sublayer_jj, sublayer_depth_map] += 1.0 432 if self.cfg.verbose and ihorizon % 1 == 0: 433 print( 434 "\t... k: {}, voxel_count: {}, sublayer_current_depth_map.mean: {:.2f} ".format( 435 k, voxel_count, sublayer_depth_map.mean() 436 ) 437 ) 438 439 # reset invalid values 440 onlap_segments[onlap_segments < 0.0] = 0.0 441 442 non_zero_pixels = onlap_segments[onlap_segments != 0.0].shape[0] 443 pct_non_zero = float(non_zero_pixels) / ( 444 cube_shape[0] * cube_shape[1] * cube_shape[2] 445 ) 446 if self.cfg.verbose: 447 print( 448 "\t...onlap_segments min: {}, mean: {}, max: {}, % non-zero: {} ".format( 449 onlap_segments.min(), 450 onlap_segments.mean(), 451 onlap_segments.max(), 452 pct_non_zero, 453 ) 454 ) 455 456 if self.cfg.verbose: 457 print(" ... finished putting onlap surface in onlap_segments ...\n") 458 459 # Print non zero voxel count details 460 if self.cfg.verbose: 461 voxel_count_non_zero = onlap_segments[onlap_segments != 0].shape[0] 462 print( 463 f"\n ...(pre-faulting) onlap segments created. min: {onlap_segments.min()}," 464 f" mean: {onlap_segments.mean():.5f}, max: {onlap_segments.max()}," 465 f" voxel count: {voxel_count_non_zero}\n" 466 f"\n ...(pre-faulting) onlap segments created.shape = {onlap_segments.shape}" 467 ) 468 469 return self.vertical_anti_alias_filter_simple(onlap_segments) 470 471 def write_cube_to_disk(self, data: np.ndarray, fname: str): 472 """ 473 Writes cube to disk. 474 ------------- 475 Writes a cube to disk. 476 477 Parameters 478 ---------- 479 data : np.ndarray 480 The data to be written to disk. 481 fname : str 482 The name to be influded in the file name. 483 484 Returns 485 ------- 486 None 487 488 It generates a `.npy` file on disk. 489 """ 490 """Write 3D array to npy format.""" 491 fname = os.path.join( 492 self.cfg.work_subfolder, f"{fname}_{self.cfg.date_stamp}.npy" 493 ) 494 np.save(fname, data)
class
Geomodel:
9class Geomodel: 10 """ 11 Geomodel 12 -------------------------- 13 The class of the Geomodel object. 14 15 This class contains all the items that make up the Geologic model. 16 17 Parameters 18 ---------- 19 parameters : datagenerator.Parameters 20 Parameter object storing all model parameters. 21 depth_maps : np.ndarray 22 A numpy array containing the depth maps. 23 onlap_horizon_list : list 24 A list of the onlap horizons. 25 facies : np.ndarray 26 The generated facies. 27 28 Returns 29 ------- 30 None 31 """ 32 def __init__( 33 self, 34 parameters: Parameters, 35 depth_maps: np.ndarray, 36 onlap_horizon_list: list, 37 facies: np.ndarray 38 ) -> None: 39 """__init__ 40 41 Initializer for the Geomodel class. 42 43 Parameters 44 ---------- 45 parameters : datagenerator.Parameters 46 Parameter object storing all model parameters. 47 depth_maps : np.ndarray 48 A numpy array containing the depth maps. 49 onlap_horizon_list : list 50 A list of the onlap horizons. 51 facies : np.ndarray 52 The generated facies. 53 """ 54 self.cfg = parameters 55 self.depth_maps = depth_maps 56 self.onlap_horizon_list = onlap_horizon_list 57 self.facies = facies 58 59 # Initialise geomodels 60 cube_shape = ( 61 self.cfg.cube_shape[0], 62 self.cfg.cube_shape[1], 63 self.cfg.cube_shape[2] + self.cfg.pad_samples, 64 ) 65 self.geologic_age = self.cfg.hdf_init("geologic_age_prefault", shape=cube_shape) 66 self.onlap_segments = self.cfg.hdf_init( 67 "onlap_segments_prefault", shape=cube_shape 68 ) 69 self.faulted_lithology = self.cfg.hdf_init( 70 "lithology_prefault", shape=cube_shape 71 ) 72 self.geomodel_ng = self.cfg.hdf_init("net_to_gross_prefault", shape=cube_shape) 73 self.faulted_depth = self.cfg.hdf_init("depth_prefault", shape=cube_shape) 74 self.faulted_depth_randomised = self.cfg.hdf_init( 75 "depth_randomised_prefault", shape=cube_shape 76 ) 77 78 # Channel volumes 79 if self.cfg.include_channels: 80 self.floodplain_shale = None 81 self.channel_fill = None 82 self.shale_channel_drape = None 83 self.levee = None 84 self.crevasse = None 85 self.channel_segments = None 86 87 def build_unfaulted_geomodels(self): 88 """ 89 Build unfaulted geomodels. 90 -------------------------- 91 A method that builds unfaulted geomodels. 92 93 This method does the following: 94 95 * Build geologic age cube from depth horizons 96 * Build onlap segmentation cube 97 * If channels are turned on, use fluvsim fortran code to build: 98 - floodplain shale cube 99 - channel fill cube 100 - shale channel drape 101 - levee cube 102 - crevasse cube 103 104 Parameters 105 ---------- 106 None 107 108 Returns 109 ------- 110 None 111 """ 112 self.geologic_age[:] = self.create_geologic_age_3d_from_infilled_horizons( 113 self.depth_maps 114 ) 115 self.onlap_segments[:] = self.insert_onlap_surfaces() 116 # if self.cfg.include_channels: 117 # floodplain_shale, channel_fill, shale_channel_drape, levee, crevasse = self.build_channel_cubes() 118 # self.floodplain_shale = self.vertical_anti_alias_filter_simple(floodplain_shale) 119 # self.channel_fill = self.vertical_anti_alias_filter_simple(channel_fill) 120 # self.shale_channel_drape = self.vertical_anti_alias_filter_simple(shale_channel_drape) 121 # self.levee = self.vertical_anti_alias_filter_simple(levee) 122 # self.crevasse = self.vertical_anti_alias_filter_simple(crevasse) 123 124 def infilled_cube_shape(self): 125 cube_shape = ( 126 self.cfg.cube_shape[0], 127 self.cfg.cube_shape[1], 128 (self.cfg.pad_samples + self.cfg.cube_shape[2]) * self.cfg.infill_factor, 129 ) 130 return cube_shape 131 132 def build_meshgrid(self): 133 """ 134 Build Meshgrid 135 -------------------------- 136 Creates a meshgrid using the data in the Geomodel. 137 138 Parameters 139 ---------- 140 None 141 142 Returns 143 ------- 144 meshgrid : np.darray 145 A meshgrid of the data in the Geomodel. 146 """ 147 return np.meshgrid( 148 range(self.cfg.cube_shape[0]), 149 range(self.cfg.cube_shape[1]), 150 sparse=False, 151 indexing="ij", 152 ) 153 154 def create_geologic_age_3d_from_infilled_horizons(self, depth_maps, verbose=False): 155 """ 156 Create geologic age 3d model from infilled horizons. 157 -------------------------- 158 Creates cube containing a geologic age model from a horizon stack. 159 160 - depth_maps have units like ft or ms 161 - geologic age has arbitrary units where 'age' is same as horizon index 162 163 Parameters 164 ---------- 165 depth_maps : np.ndarray 166 THe depth maps to use to generate the geologic age model. 167 verbose : bool 168 The level of verbosity in the logs 169 170 Returns 171 ------- 172 returns : age : np.ndarray 173 The geologic age model. 174 """ 175 if self.cfg.verbose: 176 print("\nCreating Geologic Age volume from unfaulted depth maps") 177 cube_shape = self.infilled_cube_shape() 178 # ensure that first depth_map has zeros at all X,Y locations 179 if not np.all(depth_maps[:, :, 0] == 0.0): 180 depth_maps_temp = np.dstack((np.zeros(cube_shape[:2], "float"), depth_maps)) 181 else: 182 depth_maps_temp = depth_maps.copy() 183 184 if verbose: 185 print("\n\n ... inside create_geologic_age_3D_from_infilled_horizons ") 186 print( 187 " ... depth_maps min/mean/max, cube_shape = {} {} {} {}".format( 188 depth_maps[:, :, :-1].min(), 189 depth_maps[:, :, :-1].mean(), 190 depth_maps[:, :, :-1].max(), 191 cube_shape, 192 ) 193 ) 194 195 # create geologic age cube 196 age_range = np.linspace(0.0, float(cube_shape[2] - 1), cube_shape[2]) 197 age = np.zeros(cube_shape, "float") 198 for i in range(cube_shape[0]): 199 for j in range(cube_shape[1]): 200 index_max_geo_age = np.argmax( 201 depth_maps_temp[i, j, :].clip(0.0, float(cube_shape[2] - 1)) 202 ) 203 age[i, j, :] = np.interp( 204 age_range, 205 depth_maps_temp[i, j, : int(index_max_geo_age)], 206 np.arange(index_max_geo_age), 207 ) 208 209 if self.cfg.verbose: 210 print(f" ... age.shape = {age.shape}") 211 print( 212 f" ... age min/mean/max = {age[:].min()}, {age[:].mean():.1f}, {age[:].max()}" 213 ) 214 print( 215 " ... finished create_geologic_age_3D_from_infilled_horizons ...\n" 216 ) 217 218 from datagenerator.util import plot_xsection 219 from datagenerator.util import plot_voxels_not_in_regular_layers 220 221 plot_xsection( 222 age[:], 223 depth_maps, 224 cfg=self.cfg, 225 line_num=int(cube_shape[0] / 2), 226 title=f"relative geologic age interpolated from horizons\nprior to faulting\nInline:" 227 f" {int(cube_shape[0] / 2)}", 228 png_name="QC_plot__unfaulted_relative_geologic_age.png", 229 ) 230 231 # Analyse voxels not in regular layers 232 plot_voxels_not_in_regular_layers( 233 volume=age[:], 234 threshold=0.0, 235 cfg=self.cfg, 236 title="Example Trav through 3D model\nhistogram of raw layer values", 237 png_name="QC_plot__histogram_raw_Layers_1.png", 238 ) 239 240 return self.vertical_anti_alias_filter_simple(age) 241 242 def vertical_anti_alias_filter_simple(self, cube) -> np.ndarray: 243 """ 244 Simple vertical anti alias filter 245 -------------------------- 246 Applies a simple version of the vertical anti-alias filter. 247 248 Parameters 249 ---------- 250 cube : np.ndarray 251 The cube to apply the filter to. 252 Returns 253 ------- 254 filtered_cube : np.ndarray 255 The anti-aliased filtered cube. 256 """ 257 if self.cfg.verbose: 258 print("Applying simple vertical anti-alias filter") 259 filtered_cube = cube[..., :: self.cfg.infill_factor] 260 return filtered_cube 261 262 def vertical_anti_alias_filter(self, cube): 263 """ 264 Vertical anti alias filter 265 -------------------------- 266 Applies a vertical anti-alias filter. 267 268 Parameters 269 ---------- 270 cube : np.ndarray 271 The cube to apply the filter to. 272 Returns 273 ------- 274 filtered_cube : np.ndarray 275 The anti-aliased filtered cube. 276 """ 277 infill_factor_o2_odd = next_odd(int(self.cfg.infill_factor + 1) / 2) 278 max_filt = maximum_filter(cube, size=(1, 1, infill_factor_o2_odd + 2)) 279 filtered_cube = self.vertical_anti_alias_filter_simple(max_filt) 280 return filtered_cube 281 282 def plot_channels(self, channel_segments): 283 """ 284 Plot Channels 285 ------------- 286 287 Plot the channel segments. 288 289 Parameters 290 ---------- 291 channel_segments : np.ndarray 292 An array of the channel segments. 293 294 Returns 295 ------- 296 None 297 """ 298 from datagenerator.util import plot_voxels_not_in_regular_layers 299 from datagenerator.util import find_line_with_most_voxels 300 from datagenerator.util import import_matplotlib 301 302 plt = import_matplotlib() 303 304 # find inline with the most channel voxels 305 inline_index = find_line_with_most_voxels( 306 channel_segments, (1000, 2000), self.cfg 307 ) 308 plt.close(1) 309 plt.figure(1, figsize=(10, 7)) 310 plt.clf() 311 plt.title( 312 "Example Trav through 3D model\nLayers filled with Channels Facies Codes" 313 ) 314 plt.imshow( 315 np.fliplr( 316 np.rot90( 317 channel_segments[ 318 inline_index, 319 :, 320 : self.cfg.cube_shape[2] * self.cfg.infill_factor - 1, 321 ], 322 3, 323 ) 324 ), 325 aspect="auto", 326 cmap="jet", 327 ) 328 plt.colorbar() 329 plt.ylim((self.geologic_age.shape[-1], 0)) 330 for i in range(0, self.depth_maps.shape[-1], 5): 331 plt.plot( 332 range(self.cfg.cube_shape[0]), 333 self.depth_maps[inline_index, :, i], 334 "k-", 335 lw=0.3, 336 ) 337 image_path = os.path.join( 338 self.cfg.work_subfolder, "QC_plot__LayersFilledWith_ChannelsFacies.png" 339 ) 340 plt.savefig(image_path, format="png") 341 plt.close() 342 343 # analyze voxel values not in regular layers 344 title = "Example Trav through 3D model\nhistogram of voxels related to channel facies / before faulting" 345 pname = "QC_plot__Channels__histogram_beforeFaulting.png" 346 plot_voxels_not_in_regular_layers( 347 channel_segments, 200.0, title, pname, self.cfg 348 ) 349 350 title = "Example Trav through 3D model\nhistogram of raw layer values\nShould NOT see channel facies" 351 pname = "QC_plot__histogram_raw_Layers_2.png" 352 plot_voxels_not_in_regular_layers( 353 self.geologic_age, 0.0, title, pname, self.cfg 354 ) 355 356 def insert_onlap_surfaces(self): 357 """ 358 Insert onlap surfaces 359 ------------- 360 361 Insert onlap surfaces into the geomodel. 362 363 Parameters 364 ---------- 365 None 366 367 Returns 368 ------- 369 onlap_segments : np.ndarray 370 An array of the onlap segments. 371 """ 372 if self.cfg.verbose: 373 print( 374 "\n\n ... create 3D (pre-faulting) labels for tilting episodes" 375 "\n ... reminder: tilting events were added at horizons {}\n".format( 376 self.onlap_horizon_list 377 ) 378 ) 379 380 # get work_cube_shape from cfg parameters 381 cube_shape = self.infilled_cube_shape() 382 383 # make sure 384 if self.cfg.verbose: 385 print("\n\n ... inside insertOnlap3Dsurface_prefault ") 386 print( 387 " ... depth_maps min/mean/max, cube_shape = {} {} {} {}".format( 388 self.depth_maps[:].min(), 389 self.depth_maps[:].mean(), 390 self.depth_maps[:].max(), 391 cube_shape, 392 ) 393 ) 394 395 # create 3D cube to hold segmentation results 396 onlap_segments = np.zeros(cube_shape, "float32") 397 398 # create grids with grid indices 399 ii, jj = np.meshgrid( 400 range(cube_shape[0]), range(cube_shape[1]), sparse=False, indexing="ij" 401 ) 402 403 # loop through horizons in 'onlaps_horizon_list' 404 if len(self.onlap_horizon_list) == 0: 405 return self.vertical_anti_alias_filter_simple(onlap_segments) 406 407 sorted_onlaps_horizon_list = self.onlap_horizon_list 408 sorted_onlaps_horizon_list.sort() 409 410 voxel_count = 0 411 for ihorizon in sorted_onlaps_horizon_list: 412 413 # insert onlap label in voxels around horizon 414 shallow_map = self.depth_maps[:, :, ihorizon].copy() 415 shallow_depth_map_integer = (shallow_map + 0.5).astype("int") 416 417 for k in range( 418 -int(self.cfg.infill_factor * 1.5), 419 int(self.cfg.infill_factor * 1.5) + 1, 420 ): 421 422 sublayer_ii = ii 423 sublayer_jj = jj 424 sublayer_depth_map = k + shallow_depth_map_integer 425 426 sublayer_depth_map = np.clip(sublayer_depth_map, 0, cube_shape[2] - 1) 427 sublayer = onlap_segments[sublayer_ii, sublayer_jj, sublayer_depth_map] 428 voxel_count += sublayer.flatten().shape[0] 429 430 del sublayer 431 432 onlap_segments[sublayer_ii, sublayer_jj, sublayer_depth_map] += 1.0 433 if self.cfg.verbose and ihorizon % 1 == 0: 434 print( 435 "\t... k: {}, voxel_count: {}, sublayer_current_depth_map.mean: {:.2f} ".format( 436 k, voxel_count, sublayer_depth_map.mean() 437 ) 438 ) 439 440 # reset invalid values 441 onlap_segments[onlap_segments < 0.0] = 0.0 442 443 non_zero_pixels = onlap_segments[onlap_segments != 0.0].shape[0] 444 pct_non_zero = float(non_zero_pixels) / ( 445 cube_shape[0] * cube_shape[1] * cube_shape[2] 446 ) 447 if self.cfg.verbose: 448 print( 449 "\t...onlap_segments min: {}, mean: {}, max: {}, % non-zero: {} ".format( 450 onlap_segments.min(), 451 onlap_segments.mean(), 452 onlap_segments.max(), 453 pct_non_zero, 454 ) 455 ) 456 457 if self.cfg.verbose: 458 print(" ... finished putting onlap surface in onlap_segments ...\n") 459 460 # Print non zero voxel count details 461 if self.cfg.verbose: 462 voxel_count_non_zero = onlap_segments[onlap_segments != 0].shape[0] 463 print( 464 f"\n ...(pre-faulting) onlap segments created. min: {onlap_segments.min()}," 465 f" mean: {onlap_segments.mean():.5f}, max: {onlap_segments.max()}," 466 f" voxel count: {voxel_count_non_zero}\n" 467 f"\n ...(pre-faulting) onlap segments created.shape = {onlap_segments.shape}" 468 ) 469 470 return self.vertical_anti_alias_filter_simple(onlap_segments) 471 472 def write_cube_to_disk(self, data: np.ndarray, fname: str): 473 """ 474 Writes cube to disk. 475 ------------- 476 Writes a cube to disk. 477 478 Parameters 479 ---------- 480 data : np.ndarray 481 The data to be written to disk. 482 fname : str 483 The name to be influded in the file name. 484 485 Returns 486 ------- 487 None 488 489 It generates a `.npy` file on disk. 490 """ 491 """Write 3D array to npy format.""" 492 fname = os.path.join( 493 self.cfg.work_subfolder, f"{fname}_{self.cfg.date_stamp}.npy" 494 ) 495 np.save(fname, data)
Geomodel
The class of the Geomodel object.
This class contains all the items that make up the Geologic model.
Parameters
- parameters (datagenerator.Parameters): Parameter object storing all model parameters.
- depth_maps (np.ndarray): A numpy array containing the depth maps.
- onlap_horizon_list (list): A list of the onlap horizons.
- facies (np.ndarray): The generated facies.
Returns
- None
Geomodel( parameters: datagenerator.Parameters.Parameters, depth_maps: numpy.ndarray, onlap_horizon_list: list, facies: numpy.ndarray)
32 def __init__( 33 self, 34 parameters: Parameters, 35 depth_maps: np.ndarray, 36 onlap_horizon_list: list, 37 facies: np.ndarray 38 ) -> None: 39 """__init__ 40 41 Initializer for the Geomodel class. 42 43 Parameters 44 ---------- 45 parameters : datagenerator.Parameters 46 Parameter object storing all model parameters. 47 depth_maps : np.ndarray 48 A numpy array containing the depth maps. 49 onlap_horizon_list : list 50 A list of the onlap horizons. 51 facies : np.ndarray 52 The generated facies. 53 """ 54 self.cfg = parameters 55 self.depth_maps = depth_maps 56 self.onlap_horizon_list = onlap_horizon_list 57 self.facies = facies 58 59 # Initialise geomodels 60 cube_shape = ( 61 self.cfg.cube_shape[0], 62 self.cfg.cube_shape[1], 63 self.cfg.cube_shape[2] + self.cfg.pad_samples, 64 ) 65 self.geologic_age = self.cfg.hdf_init("geologic_age_prefault", shape=cube_shape) 66 self.onlap_segments = self.cfg.hdf_init( 67 "onlap_segments_prefault", shape=cube_shape 68 ) 69 self.faulted_lithology = self.cfg.hdf_init( 70 "lithology_prefault", shape=cube_shape 71 ) 72 self.geomodel_ng = self.cfg.hdf_init("net_to_gross_prefault", shape=cube_shape) 73 self.faulted_depth = self.cfg.hdf_init("depth_prefault", shape=cube_shape) 74 self.faulted_depth_randomised = self.cfg.hdf_init( 75 "depth_randomised_prefault", shape=cube_shape 76 ) 77 78 # Channel volumes 79 if self.cfg.include_channels: 80 self.floodplain_shale = None 81 self.channel_fill = None 82 self.shale_channel_drape = None 83 self.levee = None 84 self.crevasse = None 85 self.channel_segments = None
__init__
Initializer for the Geomodel class.
Parameters
- parameters (datagenerator.Parameters): Parameter object storing all model parameters.
- depth_maps (np.ndarray): A numpy array containing the depth maps.
- onlap_horizon_list (list): A list of the onlap horizons.
- facies (np.ndarray): The generated facies.
def
build_unfaulted_geomodels(self):
87 def build_unfaulted_geomodels(self): 88 """ 89 Build unfaulted geomodels. 90 -------------------------- 91 A method that builds unfaulted geomodels. 92 93 This method does the following: 94 95 * Build geologic age cube from depth horizons 96 * Build onlap segmentation cube 97 * If channels are turned on, use fluvsim fortran code to build: 98 - floodplain shale cube 99 - channel fill cube 100 - shale channel drape 101 - levee cube 102 - crevasse cube 103 104 Parameters 105 ---------- 106 None 107 108 Returns 109 ------- 110 None 111 """ 112 self.geologic_age[:] = self.create_geologic_age_3d_from_infilled_horizons( 113 self.depth_maps 114 ) 115 self.onlap_segments[:] = self.insert_onlap_surfaces() 116 # if self.cfg.include_channels: 117 # floodplain_shale, channel_fill, shale_channel_drape, levee, crevasse = self.build_channel_cubes() 118 # self.floodplain_shale = self.vertical_anti_alias_filter_simple(floodplain_shale) 119 # self.channel_fill = self.vertical_anti_alias_filter_simple(channel_fill) 120 # self.shale_channel_drape = self.vertical_anti_alias_filter_simple(shale_channel_drape) 121 # self.levee = self.vertical_anti_alias_filter_simple(levee) 122 # self.crevasse = self.vertical_anti_alias_filter_simple(crevasse)
Build unfaulted geomodels.
A method that builds unfaulted geomodels.
This method does the following:
- Build geologic age cube from depth horizons
- Build onlap segmentation cube
- If channels are turned on, use fluvsim fortran code to build:
- floodplain shale cube
- channel fill cube
- shale channel drape
- levee cube
- crevasse cube
Parameters
- None
Returns
- None
def
build_meshgrid(self):
132 def build_meshgrid(self): 133 """ 134 Build Meshgrid 135 -------------------------- 136 Creates a meshgrid using the data in the Geomodel. 137 138 Parameters 139 ---------- 140 None 141 142 Returns 143 ------- 144 meshgrid : np.darray 145 A meshgrid of the data in the Geomodel. 146 """ 147 return np.meshgrid( 148 range(self.cfg.cube_shape[0]), 149 range(self.cfg.cube_shape[1]), 150 sparse=False, 151 indexing="ij", 152 )
Build Meshgrid
Creates a meshgrid using the data in the Geomodel.
Parameters
- None
Returns
- meshgrid (np.darray): A meshgrid of the data in the Geomodel.
def
create_geologic_age_3d_from_infilled_horizons(self, depth_maps, verbose=False):
154 def create_geologic_age_3d_from_infilled_horizons(self, depth_maps, verbose=False): 155 """ 156 Create geologic age 3d model from infilled horizons. 157 -------------------------- 158 Creates cube containing a geologic age model from a horizon stack. 159 160 - depth_maps have units like ft or ms 161 - geologic age has arbitrary units where 'age' is same as horizon index 162 163 Parameters 164 ---------- 165 depth_maps : np.ndarray 166 THe depth maps to use to generate the geologic age model. 167 verbose : bool 168 The level of verbosity in the logs 169 170 Returns 171 ------- 172 returns : age : np.ndarray 173 The geologic age model. 174 """ 175 if self.cfg.verbose: 176 print("\nCreating Geologic Age volume from unfaulted depth maps") 177 cube_shape = self.infilled_cube_shape() 178 # ensure that first depth_map has zeros at all X,Y locations 179 if not np.all(depth_maps[:, :, 0] == 0.0): 180 depth_maps_temp = np.dstack((np.zeros(cube_shape[:2], "float"), depth_maps)) 181 else: 182 depth_maps_temp = depth_maps.copy() 183 184 if verbose: 185 print("\n\n ... inside create_geologic_age_3D_from_infilled_horizons ") 186 print( 187 " ... depth_maps min/mean/max, cube_shape = {} {} {} {}".format( 188 depth_maps[:, :, :-1].min(), 189 depth_maps[:, :, :-1].mean(), 190 depth_maps[:, :, :-1].max(), 191 cube_shape, 192 ) 193 ) 194 195 # create geologic age cube 196 age_range = np.linspace(0.0, float(cube_shape[2] - 1), cube_shape[2]) 197 age = np.zeros(cube_shape, "float") 198 for i in range(cube_shape[0]): 199 for j in range(cube_shape[1]): 200 index_max_geo_age = np.argmax( 201 depth_maps_temp[i, j, :].clip(0.0, float(cube_shape[2] - 1)) 202 ) 203 age[i, j, :] = np.interp( 204 age_range, 205 depth_maps_temp[i, j, : int(index_max_geo_age)], 206 np.arange(index_max_geo_age), 207 ) 208 209 if self.cfg.verbose: 210 print(f" ... age.shape = {age.shape}") 211 print( 212 f" ... age min/mean/max = {age[:].min()}, {age[:].mean():.1f}, {age[:].max()}" 213 ) 214 print( 215 " ... finished create_geologic_age_3D_from_infilled_horizons ...\n" 216 ) 217 218 from datagenerator.util import plot_xsection 219 from datagenerator.util import plot_voxels_not_in_regular_layers 220 221 plot_xsection( 222 age[:], 223 depth_maps, 224 cfg=self.cfg, 225 line_num=int(cube_shape[0] / 2), 226 title=f"relative geologic age interpolated from horizons\nprior to faulting\nInline:" 227 f" {int(cube_shape[0] / 2)}", 228 png_name="QC_plot__unfaulted_relative_geologic_age.png", 229 ) 230 231 # Analyse voxels not in regular layers 232 plot_voxels_not_in_regular_layers( 233 volume=age[:], 234 threshold=0.0, 235 cfg=self.cfg, 236 title="Example Trav through 3D model\nhistogram of raw layer values", 237 png_name="QC_plot__histogram_raw_Layers_1.png", 238 ) 239 240 return self.vertical_anti_alias_filter_simple(age)
Create geologic age 3d model from infilled horizons.
Creates cube containing a geologic age model from a horizon stack.
- depth_maps have units like ft or ms
- geologic age has arbitrary units where 'age' is same as horizon index
Parameters
- depth_maps (np.ndarray): THe depth maps to use to generate the geologic age model.
- verbose (bool): The level of verbosity in the logs
Returns
- returns : age (np.ndarray): The geologic age model.
def
vertical_anti_alias_filter_simple(self, cube) -> numpy.ndarray:
242 def vertical_anti_alias_filter_simple(self, cube) -> np.ndarray: 243 """ 244 Simple vertical anti alias filter 245 -------------------------- 246 Applies a simple version of the vertical anti-alias filter. 247 248 Parameters 249 ---------- 250 cube : np.ndarray 251 The cube to apply the filter to. 252 Returns 253 ------- 254 filtered_cube : np.ndarray 255 The anti-aliased filtered cube. 256 """ 257 if self.cfg.verbose: 258 print("Applying simple vertical anti-alias filter") 259 filtered_cube = cube[..., :: self.cfg.infill_factor] 260 return filtered_cube
Simple vertical anti alias filter
Applies a simple version of the vertical anti-alias filter.
Parameters
- cube (np.ndarray): The cube to apply the filter to.
Returns
- filtered_cube (np.ndarray): The anti-aliased filtered cube.
def
vertical_anti_alias_filter(self, cube):
262 def vertical_anti_alias_filter(self, cube): 263 """ 264 Vertical anti alias filter 265 -------------------------- 266 Applies a vertical anti-alias filter. 267 268 Parameters 269 ---------- 270 cube : np.ndarray 271 The cube to apply the filter to. 272 Returns 273 ------- 274 filtered_cube : np.ndarray 275 The anti-aliased filtered cube. 276 """ 277 infill_factor_o2_odd = next_odd(int(self.cfg.infill_factor + 1) / 2) 278 max_filt = maximum_filter(cube, size=(1, 1, infill_factor_o2_odd + 2)) 279 filtered_cube = self.vertical_anti_alias_filter_simple(max_filt) 280 return filtered_cube
Vertical anti alias filter
Applies a vertical anti-alias filter.
Parameters
- cube (np.ndarray): The cube to apply the filter to.
Returns
- filtered_cube (np.ndarray): The anti-aliased filtered cube.
def
plot_channels(self, channel_segments):
282 def plot_channels(self, channel_segments): 283 """ 284 Plot Channels 285 ------------- 286 287 Plot the channel segments. 288 289 Parameters 290 ---------- 291 channel_segments : np.ndarray 292 An array of the channel segments. 293 294 Returns 295 ------- 296 None 297 """ 298 from datagenerator.util import plot_voxels_not_in_regular_layers 299 from datagenerator.util import find_line_with_most_voxels 300 from datagenerator.util import import_matplotlib 301 302 plt = import_matplotlib() 303 304 # find inline with the most channel voxels 305 inline_index = find_line_with_most_voxels( 306 channel_segments, (1000, 2000), self.cfg 307 ) 308 plt.close(1) 309 plt.figure(1, figsize=(10, 7)) 310 plt.clf() 311 plt.title( 312 "Example Trav through 3D model\nLayers filled with Channels Facies Codes" 313 ) 314 plt.imshow( 315 np.fliplr( 316 np.rot90( 317 channel_segments[ 318 inline_index, 319 :, 320 : self.cfg.cube_shape[2] * self.cfg.infill_factor - 1, 321 ], 322 3, 323 ) 324 ), 325 aspect="auto", 326 cmap="jet", 327 ) 328 plt.colorbar() 329 plt.ylim((self.geologic_age.shape[-1], 0)) 330 for i in range(0, self.depth_maps.shape[-1], 5): 331 plt.plot( 332 range(self.cfg.cube_shape[0]), 333 self.depth_maps[inline_index, :, i], 334 "k-", 335 lw=0.3, 336 ) 337 image_path = os.path.join( 338 self.cfg.work_subfolder, "QC_plot__LayersFilledWith_ChannelsFacies.png" 339 ) 340 plt.savefig(image_path, format="png") 341 plt.close() 342 343 # analyze voxel values not in regular layers 344 title = "Example Trav through 3D model\nhistogram of voxels related to channel facies / before faulting" 345 pname = "QC_plot__Channels__histogram_beforeFaulting.png" 346 plot_voxels_not_in_regular_layers( 347 channel_segments, 200.0, title, pname, self.cfg 348 ) 349 350 title = "Example Trav through 3D model\nhistogram of raw layer values\nShould NOT see channel facies" 351 pname = "QC_plot__histogram_raw_Layers_2.png" 352 plot_voxels_not_in_regular_layers( 353 self.geologic_age, 0.0, title, pname, self.cfg 354 )
Plot Channels
Plot the channel segments.
Parameters
- channel_segments (np.ndarray): An array of the channel segments.
Returns
- None
def
insert_onlap_surfaces(self):
356 def insert_onlap_surfaces(self): 357 """ 358 Insert onlap surfaces 359 ------------- 360 361 Insert onlap surfaces into the geomodel. 362 363 Parameters 364 ---------- 365 None 366 367 Returns 368 ------- 369 onlap_segments : np.ndarray 370 An array of the onlap segments. 371 """ 372 if self.cfg.verbose: 373 print( 374 "\n\n ... create 3D (pre-faulting) labels for tilting episodes" 375 "\n ... reminder: tilting events were added at horizons {}\n".format( 376 self.onlap_horizon_list 377 ) 378 ) 379 380 # get work_cube_shape from cfg parameters 381 cube_shape = self.infilled_cube_shape() 382 383 # make sure 384 if self.cfg.verbose: 385 print("\n\n ... inside insertOnlap3Dsurface_prefault ") 386 print( 387 " ... depth_maps min/mean/max, cube_shape = {} {} {} {}".format( 388 self.depth_maps[:].min(), 389 self.depth_maps[:].mean(), 390 self.depth_maps[:].max(), 391 cube_shape, 392 ) 393 ) 394 395 # create 3D cube to hold segmentation results 396 onlap_segments = np.zeros(cube_shape, "float32") 397 398 # create grids with grid indices 399 ii, jj = np.meshgrid( 400 range(cube_shape[0]), range(cube_shape[1]), sparse=False, indexing="ij" 401 ) 402 403 # loop through horizons in 'onlaps_horizon_list' 404 if len(self.onlap_horizon_list) == 0: 405 return self.vertical_anti_alias_filter_simple(onlap_segments) 406 407 sorted_onlaps_horizon_list = self.onlap_horizon_list 408 sorted_onlaps_horizon_list.sort() 409 410 voxel_count = 0 411 for ihorizon in sorted_onlaps_horizon_list: 412 413 # insert onlap label in voxels around horizon 414 shallow_map = self.depth_maps[:, :, ihorizon].copy() 415 shallow_depth_map_integer = (shallow_map + 0.5).astype("int") 416 417 for k in range( 418 -int(self.cfg.infill_factor * 1.5), 419 int(self.cfg.infill_factor * 1.5) + 1, 420 ): 421 422 sublayer_ii = ii 423 sublayer_jj = jj 424 sublayer_depth_map = k + shallow_depth_map_integer 425 426 sublayer_depth_map = np.clip(sublayer_depth_map, 0, cube_shape[2] - 1) 427 sublayer = onlap_segments[sublayer_ii, sublayer_jj, sublayer_depth_map] 428 voxel_count += sublayer.flatten().shape[0] 429 430 del sublayer 431 432 onlap_segments[sublayer_ii, sublayer_jj, sublayer_depth_map] += 1.0 433 if self.cfg.verbose and ihorizon % 1 == 0: 434 print( 435 "\t... k: {}, voxel_count: {}, sublayer_current_depth_map.mean: {:.2f} ".format( 436 k, voxel_count, sublayer_depth_map.mean() 437 ) 438 ) 439 440 # reset invalid values 441 onlap_segments[onlap_segments < 0.0] = 0.0 442 443 non_zero_pixels = onlap_segments[onlap_segments != 0.0].shape[0] 444 pct_non_zero = float(non_zero_pixels) / ( 445 cube_shape[0] * cube_shape[1] * cube_shape[2] 446 ) 447 if self.cfg.verbose: 448 print( 449 "\t...onlap_segments min: {}, mean: {}, max: {}, % non-zero: {} ".format( 450 onlap_segments.min(), 451 onlap_segments.mean(), 452 onlap_segments.max(), 453 pct_non_zero, 454 ) 455 ) 456 457 if self.cfg.verbose: 458 print(" ... finished putting onlap surface in onlap_segments ...\n") 459 460 # Print non zero voxel count details 461 if self.cfg.verbose: 462 voxel_count_non_zero = onlap_segments[onlap_segments != 0].shape[0] 463 print( 464 f"\n ...(pre-faulting) onlap segments created. min: {onlap_segments.min()}," 465 f" mean: {onlap_segments.mean():.5f}, max: {onlap_segments.max()}," 466 f" voxel count: {voxel_count_non_zero}\n" 467 f"\n ...(pre-faulting) onlap segments created.shape = {onlap_segments.shape}" 468 ) 469 470 return self.vertical_anti_alias_filter_simple(onlap_segments)
Insert onlap surfaces
Insert onlap surfaces into the geomodel.
Parameters
- None
Returns
- onlap_segments (np.ndarray): An array of the onlap segments.
def
write_cube_to_disk(self, data: numpy.ndarray, fname: str):
472 def write_cube_to_disk(self, data: np.ndarray, fname: str): 473 """ 474 Writes cube to disk. 475 ------------- 476 Writes a cube to disk. 477 478 Parameters 479 ---------- 480 data : np.ndarray 481 The data to be written to disk. 482 fname : str 483 The name to be influded in the file name. 484 485 Returns 486 ------- 487 None 488 489 It generates a `.npy` file on disk. 490 """ 491 """Write 3D array to npy format.""" 492 fname = os.path.join( 493 self.cfg.work_subfolder, f"{fname}_{self.cfg.date_stamp}.npy" 494 ) 495 np.save(fname, data)
Writes cube to disk.
Writes a cube to disk.
Parameters
- data (np.ndarray): The data to be written to disk.
- fname (str): The name to be influded in the file name.
Returns
- None
- It generates a
.npy
file on disk.