grail.data.model module

Version: 16.2

Table of Contents

Summary

When building a module object, you define the dimensions and items you wish to work with. To get the actual model slab, you need to call the Model object's slab method. The returned Slab object can be read from and written to (see Examples of Model Access).

In general the sequence of steps are,

  1. Create a Model with your slab dimensions.
  2. Request a handle to the slab
  3. Read/Write data to/from the slab
  4. Save the slab, if you were writing
  5. Free memory
  6. Force a garbage collection

Note

That Steps 5 and 6 are optional, since Python takes care of memory clean up. However these steps are good habit, since a very large slab may need to be freed immediately after use.

Terms and Definitions

During a discussion of Model's there are several terms that need defining.

PCF
Project Control File. This file serves as the central storage location for all your Model files. It knows what items are in what model, as well as the dimensions of your model.
Model File Name
This is the name of the model, as it is known to the PCF.
3DBM
This is a 3-D block model.
GSM
This is a gridded seam model.
Slab

This is a sub-section of an Model. It is inefficient to load an entire model into your computer's memory. Instead, you will load a sub-section that you are immediately interested in, and work on that.

In this way you will always have a slab within memory, but you will be able to read/write from the disk via the Model definition.

Cell
A one-by-one-by-one location within the Model. This is the smallest referenced item.

Memory Issues

To solve a potential memory crisis, and to ensure clean-up prior to Python natural garbage collection, the model also provides the free() method. The Python garbage collection will eventually call free() when no one is using the model, but you can make an earlier (and explicit) call by using the free() method and the gc.collect() function [1].

Undefined Values

If a value within the model is either an alpha item OR missing, the resultant value will be model.UNDEFINED.

Testing for defined values can be done via the isdefined() helper function.

Accessing the Slab's Data

There are two ways of accessing the data within a Slab,

  1. Via the get()/set() methods (0 based indexing).

    These methods are base 0 referenced. This means that if you defined a slab that had the following dimensions,

    Dimension

    Value

    Slab set/get Dimensions

    row min

    1

    0

    row max

    10

    9

    column min

    20

    0

    column max

    35

    15

    level min

    1

    0

    level max

    1

    0

    you would access the internal values of the Slab object via from a zero base reference.

  2. Via the modget()/modset() methods (model based indexing).

    These methods are considerable easier to use, since they are relative to the model indexes. This means if we had the following dimensions,

    Dimension

    Value

    Slab modget()/modset() Dimensions

    row min

    1

    1

    row max

    10

    10

    column min

    20

    20

    column max

    35

    35

    level min

    1

    1

    level max

    1

    1

    you would access the contents of the Slab object with exactly the same values as you used to define the slab.

As a summary, the above two tables would be combined as,

Dimension Value get/set modset/modget
row min 1 0 1
row max 10 9 10
column min 20 0 20
column max 35 15 35
level min 1 0 1
level max 1 0 1

Using Floating Point Indices

Currently you have the ability to use floating point numbers as indices into the Slab object; however, this practice should be discouraged since it will lead to,

  1. Slower Access.

    Indices are assumed to be integers, and are the first type of value tested while determining a location in the slab. Everytime you use a floating point number you are adding additional overhead to the access routine, since it must first attempt to use and integer, then resort to using a floating point number.

  2. Risk of Imprecision.

    It is important to remember that the floating point index determination is done via truncation.

    Using floating points is done by truncation. So if you had a floating point index of say, "3.0", you would be accessing the "3" index. Most of the time this will appear to work, but sometimes, you may have computed an index that is really "2.9999999999", which you would want to be "3", but will really be "2".

It is recommended that you do not use floating point data for determining an index into the Slab object.

However, sometimes you just have to compute an index, and that leaves you with a floating point number. If you want to get faster access however you should do the following,

# Truncate's the floating point to an integer.
i = int(2.0*4)

# Rounds the floating point to an integer.
i = int(round(1.9999999*4))

The one you chose to use is up to you and the problem that you have to solve. If you are confident (and very brave) that you will never get a repeating floating point like "1.9999999", then the first option is a good one for speed. If you want to be safe all the time, then the second option is much better.

Examples of Model Access

We will consider two examples. The first example should help to get your feet wet, and show you the basic concepts associated with model traversal. The second example will give a standard template for efficient model traversal.

Simple Zero Algorithm Example

As an example lets retrieve a sub-section of a slab, and traverse all the items, for each cell in the slab, if the item value is less than some small number, then proceed to zero it out. This example would look something like this,

import gc
from grail.data import model, pcf

NEARLY_ZERO = 0.0001

# define our slab dimensions by reading the PCF.
p = pcf.Pcf("samp10.dat")
lvl0, lvlN = 1, p.nz()
row0, rowN = 1, p.ny()
col0, colN = 1, p.nx()
items = p.itemlist('samp15.dat')

# load our model and get our slab
m = model.Model('samp10.dat', 'samp15.dat',
                 
lvl0, lvlN, row0, rowN, col0, colN, items)

# get a reference to a slab
slab = m.slab()

# run our 'zero out algorithm'
modelrange = model.modelitemrange(items, slab.maxlevel(),
                                  
slab.maxrow(), slab.maxcolumn())
for item, lvl, row, col in modelrange:
   
if 0.0 =< slab.get(item, lvl, row, col) < NEARLY_ZERO:
      
slab.set(item, lvl, row, col,0.0) = 0.0

# commit our changes
m.storeslab()

# pre-release memory (because our model could be huge)
m.free()

Notice that our changes will not be stored back to the model until we make a call to storeslab.

Efficient Model Traversal Example

Because of how a model is stored on disk, the most efficient method for traversing a model is not on a cell by cell basis.

The most efficient means of traversing your model is,

  1. By loading the entire model into memory.

    This avoids obvious disk read/write access and speeds up your model traversal.

    However, this must be compared against using up your computer's entire memory. If the computer is eventually running solely with virtual memory you will in effect get disk access again, and get a slow memory traversal.

  2. By loading a reasonable slab defined by level.

    This provides a comprise between minimizing disk read/writes and using up your entire computer's memory.

Typically any meaningful model will not be able to fit within your computer's memory, so in general you will opt for option two.

As an example of efficient model traversal let us consider traversing a model on a level-by-level basis. Off course, you could load multiple levels per loop, but we shall keep this example simple,

from grail.data import pcf
from grail.data import model

p = pcf.Pcf("samp10.dat")
minlvl, maxlvl = 1, p.nz()
minrow, maxrow = 1, p.ny()
mincol, maxcol = 1, p.nx()
items = p.itemlist("samp15.dat")

for lvl in xrange(minlvl, maxlvl+1):
    
m = model.Model("samp10.dat", "samp15.dat",
                   
lvl, lvl,
                   
minrow, maxrow,
                   
mincol, maxcol,
                   
items)
    
s = m.slab()
    
for col in xrange(mincol, maxcol+1):
        
for row in xrange(minrow, maxrow+1):
            
for item in items:

                
# set/get the data.
                
val = s.modget(item, lvl, row, col)
                
s.modset(item, lvl, row, col, 0.0)

    
m.storeslab() # save away our calc.
    
m.free()

A few remarks on the model traversal example shown above,

  • We use the python built-in xrange [2] to compose our range of for loop values.

    xrange is much more efficient range function, especially when the range values are in the thousands. The xrange function generates its values dynamically during the for loop traversal. As opposed to the range function, which builds up a list of values and then supplies it to he for loop.

  • For loop traversals are done from one to maximum value plus one.

    A range function returns the range [min, max), which in plain English means: "all steps from the minimum value up to but not including the maximum value." This is fine for 0 based traversals, but for a 1 based traversal we need to ensure we go up to the very last value, so to insure that we use [min, max+1), which is equivalent to [min, max].

Classes

There are two classes defined within this Module.

Model
Defines the basic "Model Object."
Slab
The data extracted from the defined model.

Model Class

Model(pcf, modelname, l0, ln, r0, rn, c0, cn, items)

The Model class is created with a Project Control File (PCF) path, Model Filename path, the dimensions, and items you wish to process. The processable space is known as the Slab.

An example would be,

from grail.data import model
m = model.Model("c:\\phase1\\samp10.dat", "samp15.dat",
                
1, 10,
                
1, 10,
                
1, 2,
                
["CU", "MOLY", "ORE"])

You can retrieve a reference to the slab via the Model object's slab() method.

Arguments:
pcf : string
Path to the Project Control File (PCF).
modelname : string
Name of the model within as defined in the PCF.
l0 : integer
First level for you slab, starts with 1.
ln : integer
Last level for you slab.
r0 : integer
First row for your slab, starts with 1.
rn : integer.
Last row for your slab.
c0 : integer
First column for your slab, starts with 1.
cn : integer
Last column for you slab.
items : integer
List of items that you want for you slab.

If you are used to using X, Y and Z then Column, Row, Level maps follows,

Model Dimension Cartesian Dimension
Level Z
Row Y
Column X
endcolumn()
Returns the slab's ending column (offset from 1).
endlevel()
Returns the slab's ending level (offset from 1).
endrow()
Return the slab's ending row (offset from 1).
free()

Releases the model contents from the systems memory.

Warning

Any defined Slab object references made by the slab() method will have invalid internal data the moment you call this method.

In addition, this method is implicitly called when there exists no more references to a Model object, and the Python Garbage Collector [1] collects the internal memory.

hasdict()

Warning

DEPRECATED (as of MineSight® Grail 1.1)

This function will always return 1.

itemlist()
Returns a copy of the items in the slab.
modelname()
Return the model's filename.
pcfpath()
Return the model's PCF path
slab()
Returns the reference to the slab.
startcolumn()
Returns the slab's starting column (offset from 1).
startlevel()
Returns the slab's starting level (offset from 1).
startrow()
Return the slab's starting row (offset from 1).
storeslab()
Commits the contents of the defined slab to file.

Slab Class

Slab

An object that is returned by the Model object's slab() method. This is the actual data that you defined when you created the Model object.

There are two important points to note with the Slab object.

  1. The Slab object data is valid only for the lifetime of a Model object. If the Model object does not exist anymore--either implicitly or explicitly via its free() method)--then the Slab object's contents will become undefinable.
  2. Any values set on the Slab object will not be committed to the model file until you evoke the Model object's storeslab() method.
get(item, level, row, column)

Returns a value for the given slab location (0 based indexing).

Note

That the level, row, and column are all relative to 0 for the Slab.

So for example, if you extracted levels 5 through 10 from the Model, then the get() method would have level 0 equivalent to the model's level 5, and level 4 equivalent to the models level 10.

See also Accessing the Slab's Data.

Arguments:
item : integer or string
Either an index or a string of the value that you want to use.
level : integer
Index of the level you want to access (0 based).
row : integer
Index of the row you want to access (0 based)
column : integer
Index of the column you want to access (0 based)

Returns the value for the item, level, row, column location in the slab.

See also Accessing the Slab's Data and Using Floating Point Indices for a discussion on using the indices.

maxcolumn()
Returns the count of the number of columns in a slab.
maxlevel()
Returns the count of the number of levels in a slab.
maxrow()
Returns the count of the number of rows in a slab.
modget(item, level, row, column)

Same as get() but works with model dimensions (model based indexing).

Instead of a 0-based indexing scheme, using the modset/modget methods uses a model-based indexing scheme. What this means is that if you decide to pull out of the model levels 5 through 10, then you would access those levels using values 5 through 10 (as opposed to 0 through 4 for get/set).

See set()/get() for 0-based indexing, and a note on the item argument.

