GeolOil copyrighted Logo
Home link icon Products Learn Downloads Prices Santa Christmas About GeolOil Contact


Keep me updated:

PRODUCTS


RECIPES COOKBOOK


GeolOil - Logging Scripting (GLS) Reference Manual and API Documentation


The GeolOil GLS is a simple and concise scripting programming language specifically designed to process petrophysics LAS files. It allows easy handling of well log curves without worrying too much about invalid blank, or missed -999.25 values. Some minimum exposure to elementary scripting is advised. Please read the GLS log scripting tutorial introduction before checking this reference material.


GLS Stream Operators

The operators +, -, *, /, are normal binary operators that can take either a scalar or a log curve to the left or right side of the operator. The power operator is ^. For instance, x^y means xy. Operators are evaluated from left to right according to standard operator precedence priority as any programming language. To change the evaluation order, just use enough parentheses.


The character or sigil @ is reserved for curve number references. For example, if deep resistivity is the curve number 13, the assignation Rt = @13 defines the variable Rt as a whole curve object. Measured depth should normally be the first curve on a LAS file, so MD = @1 defines MD as the curve 1. To separate instructions use ;. To start a remark or a comment to be omited use #.


The internal representation for missed, dummy, unknown, blank, invalid, infinite, not a number, or any irregular number is -999.25. GeolOil GLS handles all cases automatically in the stream mode (it compiles and executes for you all necessary depth loops on curves and if conditions), but some manual control is needed on the regular looping mode when using comparisons like ==, <, ≤, >, and ≥.


GLS Stream Functions

A stream function is automatically applied to a whole log curve. It internally takes care of all depth cells, looping, and properly handling of invalid dummy and -999.25 values. A special algebra handles automatically a whole curve without any need to loop inside individual depth values. All functions can be applied to either scalars (a single value), or log curve arguments:


  • abs(x)   Returns the absolute value or argument x

  • Three exponential and logarithmic functions:

  • exp(x)   Returns ex
  • ln(x)   Returns the natural logarithm (base e=2.71828...) of x
  • log10(x)   Returns the common logarithm (base 10) of x

  • Six trigonometric functions:

  • sin(x)   Returns the sine of x, where x is in radians, not degrees.
  • cos(x)   Returns the cosine of x, where x is in radians.
  • tan(x)   Returns the tangent of x, where x is in radians.
  • asin(x)   Returns the inverse function sin-1(x). The returned result is in radians, not degrees.
  • acos(x)   Returns the inverse function cos-1(x). The returned result is in radians.
  • atan(x)   Returns the inverse function tan-1(x). The returned result is in radians.

  • The next five functions are normally used as pre-multipliers to modify results: (See an example in the tutorial log scripting)

  • ind(x,y)   Returns 1 if x < y. Returns 0 if x ≥ y. Returns -999.25 if either x or y are invalids or -999.25
  • ind(x,y,z)   Returns 1 if (x ≤ y) and (y < z). Returns 0 if condition is false. Returns -999.25 for invalid arguments

  • blank(x,y)   Returns -999.25 if x < y. Returns 1 if x ≥ y. Also returns 1 if either x or y are invalids or -999.25
  • blank(x,y,z)   Returns -999.25 if (x ≤ y) and (y < z). Returns 1 if condition is false. Also returns 1 for invalid args
  • blankOutside(x,y,z)   Returns -999.25 if y lies outside the interval: x ≤ y < z. Returns 1 if y lies in the interval, and also 1 for invalid arguments

  • Special functions:

  • isValid(x)   Returns 1 if x is a valid argument, 0 if x is invalid, blank, or missed like -999.25, NaN, infinite, etc.
  • valueOrZero(x)   Returns its original argument x, if x is valid. But returns 0 if x is invalid

  • trim(left,x,right)   Returns x, if left ≤ x < right. Returns left, if x < left, right if x > right. -999.25 for invalid arguments
  • shiftCurve(x,n) Shifts the curve x downwards n depth steps if n < 0, or upwards n depth steps if n < 0: It returns a new shifted curve

  • min(x,y)   Returns the smaller of x and y, or -999.25 if either x or y are unknown, missed, invalid or -999.25.
  • min(x1,x2,x3,...,xn)   Returns the smallest of x1, x2, x3, ..., xn. Returns -999.25 if an argument is invalid, missed, or -999.25.

  • max(x,y)   Returns the larger of x and y, or -999.25 if either x or y are unknown, missed, invalid or -999.25.
  • max(x1,x2,x3,...,xn)   Returns the largest of x1, x2, x3, ..., xn. Returns -999.25 if an argument is invalid, missed, or -999.25.

  • merge(x,y)   Returns x, if x is known. Returns y, if x is missed, invalid, unknown or -999.25 (transparency): merge puts the x curve on top of y
  • merge(x1,x2,x3,...,xn)   Returns a new merged curve from x1, x2, x3, ..., xn. Top priority curve is x1. If x1 is missed, x2 is taken and so on

  • avg(x,y)   Returns the arithmetic average of x and y. Any -999.25 missed value is skipped. Only returns -999.25 if both x and y are missed.
  • avg(x1,x2,x3,...,xn)   Returns the average of x1, x2, x3, ..., xn. Any missed value is skipped. Only returns -999.25 if all values are missed.

  • The merge() and avg() functions combine several curves into one. Let's suppose that a LAS file has two versions of Gamma Ray curves and it is wanted to combine them into a single one (instead of selecting one). If the curve GR1 is more reliable than the curve GR2, we would like to use the curve GR1 when it is available, and use GR2 only when GR1 is missed (-999.25). If both curves have a similar behaviour and reliability, perhaps it would be preferred to average the curves to reduce noise, and yet produce an estimate even if only one curve is available:

            Measured Depth MD :   103.00  103.50  104.00  104.50  105.00   105.50
                          GR1 :    67.83   89.20 -999.25 -999.25   56.90  -999.25
                          GR2 :  -999.25   87.90 -999.25   66.48   49.10    38.61
            ---------------------------------------------------------------------
             merge (GR1, GR2) :    67.83   89.20 -999.25   66.48   56.90    38.61
               avg (GR1, GR2) :    67.83   88.55 -999.25   66.48   53.00    38.61

  • return(x)   The command return(x) exits the script, returning the curve x as the final result. If return is absent in the script, the last evaluated result is returned. Although it can be omited in many cases, it is a good practice to exit the script with a return command.

