Table of Contents
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,
- Create a Model with your slab dimensions.
- Request a handle to the slab
- Read/Write data to/from the slab
- Save the slab, if you were writing
- Free memory
- 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.
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.
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].
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.
There are two ways of accessing the data within a Slab,
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.
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
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,
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.
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.
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.
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.
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,
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.
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].
There are two classes defined within this Module.
- Model
- Defines the basic "Model Object."
- Slab
- The data extracted from the defined model.
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.
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
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.
Warning
DEPRECATED (as of MineSight® Grail 1.1)
This function will always return 1.
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.
- 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.
- Any values set on the Slab object will not be committed to the model file until you evoke the Model object's storeslab() method.
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.
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.
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,
Returns s traversable list of tuples for use in a python for loop.
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 example, this allows you to do the following,
Returns s traversable list of tuples for use in a python for loop.
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> |