Classification - AVO attribute#
This notebook shows to a simple AVO classification can be run on seismic data using Investigator.
Introduction#
Classification of data using AVO analysis can prove to be a valuable tool in helping to identify hydrocarbons - see Rutherford & Williams (1989), Castagna & Swan (1997), and many authors since.
On an intercept-gradient plot, most seismic data plots in an elliptical cloud around the origin. By ‘muting’ out this background trend, AVO anomalies can be highlighted. These anomalies may (or may not) indicate the presence of hydrocarbons.
The AVO class is defined based on the section or quadrant in which the sample plots. This is effectively a collection of 2D polygon cut-offs that are mathematically defined.
Dependencies#
This workflow has not additional dependencies. It uses only core Python functionality and CegalPrizm Investigator.
Licensing#
This workflow uses the licensed Investigator features only to display the plots in the notebook. The AVO classification can be performed in Petrel on a local machine with no CegalPrizm Investigator license.
Workflow#
Connect to Investigator#
To connect the notebook to Investigator is it necessary to import the CegalPrizm Investigator package and create a connection.
Note: Remove the parameter use_licensed_features below to run the workflow without a CegalPrizm Investigator license.
[1]:
import cegalprizm.investigator as investigator
from cegalprizm.investigator import InvestigatorConnection
inv_conn = InvestigatorConnection(use_licensed_features=True)
Attach to the seismic investigation#
Attach to the investigation on which AVO classification is to be performed. This investigation should contain a seismic dataset with a minimum of intercept and gradient dimensions.
Optional - Load existing investigation
List the available investigations
[2]:
inv_conn.available_investigations()
[2]:
[InvestigationInfoTuple(id='27b05ff7-4700-46ad-95ec-7c969df29e4f', name='Investigation')]
The seismic investigation can be loaded using the name or id of the investigation
[ ]:
inv = inv_conn.load_investigation("<investigation name>")
Optional - Load investigation from invpy file
To allow this workflow to be run independently an invpy file is available containing pseudo seismic data for demo purposes
[4]:
inv = inv_conn.investigation_from_file("SeismicAVO.invpy")
The continuous and discrete dimensions available in the investigation
[5]:
print(inv.continuous_dimension_names)
print(inv.discrete_dimension_names)
['Gradient', 'Intercept']
['Dataset']
Optional (requires license) - Intercept/Gradient crossplot
An intercept/gradient crossplot of the investigation
Note: To display the plot requires the user to have a CegalPrizm Investigator license and the investigator connection to be created with use_license_features=True
[6]:
from cegalprizm.investigator.views import CrossplotView
view = CrossplotView(inv)
view.set_color_by("Intercept")
view.lock_aspect_ratio(True)
investigator.plot(view)
[6]:

Create the AVO classifier#
Define output classifications
AVO classifies samples into a number of different classes. The following list defines the possible outputs from the AVO classifier.
A DiscreteEntryTuple object is used to define each of the different possible classifications.
[7]:
from cegalprizm.investigator import DiscreteEntryTuple
avo_class = [
DiscreteEntryTuple('I', 'blue'),
DiscreteEntryTuple('II', 'darkgreen'),
DiscreteEntryTuple('III', 'red'),
DiscreteEntryTuple('IV','orange'),
DiscreteEntryTuple('V', 'magenta'),
DiscreteEntryTuple('VI', 'cyan'),
DiscreteEntryTuple('IIp','lightgreen'),
DiscreteEntryTuple('Vp', 'purple')
]
Implement the classify function
The theta angle (in degrees) defines the slope of the diagonal classification lines. It is controlled by the point which the AVO lines crosses the x-axis in the reflectivity vs sin2(theta) plot. If the line crosses at an angle less than the specified angle (default, 30°), then it is a class IIp anomaly, otherwise it is class I.
[8]:
theta_degrees = 30
It is necessary to calculate tan(chi) from the given theta value. It is calculated once for efficiency.
[9]:
import math
chi = math.pow(math.sin(math.radians(theta_degrees)), 2)
tanChi = math.tan(chi)
This is a helper function which returns the index of the classification from the name
[10]:
def get_discrete_index(name):
for index, entry in enumerate(avo_class):
if entry.name == name:
return index
return -1
The classifier is implemented as a function that takes a sample as an input and returns the classification. The classification is determined by evaluating where the intercept/gradient sample is located on the AVO plot. These are effectively 2D cut-off regions defined mathematically.
[11]:
def classify(x):
intercept = x[0]
gradient = x[1]
angle = 0
if gradient == 0:
angle = 0
else:
angle = abs(intercept / gradient)
classIIVertical = False
classIIDef = -10
if (gradient < 0):
if (intercept > 0):
if (tanChi < angle):
return get_discrete_index('I')
else:
return get_discrete_index('IIp')
else:
if (classIIVertical and gradient < 0):
if (intercept > classIIDef):
return get_discrete_index('II')
else:
return get_discrete_index('III')
else:
if (tanChi < angle):
return get_discrete_index('III')
else:
return get_discrete_index('II')
else:
if (intercept > 0):
if (classIIVertical and gradient > 0):
if (intercept > (-1*classIIDef)):
return get_discrete_index('VI')
else:
return get_discrete_index('V')
else:
if (tanChi < angle):
return get_discrete_index('VI')
else:
return get_discrete_index('V')
else:
if (tanChi < angle):
return get_discrete_index('IV')
else:
return get_discrete_index('Vp')
Wraps the classify function in a decorated function that can be called by Investigator
To allow Investigator to call the classify function it is necessary to wrap it in a function with a decorator. The decorator is used by Investigator to ensure that the data is passed to the function using the correct format.
It ia most efficient to wrap in a 2D function but for backwards compatibility a 1D wrapper is also supported.
[12]:
from cegalprizm.investigator import InvestigatorPyFunction2D
@InvestigatorPyFunction2D
def classifier(inputs):
return [classify(v) for v in inputs]
Add the classifier to the investigation#
Define classifier input and outputs
Investigator needs to know what the inputs and outputs of the classifier are. The inputs list defines the order in which the sample data will be supplied. The output tuple defines the valid e=discrete entries that can be returned by the function.
[13]:
from cegalprizm.investigator import ContinuousPropertyTuple, DiscretePropertyTuple
inputs = [ContinuousPropertyTuple("Intercept", " "), ContinuousPropertyTuple("Gradient", " ")]
output = DiscretePropertyTuple("avo", avo_class)
Create the classifier in the investigation
This call creates the new python classifier in the investigation. The new classifier will be run on the investigation data.
[14]:
inv.create_classifier("avo", classifier, inputs, output)
The discrete dimensions available in the investigation now includes ‘avo’
[15]:
print(inv.discrete_dimension_names)
['Dataset', 'avo']
Optional (requires license) - AVO crossplot
A intercept/gradient crossplot of the investigation colored by AVO class.
Note: To display the plot requires the user to have a CegalPrizm Investigator license and the investigator connection to be created with use_license_features=True
[16]:
view = CrossplotView(inv)
view.set_color_by("avo")
view.lock_aspect_ratio(True)
view.show_legend(True)
investigator.plot(view)
[16]:

[ ]: