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)