Tutorial 2: hard data files

[1]:
import numpy as np
import matplotlib.pyplot as plt
import geone
import geone.covModel as gcm
import os
import sys
import pyvista as pv
pv.set_jupyter_backend('static')
import pandas as pd

try:
    import ArchPy
except: # if ArchPy is not installed
    print("ArchPy not installed")
    sys.path.append("../..")
    import ArchPy

Introduction

This notebook present how to load and import hard data files (boreholes) with ArchPy.

For this ArchPy requires three different data files which are text files. A list of borholes, a list of unit data and a list of facies data.

They can have any extensions but the recommended are :

list of boreholes -> .lbh

list of unit data -> .ud

list of facies data -> .fd

For this example, the files are in the 2_data_folder.

We can detail these files :

  • .lbh : text file with five columns listing all the boreholes in the data. It has five columns and default headers are :

    • bh_ID : borehole identifier to know at which borehole a unit/facies data belongs

    • bh_x : x borehole coordinate

    • bh_y : y borehole coordinate

    • bh_z : z borehole coordinate

    • bh_depth : borehole depth

  • .ud : text file with four columns listing all the stratigraphical unit interval data. Default headers are :

    • bh_ID : borehole identifier to know at whcih borehole the unit data belongs

    • Strat : Unit identifier to know at which unit this unit interval data belongs

    • top : top elevation of the interval

    • bot : bot elevation of the interval

  • .fd : text file with four columns listing all the facies interval data. Default headers are :

    • bh_ID : borehole identifier to know at whcih borehole the unit data belongs

    • facies_ID : facies identifier to know at which facies this facies interval data belongs

    • top : top elevation of the interval

    • bot : bot elevation of the interval

[2]:
#path to the files
folder = "2_data_folder"
l_bh_path = pd.read_csv(os.path.join(folder, "IO_exemple.lbh"))

unit_data_path = pd.read_csv(os.path.join(folder, "IO_exemple.ud"))

facies_data_path = pd.read_csv(os.path.join(folder, "IO_exemple.fd"))

First step : import

We can now import the geological database with the function : ArchPy.inputs.load_bh_files

This function takes the dataframe of the 3 inputs files as arguments but also column names if they are differents (e.g. bh_id_col which is by default “bh_ID” or u_top_col which is the name of the column indicating top elevation in .ud file).

Dictionnaries can also be passed (dic_units_names and dic_facies_names) which will change units/facies names in database with associated values in dictionnary. For example, if a dic_units_names = {"Lias" : "Jura",  "Dogger" : "Jura", "Malm" : "Jurassic"} is passed, all unit names in keys will be replaced by “Jurassic”. This can be useful to merge some facies or units if they are similar.

Last parameter is altitude flag which indicates to ArchPy if elevation values in database are in altitude or not (depth if False).

It returns two dataframes : the geological database (db) and the list of boreholes with columns properly renamed to be used in extract_bhs function.

[3]:
#import data
db, l_bhs = ArchPy.inputs.load_bh_files(list_bhs=l_bh_path,
                            units_data=unit_data_path,
                            facies_data=facies_data_path, altitude=True)
db #print database
[3]:
Strat_ID Facies_ID top bot
bh_ID
bh1 B Clay 15.0 10.0
bh1 B Sand 10.0 8.0
bh1 B Clay 8.0 5.0
bh1 A Silt 5.0 2.0
bh1 A Gravel 2.0 -1.0
bh1 A Sand -1.0 -5.0
bh2 C Gravel 15.0 10.0
bh2 B Sand 10.0 8.0
bh2 B Clay 8.0 4.0
bh2 A Sand 4.0 2.0
bh2 A Silt 2.0 -2.0
bh3 C Gravel 15.0 12.0
bh3 B Sand 12.0 11.0
bh3 B Clay 11.0 8.0
bh3 B Sand 8.0 7.5
bh3 B Clay 7.5 7.0
bh3 A Silt 7.0 5.0
bh3 A Sand 5.0 4.0
bh3 A Gravel 4.0 2.0
bh3 A Silt 2.0 0.0

The database has been imported but no project has been defined. We need to create one that respects the Hard data present here.

Second step

Creating the Project with appropriated units/facies.