See also Accessing the Slab's Data.

Arguments:
item : string or integer
Either an index or a string of the value that you want to use.
level : integer
Index of the level you want to access (model based).
row : integer
Index of the row you want to access (model based).
column : integer
Index of the column you want to access (model based)

Returns the value for the item, level, row, column location in the slab.

See also Accessing the Slab's Data and Using Floating Point Indices for a discussion on using the indices.

modset(item, level, row, column, value)

Sets the value for an item (model based indexing).

Generates a warning if the value is being clamped into the item's min/max. See modget() for notes about model based indexing.

Arguments:
item : string or integer
Either an index or a string of the value that you want to use.
level : integer
Index of the level you want to access (model based).
row : integer
Index of the row you want to access (model based).
column : integer
Index of the column you want to access (model based)
value :
Value to insert into the slab.

See also Accessing the Slab's Data and Using Floating Point Indices for a discussion on using the indices.

set(item, level, row, column, value)

Sets the contents of indexed slab location (0 based indexing).

See get() for a note about the 0 based indexing.

Generates a warning if the value is being clamped into the item's min/max. Unless that value is model.UNDEFINED.

Arguments:
item : integer or string
Either an index or a string of the value that you want to use.
level : integer
Index of the level you want to access (0 based).
row : integer
Index of the row you want to access (0 based)
column : integer
Index of the column you want to access (0 based)
value :
Value to insert into the slab.

See also Accessing the Slab's Data and Using Floating Point Indices for a discussion on using the indices.

setwarn(isWarn)
Indicates if you want warnings to occur when you are inserting values that are going to get clamped. By default the Slab object will not warn you if you are inserting values that will ultimately be clamped.

Exception

exception ModelError
General exception for all errors generated within this module.

Functions

isdefined(value)

Indicates if a value is undefined or not.

A value is defined if it not 'close' to the model.UNDEFINED value. The closeness is defined by the ag.isclose() function.

modelitemrange(items, dim1, dim2, dim3)

Generate a set of values useful for a model traversal. The dimensions generated are for use with the Slab object's set()/get() methods.

Warning

See warning for modelrange().

For example, it allows you to,

modelrange = model.modelitemrange(items, nlvls, nrows, ncols)
for item, lvl, row, col in modelrange:
   
if m[item, lvl, row, col] < 0.0:
      
print 'negative item.'
Arguments:
items : list
Items you wish to use in your traversal (i.e. ['TOPO', 'CU']).
dim1 through dim3 : integers
Dimensions you wish to traverse with.

Returns s traversable list of tuples for use in a python for loop.

modelrange(dim1, dim2, dim3)

Generates a set of values for a model traversal. The dimensions generated are for use with the Slab object's set()/get() methods.

Warning

This is primarily for convience, but for large traversal sets the convience gained by this function is loss to the unnecessary memory consumption.

Prefer to use the python builtin xrange() function [2].

In this case the loop shown below can be hand rolled as,

for item in items:
   
for lvl in xrange(nlvls):
      
for row in xrange(nrows):
         
for col in xrange(ncols):
            
if m[item, lvl, row, col] < 0.0:
               
print 'negative item.'

For example, this allows you to do the following,

for item in items:
   
for lvl, row, col in model.modelrange(nlvl, nrow, ncol):
      
if m[item, lvl, row, col] < 0.0:
         
print 'negative item.'
Arguments:
dim1 through dim3 : integers
Dimensions you wish to traverse with.

Returns s traversable list of tuples for use in a python for loop.

Data

UNDEFINED

A value that states a given item in a given model cell is undefined.


[1](1, 2) "gc -- Garbage Collector interface" Python Library Reference. 30 May 2003. 21 May 2004. <http://www.python.org/doc/2.2.3/lib/module-gc.html>
[2](1, 2) "Built-in Functions" Python Library Reference. 30 May 2003. 23 July 2004. <http://www.python.org/doc/2.2.3/lib/built-in-funcs.html>