Surfaces and surface attributes#

This section offers an overview of the different functions and properties available for the Petrel Surface object subcategories.

Surfaces#

Check out the API documentation to view a detailed description of all the functions and properties available for working with surfaces.

[2]:
from cegalprizm.pythontool import PetrelConnection
petrel = PetrelConnection()

The .surface property returns all the surfaces as a dictionary where the keys represent the path of the surface within the Petrel input tree and the values represent the name of each surface:

[3]:
petrel.surfaces
[3]:
Surfaces({'Input/Depth surfaces/Top Etive (Depth 1)': Surface(petrel_name="Top Etive (Depth 1)"), 'Input/Velocity Data/Top Ness [Final]': Surface(petrel_name="Top Ness [Final]"), 'Input/Surfaces (Time)/Top Etive': Surface(petrel_name="Top Etive"), 'Input/Surfaces (Time)/Base Cretaceous': Surface(petrel_name="Base Cretaceous"), 'Input/Isochores (depth)/Ness/Ness-2': Surface(petrel_name="Ness-2"), 'Input/Depth surfaces/Top Tarbert (Depth 1)': Surface(petrel_name="Top Tarbert (Depth 1)"), 'Input/AOI Model': Surface(petrel_name="AOI Model"), 'Input/Depth surfaces/Base Cretaceous (Depth 1)': Surface(petrel_name="Base Cretaceous (Depth 1)"), 'Input/Surfaces (Time)/Seabed': Surface(petrel_name="Seabed"), 'Input/Velocity Data/Top Tarbert [Final]': Surface(petrel_name="Top Tarbert [Final]"), 'Input/Depth surfaces/Top Ness (Depth 1)': Surface(petrel_name="Top Ness (Depth 1)"), 'Input/Surfaces (Time)/Top Ness': Surface(petrel_name="Top Ness"), 'Input/Isochores (depth)/Tarbert/Tarbert-1': Surface(petrel_name="Tarbert-1"), 'Input/Velocity Data/Base Cretaceous [Final]': Surface(petrel_name="Base Cretaceous [Final]"), 'Input/Surfaces (Time)/Top Tarbert': Surface(petrel_name="Top Tarbert"), 'Input/Velocity Data/Top Etive [Final]': Surface(petrel_name="Top Etive [Final]")})

We can iterate over all the surfaces within the dictionary and print out their names:

[4]:
for s in petrel.surfaces:
    print(s.petrel_name)
Top Etive (Depth 1)
Top Ness [Final]
Top Etive
Base Cretaceous
Ness-2
Top Tarbert (Depth 1)
AOI Model
Base Cretaceous (Depth 1)
Seabed
Top Tarbert [Final]
Top Ness (Depth 1)
Top Ness
Tarbert-1
Base Cretaceous [Final]
Top Tarbert
Top Etive [Final]

To select the Top Etive surface, we can create a list of paths for all of the surfaces and then use list slicing to access the surface we want. The argument paths[2], selects the third item from the list you get from paths = list(all_surfaces.keys()):

[50]:
all_surfaces = petrel.surfaces
paths = list(all_surfaces.keys())
Top_Etive = petrel.surfaces[paths[2]]
print(Top_Etive)
Surface(petrel_name="Top Etive")

To determine the size of the surface we can use the .extent property, which returns the number of surface nodes in the i and j directions:

[6]:
extent = Top_Etive.extent
print(extent)
Extent(i=236, j=268, k=1)

The .coords_extent returns the extent of the surface in world-coordinates (Xmin, Xmax, Ymin, Ymax, Zmin, Zmax):

[80]:
coord_extent = Top_Etive.coords_extent
print(coord_extent, '\n')
print(f'x: {coord_extent.x_axis}')
print(f'y: {coord_extent.y_axis}')
print(f'z: {coord_extent.z_axis}')
print()

CoordinatesExtent(x_axis=AxisExtent(min=450800.000000, max=459080.000000), y_axis=AxisExtent(min=6780240.000000, max=6790240.000000), z_axis=AxisExtent(min=-2278.070000, max=-1643.700000))

x: AxisExtent(min=450800.000000, max=459080.000000)
y: AxisExtent(min=6780240.000000, max=6790240.000000)
z: AxisExtent(min=-2278.070000, max=-1643.700000)

The returned results of the .extent and .coords_extent properties can also be visualized in Petrel by accessing the Statistics of the selected surface:

surface_extend.png

Using the .position() function we can retrieve the x,y,z position of the surface node by specifying the i and j-index of the surface node:

[8]:
i = 11
j = 20
pos = Top_Etive.position(i, j)
print(f' {i}, {j}: {pos}')
 11, 20: Point(x=451000.00, y=6781040.00, z=nan)

Similarly, using the .indices() function we can retrieve the indices of the surface node nearest the specified point. Note that the node indices are 0-based, but in the Petrel UI they are 1-based:

[10]:
inds = Top_Etive.indices(pos.x, pos.y)
print(f'{pos}: {inds.i}, {inds.j}')
Point(x=451000.00, y=6781040.00, z=nan): 11, 20

The .parent_collection property will return the selected surface and all its siblings in the Petrel Input Tree:

parentcol.png

[11]:
sibling_surfaces = Top_Etive.parent_collection
for Top_Etive in sibling_surfaces:
    print(f'\t{Top_Etive.extent}\t{Top_Etive.petrel_name}')
        Extent(i=286, j=180, k=1)       Seabed
        Extent(i=238, j=291, k=1)       Base Cretaceous
        Extent(i=237, j=269, k=1)       Top Tarbert
        Extent(i=235, j=269, k=1)       Top Ness
        Extent(i=236, j=268, k=1)       Top Etive

To check the available surface attributes for the selected surface we can use the .surface_attributes property:

[13]:
for atr in Top_Etive.surface_attributes:
    print(atr)
SurfaceAttribute(petrel_name="TWT")
SurfaceAttribute(petrel_name="Depth 1")

Surface attributes#

Check out the API documentation to view a detailed description of all the functions and properties available for working with surface attributes:

The .surface_attributes property returns all the continuous surface attributes as a dictionary where the keys represent the path of the surface attribute within the Petrel input tree and the value represents its name:

[14]:
petrel.surface_attributes
[14]:
SurfaceAttributes({'Input/Surfaces (Time)/Base Cretaceous/Depth 1': SurfaceAttribute(petrel_name="Depth 1"), 'Input/Surfaces (Time)/Top Tarbert/TWT': SurfaceAttribute(petrel_name="TWT"), 'Input/Surfaces (Time)/Top Ness/Depth 1': SurfaceAttribute(petrel_name="Depth 1"), 'Input/Surfaces (Time)/Top Etive/TWT': SurfaceAttribute(petrel_name="TWT"), 'Input/Surfaces (Time)/Top Tarbert/Depth 1': SurfaceAttribute(petrel_name="Depth 1"), 'Input/Surfaces (Time)/Top Etive/Depth 1': SurfaceAttribute(petrel_name="Depth 1"), 'Input/Surfaces (Time)/Top Ness/TWT': SurfaceAttribute(petrel_name="TWT"), 'Input/Surfaces (Time)/Base Cretaceous/TWT': SurfaceAttribute(petrel_name="TWT")})

Similarly, we can retrieve a dictionary of all the discrete surface attributes by using the .surface_discrete_attributes property:

[20]:
petrel.surface_discrete_attributes
[20]:
SurfaceDiscreteAttributes({'Input/Surfaces (Time)/Top Etive/Facies': SurfaceDiscreteAttribute(petrel_name="Facies")})

We can iterate over all the surface attributes and return their names:

[56]:
print('Continuous surface attributes:', '\n')
for sa in petrel.surface_attributes:
    print(sa.petrel_name)
print('\n','Discrete surface attributes:', '\n')
for sa in petrel.surface_discrete_attributes:
    print(sa.petrel_name)
Continous surface attributes

Depth 1
TWT
Depth 1
TWT
Depth 1
Depth 1
TWT
TWT

 Discrete surface attributes

Facies

Let’s select the depth surface attribute which belongs to the Top Etive surface:

[59]:
all_surfaces_atr = petrel.surface_attributes
paths = list(all_surfaces_atr.keys())
Top_Etive_Depth = petrel.surface_attributes[paths[5]]
print(Top_Etive_Depth)
SurfaceAttribute(petrel_name="Depth 1")

We can check if the selected attribute belongs to the Top Etive surface using the .surface property:

[61]:
parent_surf = Top_Etive_Depth.surface
print(parent_surf)
Surface(petrel_name="Top Etive")

To check what other attributes the selected surface has we can use the .surface_attributes property:

[63]:
for attr in parent_surf.surface_attributes:
    print(f'{attr}')
SurfaceAttribute(petrel_name="TWT")
SurfaceAttribute(petrel_name="Depth 1")
SurfaceDiscreteAttribute(petrel_name="Facies")

Petrel represents some undefined values by MAX_INT, and others by NaN. The undef value of the surface attribute can be returned using the .undef_value property:

[45]:
print(Top_Etive_Depth.undef_value)
nan

The .template property returns the Petrel template for the surface as a string. If no template is available, it will return an empty string. Similarly, using the .unit_symbol property, we can access the unit for any object associated with a certain template. In Petrel, you can access this data by opening the global settings for the selected property template.

unitsurf.png

[66]:
print('Template:', Top_Etive_Depth.template)
print('\n')
print('Unit:',Top_Etive_Depth.unit_symbol)
Template: Elevation depth


Unit: m

The .has_same_parent() function checks if two attributes belong to the same parent surface. In the example, below we assign two different surface attributes to the variables attr0 and attr1. The .has_same_parent() checks if the two belong to the Top Etive surface and returns a boolean value:

[72]:
attr0 = petrel.surface_attributes[paths[0]]
attr1 = petrel.surface_attributes[paths[3]]
print(f'{attr1} and {Top_Etive} has same parent: {attr1.has_same_parent(Top_Etive_Depth)}')
print(f'{attr0} and {Top_Etive} has same parent: {attr0.has_same_parent(Top_Etive_Depth)}')

SurfaceAttribute(petrel_name="TWT") and Surface(petrel_name="Top Etive") has same parent: True
SurfaceAttribute(petrel_name="Depth 1") and Surface(petrel_name="Top Etive") has same parent: False

We can create a chunk that contains all the values for the attribute by using the .all() function. To learn more about chunks please view the Working with chunks subchapter of the User Guide.

[73]:
Top_Etive_Depth.all()
[73]:
<cegalprizm.pythontool.chunk.Chunk at 0x1fc8683f040>

Alternatively, using the .chunk() function, we can select only a range of the surface attribute that we want to convert into a chunk:

[77]:
i = (11, 25)
j = (20, 40)
Top_Etive_Depth.chunk(i,j)
[77]:
<cegalprizm.pythontool.chunk.Chunk at 0x1fc7a0d4070>

For the discrete surface attributes, we can return a dictionary of the discrete codes and values using the .discrete_codes property:

SURFACE_DISCRETE.png

[79]:
all_surfaces_atr = petrel.surface_discrete_attributes
paths = list(all_surfaces_atr.keys())
Top_Etive_Facies = petrel.surface_discrete_attributes[paths[0]]

Top_Etive_Facies.discrete_codes
[79]:
{0: 'Clay', 1: 'Sand', 2: 'Silt', 3: 'Fine Silt'}