For simplicity SIS will be used to fill these units.

[4]:
T1 = ArchPy.base.Arch_table(name = "ex2", working_directory="2_data_folder", seed = 10, verbose = 1)
[5]:
nx = 150 #number of cells in x
ny = 150
nz = 50
sx = 0.2 #cell width in x
sy = 0.2
sz = 0.5
ox = 0 # x coordinates of the origin
oy = 0
oz = -10
dimensions = (nx, ny, nz)
spacing = (sx, sy, sz)
origin = (ox, oy, oz)

T1.add_grid(dimensions, spacing, origin) #adding the grid
## Adding Grid ##
## Grid added and is now simulation grid ##
[6]:
P1 = ArchPy.base.Pile("Pile_1")
T1.set_Pile_master(P1)
Pile sets as Pile master

Units

[7]:
##C
#Creation of the top unit C
covmodel_SIS = gcm.CovModel3D(elem = [("spherical", {"w":2, "r": [20,20,10]}),
                                      ("exponential", {"w":1, "r": [30,30,10]})])

dic_facies_c = {"f_method" : "homogenous", #filling method
                 "f_covmodel" : covmodel_SIS, #SIS covmodels
                } #dictionnary for the unit filling

C = ArchPy.base.Unit(name = "C",
                      order = 1,       #order in pile
                      color = "lightgreen",
                      surface=ArchPy.base.Surface(),  # top surface
                      ID = 1,
                      dic_facies=dic_facies_c
                     )


## B
#surface B
covmodel_b = gcm.CovModel2D(elem = [("cubic", {"w":2, "r" : [15,5]})])
dic_surf_b = {"covmodel" : covmodel_b, "int_method" : "grf_ineq"}
Sb = ArchPy.base.Surface(name = "Sb", dic_surf=dic_surf_b)

#dic facies b
dic_facies_b = {"f_method" : "SIS", "f_covmodel" : covmodel_SIS, "probability" : [0.7, 0.3]}

B = ArchPy.base.Unit(name = "B",
                      order = 2,   #order in pile
                      color = "greenyellow", #color
                      surface=Sb, # top surface
                      ID = 2,     #ID
                      dic_facies=dic_facies_b #facies dictionnary
                     )


##A
covmodel_a = gcm.CovModel2D(elem = [("spherical", {"w":1, "r" : [5,5]})])
dic_surf_a = {"covmodel" : covmodel_b, "int_method" : "grf_ineq"}
Sa = ArchPy.base.Surface(name = "Sa", dic_surf=dic_surf_a)

#dic facies a
dic_facies_a = {"f_method" : "SIS", "f_covmodel" : covmodel_SIS}

A = ArchPy.base.Unit(name = "A",
                      order = 3,   #order in pile
                      color = "lightcoral", #color
                      surface=Sa, # top surface
                      ID = 3,     #ID
                      dic_facies=dic_facies_a #facies dictionnary
                     )

#Adding the units to the Pile
P1.add_unit([C, B, A])
Unit C: Surface added for interpolation
Unit B: covmodel for SIS added
Unit B: Surface added for interpolation
Unit A: covmodel for SIS added
Unit A: Surface added for interpolation
Stratigraphic unit C added ✅
Stratigraphic unit B added ✅
Stratigraphic unit A added ✅

Facies

[8]:
Sand = ArchPy.base.Facies(ID = 1, name = "Sand", color = "yellow")
Clay = ArchPy.base.Facies(ID = 2, name = "Clay", color = "royalblue")
Gravel = ArchPy.base.Facies(ID = 3, name = "Gravel", color = "palegreen")
Silt = ArchPy.base.Facies(ID = 4, name = "Silt", color = "goldenrod")


C.add_facies(Gravel)
B.add_facies([Clay, Sand])
A.add_facies([Sand, Gravel, Silt])
Facies Gravel added to unit C ✅
Facies Clay added to unit B ✅
Facies Sand added to unit B ✅
Facies Sand added to unit A ✅
Facies Gravel added to unit A ✅
Facies Silt added to unit A ✅

Extract boreholes from database

We can now extract boreholes objects with ArchPy.inputs.extract_bhs. Note : This function requires the list of boreholes path

