class Chem::Spatial::Grid

Overview

TODO add support for non-cubic grids (use lattice instead of bounds?) - i to coords: origin.x + (i / nx) * lattice.a - coords to i: ?

TODO implement functionality from vmd's volmap

Included Modules

Defined in:

chem/io/reader.cr
chem/io/writer.cr
chem/spatial/grid.cr

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from module Indexable(Float64)

index!(of object, offset : Int = 0) : Int
index!(offset : Int = 0, &block : T -> Bool) : Int
index!

Constructor Detail

def self.[](ni : Int, nj : Int, nk : Int) : self #

[View source]
def self.atom_distance(structure : Structure, dim : Dimensions, bounds : Bounds | Nil = nil) : self #

[View source]
def self.atom_distance_like(other : self | Info, structure : Structure) : self #

Returns a grid filled with the distances to the nearest atom. It will have the same bounds and points as other.

structure = Structure.read "path/to/file"
info = Grid::Info.new Bounds[1.5, 2.135, 6.12], {10, 10, 10}
grid = Grid.atom_distance structure, info.dim, info.bounds
Grid.atom_distance_like(info, structure) == grid # => true

[View source]
def self.build(dim : Dimensions, bounds : Bounds, &block : Pointer(Float64), Int32 -> ) : self #

[View source]
def self.build(info : Info, & : Pointer(Float64), Int32 -> ) : self #

Creates a new Grid with info and yields a buffer to be filled.

This method is unsafe, but it is usually used to initialize the buffer in a linear fashion, e.g., reading values from a file.

Grid.build(Grid.info("/path/to/file")) do |buffer, size|
  size.times do |i|
    buffer[i] = read_value
  end
end

[View source]
def self.empty_like(other : self | Info) : self #

Returns a zero-filled grid with the same bounds and points as other.

grid = Grid.from_dx "/path/to/grid"
other = Grid.empty_like grid
other.bounds == grid.bounds # => true
other.dim == grid.dim       # => true
other.to_a.minmax           # => {0.0, 0.0}

[View source]
def self.fill_like(other : self | Info, value : Number) : self #

Returns a grid with the same bounds and points as other filled with value.

grid = Grid.from_dx "/path/to/grid"
other = Grid.fill_like grid, 2345.123
other.bounds == grid.bounds # => true
other.dim == grid.dim       # => true
other.to_a.minmax           # => {2345.123, 2345.123}

[View source]
def self.from_chgcar(input : ::IO | Path | String, *args, **options) : self #

[View source]
def self.from_cube(input : ::IO | Path | String, *args, **options) : self #

[View source]
def self.from_dx(input : ::IO | Path | String, *args, **options) : self #

[View source]
def self.from_locpot(input : ::IO | Path | String, *args, **options) : self #

[View source]
def self.new(dim : Dimensions, bounds : Bounds, initial_value : Float64) #

[View source]
def self.new(dim : Dimensions, bounds : Bounds) #

[View source]
def self.new(dim : Dimensions, bounds : Bounds, &block : Location -> Number) #

[View source]
def self.vdw_mask(structure : Structure, dim : Dimensions, bounds : Bounds | Nil = nil, delta : Float64 = 0.02) : self #

TODO add more tests

