Downhole compositing algorithm

Opengeostat is working in an open source python module named PyGSLIB and in this post we are discussing the downhole compositing implementation in pure Python.

PyGSLIB is basically a combination of:

  • gslib fortran code wrapped into python function
  • a cython module for drillhole processing
  • a cython module for block model processing
  • a cython module for computational geometry, based in VTK

The last version of the module is available at

In future posts we will discuss the downhole desurvey and other drillhole algorithms. I upcoming posts series we will discuss the implementation of other sub-modules of PyGSLIB.

Drillhole composite implementation

Drill hole compositing is generally required when the sampling interval is not constant or when composites with length proportional to block sizes are required. There are many types of compositing but downhole composing is the most common and the one implemented here.

The compositing process is simple, it consists in calculating an average weighted by length. This is better illustrated with an example; in the figure below the value of the composite interval 2 is calculated as:

Grade 2 = (grade A * a” + grade B * b’ + Grade C * c’) / (a” + b’ + c’)

Downhole composite parameters
Downhole composite parameters

The implementation for a single drillhole consists of finding, for each composite interval, the samples with overlap, the length overlapped and with this two parameters the weighted average. In addition we are including an accumulation variable defined as

accumulation (1 composite interval) = sum of (grade interval * interval length)

This is useful to calculate the percentage occupied by indicator variables (example lithology) or to calculate accumulated variables (e.g. Au * mineralized length).

There are 6 possible cases to consider regarding the coincidence of the original drillhole intervals and a composite interval:

  1. interval is below composite
  2. interval is over composite
  3. interval is within composite
  4. interval overlap at the top of the composite
  5. composite is within interval
  6. composite overlap at the top of the interval
Interval selection: possible cases
Interval selection: possible cases

If any interval is omitted, or if the value of the variable is non-defined or non-finite, then it is ignored in the calculation.

A minimum length of the composite was introduced as input parameter, if the total length of drillhole intervals used to calculate a composite is less than the minimum length then the composite value is assigned with NaN value. The FROM – TO intervals will be always constant but the length will be reported as the total length of drillhole intervals used to calculate a composite.

The python compositing function

import numpy as np

def composite(ifrom, ito, ivar, cint = 1., minlen=-1.):
    cfrom,cto,clen,cvar,cacum= composite(ifrom, ito, ivar, 
                                        cint = 1, minlen=-1)
    Composite intervals in a single drillhole. The From-To 
    intervals may be sorted. 
    ifrom, ito:     1D arrays of floats 
        From - To  interval
    ivar :   1D array of floats/integers
    cint: Optional, float, default 1.  
        length of the compositing intervals
    minlen: Optional, float, defaul -1.  
        minimum length of the composite, if <=0 then minlen = cint/2.
    (cfrom, cto, clen, cvar, cacum)
    cfrom, cto:  1D arrays of floats
         From, To composited intervals 
    clen, cvar, cacum:  1D arrays of floats     
         total length of intervals composited
         variable composited 
         variable accumulated
    assert ifrom.shape == ito.shape, "Error: ifrom and ito with different shape"
    assert all(ifrom < ito), "Error: ifrom >= ito, wrong or zero length intervals"
    assert all(np.isfinite(ifrom)),"Error: ifrom with not finite elements"
    assert all(np.isfinite(ito)),"Error: ito with not finite elements"
    if minlen<=0:
        minlen= cint/2.

    ncomp = int(ito[-1]/cint + 1)  
    nintrb = len(ifrom)           

    #create the composite arrays
    cfrom = np.arange(0.,ito[-1]+cint,cint) 
    cto = cfrom + cint                       
    clen = np.zeros (cto.shape)             
    cvar = np.zeros (cto.shape)             
    cvar[:] = np.nan
    cacum = np.zeros (cto.shape)            

    iprop =np.zeros(ito.shape)             

    # for each composite 
    for i in range (ncomp): 

        # initialize proportions 

        #for each interval 
        for l in range (nintrb):
            # ignore interval if variable is nan 
            if np.isnan(ivar[l]):

            # case a, below the composite 
            if ifrom[l]>=cto[i]: 

            # case b, over the composite
            if ito[l]<=cfrom[i]: 

            # --these are overlap--

            # case overlap top or contained
            if ito[l]>cfrom[i] and ito[l]<=cto[i]:     

                # case c, interval in composite
                if ifrom[l]>=cfrom[i]: 
                    iprop[l] = ito[l]-ifrom[l]

                # case d, overlap top
                    iprop[l] = ito[l]-cfrom[i]

            # case e, composite in interval
            if ifrom[l]<cfrom[i] and ito[l]>cto[i]: 
                iprop[l] = cto[i]-cfrom[i]

            # case f, overlap bottom
            if ifrom[l]>=cfrom[i] and ifrom[l]<cto[i] and ito[l]>cto[i]:
                iprop[l] = cto[i]-ifrom[l]

        clen[i] = np.nansum(iprop)
        if clen[i]> minlen: 
            cacum[i] = np.nansum(ivar*iprop)
            cvar[i] = cacum[i]/clen[i]  # wighted average
            cvar[i] = np.nan
            cacum[i] = np.nan

    return cfrom, cto, clen, cvar, cacum

Testing the compositing function

We used the following code to test the compositing function.

import numpy as np

# one drillhole intervals
ifrom = np.array([0,2.5,4.5,6.5,8.3,15.5])
ito = np.array([2.5,4.5,6.5,8.3,15.5,18])
ivar = np.array ([1,2,np.nan,4,5,6.])

# calling the function to do downhole compositing
cfrom, cto, clen, cvar, cacum= composite(ifrom, ito, ivar, 
                                      cint = 2, minlen=-2)

# output results with pandas
import pandas as pd

print 'drillhole intervals'
print '--------------------------'
print pd.DataFrame({'ifrom':ifrom, 
                    'ito': ito, 
                    'ivar': ivar})
print '\n'

print 'composited drillhole'
print '--------------------------'
print pd.DataFrame({'cfrom': cfrom, 
                    'cto': cto, 
                    'clen': clen, 
                    'cvar': cvar, 
                    'cacum': cacum}))

This is the output

drillhole intervals
   ifrom   ito  ivar
0    0.0   2.5     1
1    2.5   4.5     2
2    4.5   6.5   NaN
3    6.5   8.3     4
4    8.3  15.5     5
5   15.5  18.0     6

composited drillhole
   cacum  cfrom  clen  cto  cvar
0    2.0      0   2.0    2  1.00
1    3.5      2   2.0    4  1.75
2    NaN      4   0.5    6   NaN
3    6.0      6   1.5    8  4.00
4    9.7      8   2.0   10  4.85
5   10.0     10   2.0   12  5.00
6   10.0     12   2.0   14  5.00
7   10.5     14   2.0   16  5.25
8   12.0     16   2.0   18  6.00
9    NaN     18   0.0   20   NaN
Print Friendly