Scalar Grids

A grid is a data container that holds a value at every point in a three dimensional lattice. The simplest type of value to hold is a floating point value. In OEChem, these are called OEScalarGrids. OEScalarGrids are inherently cubic.

The following fundamental parameters define a grid:

Dimension:The number of grid points per axis
Midpoint:The center of the grid in Cartesian space
Spacing:The length between grid points
../_images/OEGridParameters.png

Fundamental Grid Parameters

Listing 1 demonstrates how to construct a grid with 64 points in each direction, a midpoint at the origin, and a spacing of 0.5 between each point in the grid.

Listing 1: Constructing a scalar grid

using System;
using OpenEye.OEChem;
using OpenEye.OEGrid;

public class ScalarGridCtor
{
    public static int Main(string[] args)
    {
        OEScalarGrid grid = new OEScalarGrid(64, 64, 64, 0.0f, 0.0f, 0.0f, 0.5f);
        return 0;
    }
}

Note

The memory footprint of the grid is determined by the dimensions. It is easy to create grids that are too large for memory. For example, the grid in Listing 1 will consume \(64^3 * sizeof(float) = 1048576 bytes = 1 MB\) of memory for this nominally sized grid.

Minimum and maximum

Every grid can have its maximum and minimum vales reported by GetXMax, GetXMin etc. The image Grid coordinates shows a 2D representation for simplicity. It shows 4 grid points centered at (0,0), with a spacing of 1.0. Each grid point will have spatial coordinates (0.5,0.5), (-0.5,0.5) etc. However each grid point lies at the center of a pixel (or rather a voxel in 3D). Any property that falls into the area (volume) of that grid point is mapped to that grid point. This means that the maximum value of x is the extent of the grid, not just the maximum value of the grid point coordinates. In this case that value is 1.0.

../_images/OEGrid.png

Grid coordinates

Data Access

Grids provide three ways to index data inside the grid. They are ordered here by speed of access, i.e., grid elements perform simple pointer arithmetic where spatial coordinates require floating point computation.

Grid Element:Index into the underlying data array
Grid Indices:Unsigned integers less than the dimension for that axis
Spatial Coordinates:
 Cartesian coordinates of any point inside the grid, the grid will determine the closest grid point

Spatial Coordinates

Assuming grid is already filled with relevant values, Listing 2 demonstrates how to retrieve the grid value closest to the atoms in the molecule mol.

Listing 2: Getting values associated with coordinates

foreach (OEAtomBase atom in mol.GetAtoms())
{
    float[] xyz = new float[3];
    mol.GetCoords(atom, xyz);
    if (grid.IsInGrid(xyz[0], xyz[1], xyz[2]))
    {
        Console.WriteLine("value = {0:0.00000}",
                          grid.GetValue(xyz[0], xyz[1], xyz[2]));
    }
}

Spatial coordinates can also be used to set data on the grid. Listing 3 demonstrates how to assign an atom’s partial charge to the grid point it is closest to.

Listing 3: Setting values associated with coordinates

foreach (OEAtomBase atom in mol.GetAtoms())
{
    float[] xyz = new float[3];
    mol.GetCoords(atom, xyz);
    if (grid.IsInGrid(xyz[0], xyz[1], xyz[2]))
    {
        float charge = (float)atom.GetPartialCharge();
        grid.SetValue(xyz[0], xyz[1], xyz[2], charge);
    }
}

Note

In the preceding two code fragments bounds checking was explicitly performed using the IsInGrid method. Accessing data outside the grid is undefined behavior (usually a segmentation fault). However, IsInGrid can become an expensive operation if performed excessively. One way to avoid this cost is to make sure your grid is big enough to enclose the object being worked on. See OEMakeGridFromCenterAndExtents for an example of constructing a grid that covers the entire molecule to ensure no spatial coordinate access is outside the bounds of the grid.

Grid Indices

Grid indices are faster than spatial coordinates because there is no floating point arithmetic to perform. Grid indices make it easy to iterate over the neighbors of any particular point in the grid. Listing 4 demonstrates iterating over all 27 grid points adjacent to and including the grid point given by the grid indices: ix, iy, iz.

Listing 4: Iterating over neighbor points

float[] xyz = new float[3];
mol.GetCoords(atom, xyz);
uint[] ixyz = new uint[3];
grid.SpatialCoordToGridIdx(xyz[0], xyz[1], xyz[2], ixyz);