User Defined Stream Functions

In some cases the built-in collection of GLS functions is not enough for some advanced computations. Custom specific functions can be defined by the user both in stream mode or regular depth looping mode. The defined functions are usually placed at the end of the script. As an example, the following user defined stream function has the same behaviour as the default built-in GLS avg(x,y) function:


        def average (x,y)                               # Sums the valid numbers and divide by the amount of valid ones
        {
        numerator   = valueOrZero(x) + valueOrZero(y)   # The sum of the valid numbers. Zero won't add invalid numbers
        denominator =     isValid(x) +     isValid(y)   # The amount of valid elements, can be 2, 1, or 0
        return (numerator/denominator)                  # No worries if denominator if zero, GLS will handle this
        }
        

GLS Regular Depth Looping Mode

The GLS stream mode normally handles most of the programmer's needs. However, in some cases an advanced user may want full control of the computations depth by depth step on a curve. Such custom scripts are usually unnecessarily verbose, long, complex, and prone to bugs, as the user has to take care of all cases, exceptions, if conditions, and looping.


While algebra with invalid numbers like NaN or -999.25 is handled correctly by GLS, the user must not use if and comparison operators like <, ≤, ==, ≥, or >. The reason for that is that an unknown number like NaN or -999.25 can't be compared against anything by definition. For instance, what is the logic result of the instructions: x=-999.25; if (x<0) {x=x+1;}. Since -999.25 is a missed invalid number, don't expect the result to be true (yielding x=-998.25). Likewise, the if() comparison should not be taken as false either!. How to treat this then?