FIXME check delta calculation (grid.resolution.min / 2 shouldn't be enough?)


[View source]
def self.vdw_mask_like(other : self | Info, structure : Structure, delta : Float64 = 0.02) : self #

Returns a grid mask with the points at the vdW spheres set to 1. It will have the same bounds and points as other.

structure = Structure.read "path/to/file"
info = Grid::Info.new Bounds[5.213, 6.823, 10.352], {20, 25, 40}
grid = Grid.vdw_mask structure, info.dim, info.bounds
Grid.vdw_mask_like(info, structure) == grid # => true

[View source]

Class Method Detail

def self.info(input : ::IO | Path | String, format : IO::FileFormat) : Info #

[View source]
def self.info(path : Path | String) : Info #

[View source]

Instance Method Detail

def *(rhs : Number) : self #

[View source]
def *(rhs : self) : self #

[View source]
def +(rhs : Number) : self #

[View source]
def +(rhs : self) : self #

[View source]
def -(rhs : Number) : self #

[View source]
def -(rhs : self) : self #

[View source]
def /(rhs : Number) : self #

[View source]
def /(rhs : self) : self #

[View source]
def ==(rhs : self) : Bool #
Description copied from class Reference

Returns true if this reference is the same as other. Invokes same?.


[View source]
def [](*args, **options) : Float64 #

[View source]
def []=(i : Int, j : Int, k : Int, value : Float64) : Float64 #

[View source]
def []=(i : Int, value : Float64) : Float64 #

[View source]
def []=(loc : Location, value : Float64) : Float64 #

[View source]
def []?(i : Int, j : Int, k : Int) : Float64 | Nil #

[View source]
def []?(loc : Location) : Float64 | Nil #

[View source]
def []?(vec : Vector) : Float64 | Nil #

TODO add interpolation (check ARBInterp)


[View source]
def bounds : Bounds #

[View source]
def coords_at(*args, **options) : Vector #

[View source]
def coords_at?(i : Int, j : Int, k : Int) : Vector | Nil #

[View source]
def coords_at?(i : Int) : Vector | Nil #

[View source]
def coords_at?(loc : Location) : Vector | Nil #

[View source]
def dim : Dimensions #

[View source]
def dup : self #
Description copied from class Reference

Returns a shallow copy of this object.

This allocates a new object and copies the contents of self into it.


[View source]
def each_axial_slice(axis : Int, reuse : Bool | Array(Float64) = false, & : Array(Float64) -> ) : Nil #

Iterates over slices along axis. Axis is specified as an integer: 0-2 refer to the direction of the first, second or third basis vector, respectively.

grid = Grid.new({2, 3, 2}, Bounds[1, 1, 1]) { |i, j, k| i * 6 + j * 2 + k }
grid.to_a # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
slices = [] of Array(Float64)
grid.each_axial_slice(axis: 1) { |slice| slices << slice }
slices # => [[0, 1, 6, 7], [2, 3, 8, 9], [4, 5, 10, 11]]

When using read-only slices, one can specify the reuse option to prevent many memory allocations:

  • If reuse is an Array, this array will be reused.
  • If reuse is true, the method will create a new array and reuse it.
  • If reuse is false (default), a new array is created and yielded on each iteration.

[View source]
def each_coords(& : Vector -> ) : Nil #

[View source]
def each_loc(vec : Vector, cutoff : Number, & : Location, Float64 -> ) : Nil #

[View source]
def each_loc(& : Location -> ) : Nil #

[View source]
def each_with_coords(& : Float64, Vector -> ) : Nil #

[View source]
def each_with_loc(& : Float64, Location -> ) : Nil #

[View source]
def includes?(*args, **options) #

[View source]
def includes?(*args, **options, &) #

[View source]
def index(loc : Location) : Int32 | Nil #

[View source]
def index(vec : Vector) : Int32 | Nil #

[View source]
def index!(*args, **options) : Int32 #

[View source]
def loc_at(*args, **options) : Location #

[View source]
def loc_at?(i : Int) : Location | Nil #

[View source]
def loc_at?(vec : Vector) : Location | Nil #

[View source]
def map(& : Float64 -> Float64) : self #
Description copied from module Enumerable(Float64)

Returns an Array with the results of running the block against each element of the collection.

[1, 2, 3].map { |i| i * 10 } # => [10, 20, 30]

[View source]
def map!(& : Float64 -> Float64) : self #

[View source]
def map_with_coords(& : Float64, Vector -> Number) : self #

[View source]
def map_with_coords!(& : Float64, Vector -> Number) : self #

[View source]
def map_with_index(& : Float64, Int32 -> Number) : self #
Description copied from module Enumerable(Float64)

Like #map, but the block gets passed both the element and its index.

["Alice", "Bob"].map_with_index { |name, i| "User ##{i}: #{name}" }
# => ["User #0: Alice", "User #1: Bob"]

Accepts an optional offset parameter, which tells it to start counting from there.


[View source]
def map_with_index!(& : Float64, Int32 -> Number) : self #

[View source]
def map_with_loc(& : Float64, Location -> Number) : self #

[View source]
def map_with_loc!(& : Float64, Location -> Number) : self #

[View source]
def mask(& : Float64 -> Bool) : self #

Returns a grid mask. Elements for which the passed block returns true are set to 1, otherwise 0.

Grid masks are very useful to deal with multiple grids, and when points are to be selected based on one grid only.

grid = Grid.new({2, 2, 2}, Bounds[10, 10, 10]) { |i, j, k| i + j + k }
grid.to_a              # => [0, 1, 1, 2, 1, 2, 2, 3]
grid.mask(&.>(1)).to_a # => [0, 0, 0, 1, 0, 1, 1, 1]
grid.to_a              # => [0, 1, 1, 2, 1, 2, 2, 3]

[View source]
def mask(value : Number, delta : Number) : self #

Returns a grid mask. Elements for which (value - ele).abs <= delta returns true are set to 1, otherwise 0.

Grid masks are very useful to deal with multiple grids, and when points are to be selected based on one grid only.

grid = Grid.new({2, 2, 3}, Bounds[1, 1, 1]) { |i, j, k| (i + 1) * (j + 1) * (k + 1) / 5 }
grid.to_a              # => [0.2, 0.4, 0.6, 0.4, 0.8, 1.2, 0.4, 0.8, 1.2, 0.8, 1.6, 2.4]
grid.mask(1, 0.5).to_a # => [0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0]
grid.to_a              # => [0.2, 0.4, 0.6, 0.4, 0.8, 1.2, 0.4, 0.8, 1.2, 0.8, 1.6, 2.4]

[View source]
def mask(pattern) : self #

Returns a grid mask. Elements for which pattern === element returns true are set to 1, otherwise 0.

Grid masks are very useful to deal with multiple grids, and when points are to be selected based on one grid only.

grid = Grid.new({2, 2, 3}, Bounds[1, 1, 1]) { |i, j, k| (i + 1) * (j + 1) * (k + 1) }
grid.to_a              # => [1, 2, 3, 2, 4, 6, 2, 4, 6, 4, 8, 12]
grid.mask(2..4.5).to_a # => [0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0]
grid.to_a              # => [1, 2, 3, 2, 4, 6, 2, 4, 6, 4, 8, 12]

[View source]
def mask!(& : Float64 -> Bool) : self #

Masks a grid by the passed block. Elements for which the passed block returns false are set to 0.

Optimized version of creating a mask and applying it to the same grid, but avoids creating intermediate grids. This is equivalent to grid = grid * grid.mask { ... }.

grid = Grid.new({2, 2, 2}, Bounds[10, 10, 10]) { |i, j, k| i + j + k }
grid.to_a # => [0, 1, 1, 2, 1, 2, 2, 3]
grid.mask! &.>(1)
grid.to_a # => [0, 0, 0, 2, 0, 2, 2, 3]

[View source]
def mask!(value : Number, delta : Number) : self #

Masks a grid by value+/-delta. Elements for which (value - ele).abs > delta returns true are set to 0.

Optimized version of creating a mask and applying it to the same grid, but avoids creating intermediate grids. This is equivalent to grid = grid * grid.mask(value, delta)

grid = Grid.new({2, 2, 3}, Bounds[1, 1, 1]) { |i, j, k| (i + j + k) / 5 }
grid.to_a # => [0.0, 0.2, 0.4, 0.2, 0.4, 0.6, 0.2, 0.4, 0.6, 0.4, 0.6, 0.8]
grid.mask! 0.5, 0.1
grid.to_a # => [0.0, 0.0, 0.4, 0.0, 0.4, 0.6, 0.0, 0.4, 0.6, 0.4, 0.6, 0.0]

[View source]
def mask!(pattern) : self #

Masks a grid by pattern. Elements for which pattern === element returns false are set to 0.

Optimized version of creating a mask and applying it to the same grid, but avoids creating intermediate grids. This is equivalent to grid = grid * grid.mask(pattern)

grid = Grid.new({2, 2, 3}, Bounds[1, 1, 1]) { |i, j, k| (i + 1) * (j + 1) * (k + 1) }
grid.to_a # => [1, 2, 3, 2, 4, 6, 2, 4, 6, 4, 8, 12]
grid.mask! 2..4.5
grid.to_a # => [0, 2, 3, 2, 4, 0, 2, 4, 0, 4, 0, 0]

[View source]
def mask_by_coords(& : Vector -> Bool) : self #

Returns a grid mask. Coordinates for which the passed block returns true are set to 1, otherwise 0.

Grid masks are very useful to deal with multiple grids, and when points are to be selected based on one grid only.

grid = Grid.new({2, 2, 2}, Bounds[10, 10, 10]) { |i, j, k| i * 4 + j * 2 + k }
grid.to_a                           # => [0, 1, 2, 3, 4, 5, 6, 7]
grid.mask_by_coords(&.x.==(0)).to_a # => [1, 1, 1, 1, 0, 0, 0, 0]
grid.to_a                           # => [0, 1, 2, 3, 4, 5, 6, 7]

[View source]
def mask_by_coords!(& : Vector -> Bool) : self #

Masks a grid by coordinates. Coordinates for which the passed block returns false are set to 0.

Optimized version of creating a mask and applying it to the same grid, but avoids creating intermediate grids. This is equivalent to grid = grid * grid.mask_by_coords { ... }

grid = Grid.new({2, 2, 2}, Bounds[5, 5, 5]) { |i, j, k| i * 4 + j * 2 + k }
grid.to_a # => [0, 1, 2, 3, 4, 5, 6, 7]
grid.mask_by_coords! { |vec| vec.y == 5 }
grid.to_a # => [0, 0, 2, 3, 0, 0, 6, 7]

[View source]
def mask_by_index(& : Int32 -> Bool) : self #

Returns a grid mask. Indexes for which the passed block returns true are set to 1, otherwise 0.

Grid masks are very useful to deal with multiple grids, and when points are to be selected based on one grid only.

grid = Grid.new({2, 2, 2}, Bounds[10, 10, 10]) { |i, j, k| i * 4 + j * 2 + k }
grid.to_a                       # => [0, 1, 2, 3, 4, 5, 6, 7]
grid.mask_by_index(&.>(4)).to_a # => [0, 0, 0, 0, 0, 1, 1, 1]
grid.to_a                       # => [0, 1, 2, 3, 4, 5, 6, 7]

[View source]
def mask_by_index!(& : Int32 -> Bool) : self #

Masks a grid by index. Indexes for which the passed block returns false are set to 0.

Optimized version of creating a mask and applying it to the same grid, but avoids creating intermediate grids. This is equivalent to grid = grid * grid.mask_by_index { ... }

grid = Grid.new({2, 2, 2}, Bounds[1, 1, 1]) { |i, j, k| i * 4 + j * 2 + k }
grid.to_a # => [0, 1, 2, 3, 4, 5, 6, 7]
grid.mask_by_index! &.<(3)
grid.to_a # => [0, 1, 2, 0, 0, 0, 0, 0]

[View source]
def mask_by_loc(& : Location -> Bool) : self #

Returns a grid mask. Locations for which the passed block returns true are set to 1, otherwise 0.

Grid masks are very useful to deal with multiple grids, and when points are to be selected based on one grid only.

grid = Grid.new({2, 2, 2}, Bounds[10, 10, 10]) { |i, j, k| i * 4 + j * 2 + k }
grid.to_a                                  # => [0, 1, 2, 3, 4, 5, 6, 7]
grid.mask_by_loc { |i, j, k| k == 1 }.to_a # => [0, 1, 0, 1, 0, 1, 0, 1]
grid.to_a                                  # => [0, 1, 2, 3, 4, 5, 6, 7]

[View source]
def mask_by_loc!(& : Location -> Bool) : self #

Masks a grid by location. Locations for which the passed block returns false are set to 0.

Optimized version of creating a mask and applying it to the same grid, but avoids creating intermediate grids. This is equivalent to grid = grid * grid.mask_by_loc { ... }

grid = Grid.new({2, 2, 2}, Bounds[1, 1, 1]) { |i, j, k| i * 4 + j * 2 + k }
grid.to_a # => [0, 1, 2, 3, 4, 5, 6, 7]
grid.mask_by_loc! { |(i, j, k)| i == 1 }
grid.to_a # => [0, 0, 0, 0, 4, 5, 6, 7]

[View source]
def mean(axis : Int) : Array(Float64) #

Returns the arithmetic mean along axis. Axis is specified as an integer: 0-2 refer to the direction of the first, second or third basis vector, respectively.

Raises IndexError is axis is out of bounds.

grid = Grid.new({2, 3, 4}, Bounds[1, 1, 1]) { |i, j, k| i * 12 + j * 4 + k }
grid.mean(axis: 0) # => [5.5, 17.5]
grid.mean(axis: 1) # => [7.5, 11.5, 15.5]
grid.mean(axis: 2) # => [10, 11, 12, 13]
grid.mean(axis: 3) # raises IndexError

[View source]
def mean : Float64 #

Returns the arithmetic mean of the grid elements.

grid = Grid.new({2, 3, 4}, Bounds[1, 1, 1]) { |i, j, k| i * 12 + j * 4 + k }
grid.mean # => 11.5

[View source]
def mean_with_coords(axis : Int) : Array(Tuple(Float64, Float64)) #

Returns the arithmetic mean along axis with its coordinates. Axis is specified as an integer: 0-2 refer to the direction of the first, second or third basis vector, respectively.

Raises IndexError is axis is out of bounds.

grid = Grid.new({2, 3, 5}, Bounds[1, 1, 1]) { |i, j, k| i * 12 + j * 4 + k }
grid.mean(axis: 1) # => [{9.5, 0.0}, {14.5, 0.5}, {19.5, 1.0}]

[View source]
def ni : Int32 #

[View source]
def nj : Int32 #

[View source]
def nk : Int32 #

[View source]
def origin(*args, **options) #

[View source]
def origin(*args, **options, &) #

[View source]
def resolution : Tuple(Float64, Float64, Float64) #

[View source]
def size : Int32 #
Description copied from module Indexable(Float64)

Returns the number of elements in this container.


[View source]
def step(di : Int, dj : Int, dk : Int) : self #

[View source]
def step(n : Int) : self #

[View source]
def to_a : Array(Float64) #
Description copied from module Indexable(Float64)

Returns an Array with all the elements in the collection.

{1, 2, 3}.to_a # => [1, 2, 3]

[View source]
def to_chgcar(output : ::IO | Path | String, *args, **options) : Nil #

[View source]
def to_chgcar(*args, **options) : String #

[View source]
def to_cube(output : ::IO | Path | String, *args, **options) : Nil #

[View source]
def to_cube(*args, **options) : String #

[View source]
def to_dx(output : ::IO | Path | String, *args, **options) : Nil #

[View source]
def to_dx(*args, **options) : String #

[View source]
def to_locpot(output : ::IO | Path | String, *args, **options) : Nil #

[View source]
def to_locpot(*args, **options) : String #

[View source]
def to_unsafe : Pointer(Float64) #

[View source]
def unsafe_fetch(i : Int) : Float64 #
Description copied from module Indexable(Float64)

Returns the element at the given index, without doing any bounds check.

Indexable makes sure to invoke this method with index in 0...size, so converting negative indices to positive ones is not needed here.

Clients never invoke this method directly. Instead, they access elements with #[](index) and #[]?(index).

This method should only be directly invoked if you are absolutely sure the index is in bounds, to avoid a bounds check for a small boost of performance.


[View source]
def unsafe_fetch(loc : Location) : Float64 #

[View source]
def volume(*args, **options) #

[View source]
def volume(*args, **options, &) #

[View source]