// Make sure not to go past grid bounds
uint mini = Math.Max(ixyz[0] - 1, 0);
uint minj = Math.Max(ixyz[1] - 1, 0);
uint mink = Math.Max(ixyz[2] - 1, 0);
uint maxi = Math.Min(ixyz[0] + 1, grid.GetXDim());
uint maxj = Math.Min(ixyz[1] + 1, grid.GetYDim());
uint maxk = Math.Min(ixyz[2] + 1, grid.GetZDim());

for (uint k = mink; k < maxk; k++)
{
    for (uint j = minj; j < maxj; j++)
    {
        for (uint i = mini; i < maxi; i++)
        {
            Console.WriteLine("value = {0:0.00000}", grid.GetValue(i, j, k));
        }
    }
}

Grid Elements

Grid values are actually stored in a large one dimensional block of memory. The fastest way to access all the data is to linearly scan through memory. Listing 5 demonstrates how to square every value in the grid.

Listing 5: Squaring every grid value

for (uint i = 0; i < grid.GetSize(); ++i)
{
    float val = grid.GetValue(i);
    grid.SetValue(i, val * val);
}

Input and Output

The following grid file formats are supported:

file extension(s) definition
.phi GRASP format
.grd OpenEye Binary format
.agd OpenEye ASCII format
.map .ccp .ccp4 CCP4 format
.xplor .xplmap XPLOR format
.mtz MTZ crystallographic reflection format

The ASCII format (.agd) was developed by OpenEye to allow for easy integration with other software. The following is an example of the ASCII output for a grid centered at the origin, 2 points along each axis, a spacing of 0.5, and every value zero.

Title: Example Grid
Mid:     0.000000     0.000000     0.000000
Dim:      2      2      2
Spacing:     0.500000
Values:
0.000000e+00
0.000000e+00
0.000000e+00
0.000000e+00
0.000000e+00
0.000000e+00
0.000000e+00
0.000000e+00

Listing 6 is the code used to write the ASCII format out. It is provided here to leave no doubt as to how to inter-operate with the format.

Listing 6: Writing the ASCII format

using System;
using OpenEye.OEChem;
using OpenEye.OEGrid;

public class ASCIIWriter
{
    public static int Main(string[] args)
    {
        OEScalarGrid grid = new OEScalarGrid(2, 2, 2, 0.0f, 0.0f, 0.0f, 0.5f);
        grid.SetTitle("Simple Grid");

        Console.WriteLine(String.Format("Title: {0}", grid.GetTitle()));

        Console.WriteLine("Mid: {0,12:0.000000} {1,12:0.000000} {2,12:0.000000}",
                          grid.GetXMid(),
                          grid.GetYMid(),
                          grid.GetZMid());

        Console.WriteLine("Dim: {0,6:d} {0,6:d} {0,6:d}",
                          grid.GetXDim(),
                          grid.GetYDim(),
                          grid.GetZDim());

        Console.WriteLine("Spacing: {0,12:0.000000}", grid.GetSpacing());

        Console.WriteLine("Values:");
        for (uint iz = 0; iz < grid.GetZDim(); iz++)
        {
            for (uint iy = 0; iy < grid.GetYDim(); iy++)
            {
                for (uint ix = 0; ix < grid.GetXDim(); ix++)
                {
                    Console.WriteLine("{0:e6}", grid.GetValue(ix, iy, iz));
                }
            }
        }
        return 0;
    }
}

Grid as Generic Data

Grids can also be attached to molecules and then written out to OEBinary (.oeb) files. A visualizer can then read in the molecule and grid without any other means of making the association. Listing 7 demonstrates how to attach a Gaussian grid to the molecule it was created from.

The grid is attached to a molecule using the ‘generic data’ interface provided by the OEBase base class. This allows an arbitrary number of grids (or any type of data) to be attached to molecules. Since all grids also derive from OEBase generic data can be attached to grids as well allowing for arbitrarily complex data hierarchies.

Listing 7: Attaching a grid to a molecule

OEScalarGrid grid = new OEScalarGrid();
OEGrid.OEMakeMolecularGaussianGrid(grid, mol, 0.5f);

OEGrid.OESetGrid(mol, "Gaussian Grid", grid);

OEChem.OEWriteMolecule(ofs, mol);

See also