Working with Chunks#

Chunks are the only way of accessing the values of a gridded Petrel object such as a GridProperty, SurfaceAttribute etc.

Getting a chunk#

Objects have methods which return different chunks of their values. For instance, the cegalprizm.pythontool.GridProperty class has the two methods cegalprizm.pythontool.GridProperty.column() and cegalprizm.pythontool.GridProperty.layer(), which retrieve a chunk for a particular column and a particular layer respectively.

Seismic objects can only generate column chunks (for efficiency); surface attributes can only generate all chunks (which are effectively the values of the single layer of the surface).

Grid property and seismic objects also allow you to retrieve a list of chunks, optionally specifying the range. This is useful when you want to work on an i-slice of a property for example, or just a set of layers. See the cegalprizm.pythontool.GridProperty.columns() method for details - this is available for layers as well in the case of grid properties.

Retrieving the values of a chunk#

values as_array#

There are two ways of retrieving the values of a chunk. The first and most straightforward is to use the cegalprizm.pythontool.Chunk.as_array() method. This returns a numpy.ndarray of values; if the chunk is of a continuous property they will be of type float. If discrete, they will be of type int.

The array returned will be 1-dimensional if the chunk is a column-chunk or two-dimensional if it’s a layer chunk. However, Python will allow you to iterate over the values without knowing the dimensionality:

# prints all the values in the layer
my_layer = my_prop.layer(5)
vals = my_layer.as_array()
for val in vals:
    print(val)

Enumeration#

As the above example shows, although using as_array() is straightforward and the most performant option, you have to keep track of the indices of the value manually. The cegalprizm.pythontool.Chunk.enumerate() method gives values together with their indices. This example uses the Python technique called tuple-unpacking which allows you to assign multiple values in one go.

# prints all the values in the layer together with their indices
my_layer = my_prop.layer(5)
for (i, j, k, val) in my_layer.enumerate():
    print("[%d %d %d] => %f" % (i, j, k, val))

This will output something like (with made up values)

[0 0 0] => 0.001
[0 0 1] => 0.00121
...
[1 2 3] => 0.0093
[1 2 4] => 0.0033
..

This method is slower than using as_array() but can be much more convenient when developing an algorithm.

Setting the values of a chunk#

When you retrieve the values from a Chunk, you are able to change them without consequence. The Petrel object’s values will not be affected until you call the cegalprizm.pythontool.Chunk.set() method of the chunk.

The set method is designed to be as convenient as possible, at the cost of some performance.

The most performant way of doing this is to pass in a System.Array (or numpy.ndarray in Python 3.6) of values, such as you might have retrieved from another chunk:

## assigns the values of layer k=1 to layers k=2 through 10
layerA = my_prop.layer(1)
layerA_values = layerA.as_array()
for layer in my_prop.layers(krange=[2, 3, 4, 5, 6, 7, 8, 9, 10]):
    layer.set(layerA_values)

As soon as you set the values for each layer, the Petrel object is updated. If the Petrel 3D window is displaying the property, you can see them update in real-time (be warned though that it is much slower when displaying the property in a 3D window as Petrel has to read and update the display for each layer). This can be very useful for debugging.

set also takes a list of values, as long as there are exactly the same number of values in the list as there needs to be in the chunk. For instance, if we wanted to clamp the values in a layer to a minimum of 0.1:

layer = my_prop.layer(1)
layer_values = layer.as_array()
# use a 'list-comprehension' to generate a Python list of values from the array
clamped = [0.1 if val < 0.1 else val for val in layer_values]
# pass the Python list to 'set'
layer.set(clamped)

(It is also valid to pass a list-of-lists, as long as the dimensions of the lists match up).

You can also set all of a chunk’s values to a single number:

# set all of a column to be 0
column = my_prop.column(2, 3)
column.set(0)

Finally, you can pass another Chunk and the values are transferred wholesale, as long as the chunks have the same size, orientation and ‘type’ (i.e., continuous or discrete):

# set layer k=1 to have the same values as a layer in another property
layer = my_prop.layer(1)
layer.set(my_other_prop.layer(10))

Chunk arithmetic#

For chunks of continuous values, (i.e., not discrete properties or surface attributes) you can perform simple arithmetic using scalar values or other Chunk objects, as long as they are of compatible size and orientation. The resultant Chunk object is termed disconnected - it does not have a link to a Petrel object. You can however pass this Chunk to another connected chunk:

average_layer = (my_prop.layer(10) + my_prop.layer(11)) / 2
print(average_layer.disconnected) # outputs 'True'

my_prop.layer(12).set(average_layer)

The arithmetic operations work with Chunks both on the left-hand side or right-hand side:

layerA = my_prop.layer(10) + 2
layerB = 2 + my_prop.layer(10)