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 https://github.com/opengeostat/pygslib.

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’)

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

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.

Parameters
----------
ifrom, ito:     1D arrays of floats
From - To  interval
ivar :   1D array of floats/integers
variable,
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.

Return
-------
(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
iprop[:]=0

#for each interval
for l in range (nintrb):

# ignore interval if variable is nan
if np.isnan(ivar[l]):
continue

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

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

# --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
else:
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]
continue

# 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]
continue

clen[i] = np.nansum(iprop)

if clen[i]> minlen:
cacum[i] = np.nansum(ivar*iprop)
cvar[i] = cacum[i]/clen[i]  # wighted average
else:
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
```