=========================== ``grail.data.model`` module =========================== .. include:: ../../version.h .. contents:: Table of Contents :backlinks: top ------- 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 :c:`Model` object's :f:`slab` method. The returned :c:`Slab` object can be read from and written to (see `Examples of Model Access`_). In general the sequence of steps are, 1. Create a :c:`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 :c:`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 :f:`free()` method. The Python garbage collection will eventually call :f:`free()` when no one is using the model, but you can make an earlier (and explicit) call by using the :f:`free()` method and the :f:`gc.collect()` function [#python-lib-gc]_. ---------------- Undefined Values ---------------- If a value within the model is either an alpha item OR missing, the resultant value will be :d:`model.UNDEFINED`. Testing for defined values can be done via the :d:`isdefined()` helper function. ------------------------- Accessing the Slab's Data ------------------------- There are two ways of accessing the data within a :c:`Slab`, 1. Via the :f:`get()`/:f:`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 :c:`Slab` object via from a zero base reference. 2. Via the :f:`modget()`/:f:`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 :f:`modget()`/:f:`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 :c:`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 :f:`get`/:f:`set` :f:`modset`/:f:`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 ---------------------------- .. Based on the notes from Bug 8381, using floating point integers .. to access the slab's contents. Currently you have the ability to use floating point numbers as indices into the :c:`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 :c:`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, .. Python:: # 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, .. Python:: 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 :f:`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, .. Python:: 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 :f:`xrange` [#python-lib-builtin-funcs]_ to compose our range of for loop values. :f:`xrange` is much more efficient range function, especially when the range values are in the thousands. The :f:`xrange` function generates its values dynamically during the for loop traversal. As opposed to the :f:`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 ----------- :dc:`Model(pcf, modelname, l0, ln, r0, rn, c0, cn, items)` The :c:`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 :c:`Slab`. An example would be, .. Python:: 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 :c:`Model` object's :f:`slab()` method. Arguments: :a:`pcf` : string Path to the Project Control File (PCF). :a:`modelname` : string Name of the model within as defined in the PCF. :a:`l0` : integer First level for you slab, starts with 1. :a:`ln` : integer Last level for you slab. :a:`r0` : integer First row for your slab, starts with 1. :a:`rn` : integer. Last row for your slab. :a:`c0` : integer First column for your slab, starts with 1. :a:`cn` : integer Last column for you slab. :a:`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 =============== =================== :df:`endcolumn()` Returns the slab's ending column (offset from 1). :df:`endlevel()` Returns the slab's ending level (offset from 1). :df:`endrow()` Return the slab's ending row (offset from 1). :df:`free()` Releases the model contents from the systems memory. .. Warning:: Any defined :c:`Slab` object references made by the :f:`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 :c:`Model` object, and the Python Garbage Collector [#python-lib-gc]_ collects the internal memory. :df:`hasdict()` .. Warning:: DEPRECATED (as of MineSightŪ Grail 1.1) This function will always return 1. :df:`itemlist()` Returns a copy of the items in the slab. :df:`modelname()` Return the model's filename. :df:`pcfpath()` Return the model's PCF path :df:`slab()` Returns the reference to the slab. :df:`startcolumn()` Returns the slab's starting column (offset from 1). :df:`startlevel()` Returns the slab's starting level (offset from 1). :df:`startrow()` Return the slab's starting row (offset from 1). :df:`storeslab()` Commits the contents of the defined slab to file. Slab Class ---------- :dc:`Slab` An object that is returned by the :c:`Model` object's :f:`slab()` method. This is the actual data that you defined when you created the :c:`Model` object. There are two important points to note with the :c:`Slab` object. 1. The :c:`Slab` object data is valid only for the lifetime of a :c:`Model` object. If the :c:`Model` object does not exist anymore--either implicitly or explicitly via its :f:`free()` method)--then the :c:`Slab` object's contents will become undefinable. 2. Any values set on the :c:`Slab` object will not be committed to the model file until you evoke the :c:`Model` object's :f:`storeslab()` method. :df:`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 :c:`Slab`. So for example, if you extracted levels 5 through 10 from the Model, then the :f:`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: :a:`item` : integer or string Either an index or a string of the value that you want to use. :a:`level` : integer Index of the level you want to access (0 based). :a:`row` : integer Index of the row you want to access (0 based) :a:`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. :df:`maxcolumn()` Returns the count of the number of columns in a slab. :df:`maxlevel()` Returns the count of the number of levels in a slab. :df:`maxrow()` Returns the count of the number of rows in a slab. :df:`modget(item, level, row, column)` Same as :f:`get()` but works with model dimensions (model based indexing). Instead of a 0-based indexing scheme, using the :f:`modset`/:f:`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 :f:`set()`/:f:`get()` for 0-based indexing, and a note on the item argument. See also `Accessing the Slab's Data`_. Arguments: :a:`item` : string or integer Either an index or a string of the value that you want to use. :a:`level` : integer Index of the level you want to access (model based). :a:`row` : integer Index of the row you want to access (model based). :a:`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. :df:`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 :df:`modget()` for notes about model based indexing. Arguments: :a:`item` : string or integer Either an index or a string of the value that you want to use. :a:`level` : integer Index of the level you want to access (model based). :a:`row` : integer Index of the row you want to access (model based). :a:`column` : integer Index of the column you want to access (model based) :a:`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. :df:`set(item, level, row, column, value)` Sets the contents of indexed slab location (0 based indexing). See :df:`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 :d:`model.UNDEFINED`. Arguments: :a:`item` : integer or string Either an index or a string of the value that you want to use. :a:`level` : integer Index of the level you want to access (0 based). :a:`row` : integer Index of the row you want to access (0 based) :a:`column` : integer Index of the column you want to access (0 based) :a:`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. :df:`setwarn(isWarn)` Indicates if you want warnings to occur when you are inserting values that are going to get clamped. By default the :c:`Slab` object will not warn you if you are inserting values that will ultimately be clamped. --------- Exception --------- exception :de:`ModelError` General exception for all errors generated within this module. --------- Functions --------- :df:`isdefined(value)` Indicates if a value is undefined or not. A value is defined if it not 'close' to the :d:`model.UNDEFINED` value. The closeness is defined by the :f:`ag.isclose()` function. :df:`modelitemrange(items, dim1, dim2, dim3)` Generate a set of values useful for a model traversal. The dimensions generated are for use with the :c:`Slab` object's :f:`set()`/:f:`get()` methods. .. Warning:: See warning for :f:`modelrange()`. For example, it allows you to, .. Python:: 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: :a:`items` : list Items you wish to use in your traversal (i.e. ['TOPO', 'CU']). :a:`dim1` through :a:`dim3` : integers Dimensions you wish to traverse with. Returns s traversable list of tuples for use in a python for loop. :df:`modelrange(dim1, dim2, dim3)` Generates a set of values for a model traversal. The dimensions generated are for use with the :c:`Slab` object's :f:`set()`/:f:`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 :f:`xrange()` function [#python-lib-builtin-funcs]_. In this case the loop shown below can be hand rolled as, .. Python:: 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, .. Python:: 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: :a:`dim1` through :a:`dim3` : integers Dimensions you wish to traverse with. Returns s traversable list of tuples for use in a python for loop. ---- Data ---- :dd:`UNDEFINED` A value that states a given item in a given model cell is undefined. ----- .. [#python-lib-gc] "gc -- Garbage Collector interface" *Python Library Reference*. 30 May 2003. 21 May 2004. .. [#python-lib-builtin-funcs] "Built-in Functions" *Python Library Reference*. 30 May 2003. 23 July 2004.