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:

- interval is below composite
- interval is over composite
- interval is within composite
- interval overlap at the top of the composite
- composite is within interval
- 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