[9]:
boreholes = ArchPy.inputs.extract_bhs(df=db, list_bhs=l_bhs, ArchTable=T1)

Borehole class

In ArchPy, the geological data are in “borehole” format. Here we have created boreholes directly from a database but they can also be create manually in python with ArchPy.base.borehole.

A borehole takes multiple arguments :

  • name

  • ID

  • x,y,z : top borehole coordinates

  • depth : depth of the borehole

  • log_strati : unit data which is a list of tuple, each tuple containing a unit object and an elevation (e.g. log_strati = [(D, 10), (C, 6), ...]).

  • log_facies : facies data, identical to log_strati except unit object are facies object

They are added to the project with add_bh.

[10]:
T1.add_bh(boreholes)
Borehole bh1 added
Borehole bh2 added
Borehole bh3 added
[11]:
#We can look at what the boreholes looks with plot_bhs()
p=pv.Plotter()
T1.plot_bhs(plotter=p)
p.show_axes()
p.show()
../_images/notebooks_2_Hard_data_19_0.png

It is now important to process the data in order to get the HD correctly

All this step is automated by calling process_bhs

[12]:
T1.process_bhs()
##### ORDERING UNITS #####
Pile Pile_1: ordering units
Stratigraphic units have been sorted according to order
hierarchical relations set

 ## Computing distributions for Normal Score Transform ##

Processing ended successfully

Check

We can check that Hard Data have been extracted. They are stored inside surface objects directly. Here with B top surface that have 2 equality point and 1 inequality point (lower boundary)

[13]:
unit = B

for ix,iy,iz in zip(unit.surface.x,unit.surface.y,unit.surface.z):
    print("equality point : {} x, {} y, {} z".format(ix,iy,iz))

for ineq in unit.surface.ineq:
    print("inequality point : {} x, {} y, {} vmin, {} vmax".format(ineq[0], ineq[1], ineq[3], ineq[4]))
equality point : 15 x, 25 y, 10.0 z
equality point : 20 x, 5 y, 12.0 z
inequality point : 1 x, 15 y, 14.0 vmin, nan vmax

We can also plot on a top-view the hard data

[14]:
plt.figure(figsize=(12,5))
plt.subplot(1, 2, 1)
T1.plot_unit_data_point(B)
plt.subplot(1, 2, 2)
T1.plot_unit_data_point(B, typ="thk")
../_images/notebooks_2_Hard_data_25_0.png

Simulations

[15]:
T1.compute_surf(1)
########## PILE Pile_1 ##########
Pile Pile_1: ordering units
Stratigraphic units have been sorted according to order

#### COMPUTING SURFACE OF UNIT A
A: time elapsed for computing surface 0.03850197792053223 s

#### COMPUTING SURFACE OF UNIT B
B: time elapsed for computing surface 0.03570961952209473 s

#### COMPUTING SURFACE OF UNIT C
C: time elapsed for computing surface 0.0 s

Time elapsed for getting domains 0.009617328643798828 s
##########################


### 0.10334396362304688: Total time elapsed for computing surfaces ###
[16]:
T1.compute_facies(1)

### Unit C: facies simulation with homogenous method ####
### Unit C - realization 0 ###
Time elapsed 0.0 s

### Unit B: facies simulation with SIS method ####
### Unit B - realization 0 ###
Only one facies covmodels for multiples facies, adapt sill to right proportions
Time elapsed 1.59 s

### Unit A: facies simulation with SIS method ####
### Unit A - realization 0 ###
Only one facies covmodels for multiples facies, adapt sill to right proportions
Time elapsed 3.12 s

### 4.72: Total time elapsed for computing facies ###
[17]:
p = pv.Plotter()

v_ex = 1
T1.plot_units(plotter=p, slicex=(0.5), slicey=(0.5),v_ex=v_ex)
T1.plot_bhs(plotter=p, v_ex=v_ex)
p.show()
../_images/notebooks_2_Hard_data_29_0.png
[18]:
p = pv.Plotter()
T1.plot_facies(plotter=p, slicex=(0.5), slicey=(0.5),v_ex=1)
T1.plot_bhs("facies", plotter=p, v_ex=1)
p.show()
../_images/notebooks_2_Hard_data_30_0.png

This concludes this little tutorial on ArchPy inputs capabilities