There are two work-arounds to deal with this problem. The first choice is prefer to script as much as possible in the GLS stream mode that takes those details correctly and automatically for you. The second choice is to explictly skip all invalid comparisons and continue to the next instruction or iteration in a depth loop using the isValid(x) function, or its logic negation !isValid(x)



Looping over all Depths


To build a loop over all depths in a LAS file, the user needs to take care of the followng details:

  1. Define new void curves initialized to blank missed values (-999.25) that can store computation results. Once any curve is already defined, a new curve with -999.25 cell depths is easily assigned using the .blank() instance method on the LasCurve. For instance:
    MD = @1; workingResult = new LasCurve (MD.blank());

  2. Find the LasCurve size. Which is the amount of elements or depths in the curve, for example: nDepths = MD.size();

  3. Get for each LasCurve to be read, its scalar individual value at the target depth index. This is done with the LasCurve instance method .get(i). For instance:
  4.         MD = @1;  phi = @13;  rt = @7;  rw = @10
    
            SW = new LasCurve (MD.blank())  # All cell elements of SW are initialized to missed -999.25
            steps = MD.size();
    
            for (int depthIndex=0; depthIndex < steps;  ++depthIndex)
                {
                por = phi.get (depthIndex)
                res =  rt.get (depthIndex)
                rwt =  rw.get (depthIndex)
                ... more code follows
                }
            
  5. Continue to the next loop iteration if you plan to use comparators <, ≤, ==, ≥, or >, on variables that might have missed -999.25 values which would behave unexpectedly during comparisons. Just use logical or on negated !isValid() functions to skip those possibilities. (If any value is invalid, skip and continue to the next iteration) For instance:

  6.         for (int depthIndex=0; depthIndex < steps;  ++depthIndex)
                {
                por = phi.get (depthIndex)
                res =  rt.get (depthIndex)
                rwt =  rw.get (depthIndex)
    
                if ( (!isValid(por)) || (!isValid(res)) || (!isValid(rwt)) ) {continue;}  # Skips to the next depthIndex iteration
    
                swValue = ( (1/(por^2)) * (rwt/res) ) ^ (1/2)  # Calculates one depth element of the Archie water saturation
    
                ... more code follows
                }
            
  7. Set the collected values of swValue in their corresponding whole SW LasCurve positions using the instance method .set (index, value):
  8.         for (int depthIndex=0; depthIndex < steps;  ++depthIndex)
                {
                por = phi.get (depthIndex)
                res =  rt.get (depthIndex)
                rwt =  rw.get (depthIndex)
    
                if ( (!isValid(por)) || (!isValid(res)) || (!isValid(rwt)) ) {continue;}  # Skips to the next depthIndex iteration
    
                swValue = ( (1/(por^2)) * (rwt/res) ) ^ (1/2)  # Calculates one depth element of the Archie water saturation
    
                if (!isValid(swValue)) {continue;}  # swValue can result in -999.25 if for instance por=0
    
                # Now that swValue can't be -999.25 it is legit to make comparisons, like:
    
                if (swValue < 0.15) {swValue = 0.15;}  # Water saturation must be higher than the irreducible water saturation
                if (swValue > 1.00) {swValue = 1.00;}  # Water saturation must be less or equal than 1.0
    
                SW.set (depthIndex, swValue)
                }
            return (SW)
            

All this large boilerplate code is equivalent (and safer, more intuitive and human readable) to just only three lines of scripting in the Stream Mode. So think twice before considering to write code on the Regular Looping Mode, or other petrophysical software under Python scripts. What about trying in Excel?

        phi = @13;  rt = @7;  rw = @10
        SW = ( (1/(phi^2)) * (rw/rt) ) ^ (1/2)
        SW = trim (0.15, SW, 1.0)
        

Only if you really need to write complicated math code that involves the flow control statements: if, else, for, do, while, continue, and break, stick with the Stream Mode.






Content, and web design © 2012-2019 GeolOil LLC. You are welcome to refer them. Please give us fair credits under Creative Commons License CC-by-ND