class Chem::Spatial::Grid

Overview

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

TODO implement functionality from vmd's volmap

Included Modules

Defined in:

chem/register_format.cr
chem/spatial/grid.cr

Constructors

Instance Method Summary

Instance methods inherited from module Indexable(Float64)

sentence(io : IO, separator : String = ", ", *, pair_separator : String = " and ", tail_separator : String = ", and ", & : T, IO -> ) : Nil
sentence(io : IO, separator : String = ", ", *, pair_separator : String = " and ", tail_separator : String = ", and ") : Nil
sentence(separator : String = ", ", *, pair_separator : String = " and ", tail_separator : String = ", and ", & : T -> ) : String
sentence(separator : String = ", ", *, pair_separator : String = " and ", tail_separator : String = ", and ") : String
sentence

Instance methods inherited from module Enumerable(Float64)

average(weights : Indexable(Number))
average(weights : Indexable(Number), & : T -> _)
average
, mean
mean(& : T -> _)
mean

Constructor Detail

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

[View source]
def self.atom_distance(structure : Structure, dim : Dimensions, bounds : Parallelepiped | 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 Parallelepiped[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(info : Info, source_file : String | Path | Nil = nil, & : 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.build(dim : Dimensions, bounds : Parallelepiped, source_file : String | Path | Nil = nil, & : Pointer(Float64), Int32 -> ) : self #

[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) : self #

Returns the grid encoded in input using the Chem::VASP::Chgcar file format. Arguments are forwarded to Chem::VASP::Chgcar::Reader.open.


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

Returns the grid encoded in input using the Chem::Cube file format. Arguments are forwarded to Chem::Cube::Reader.open.


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

Returns the grid encoded in input using the Chem::DX file format. Arguments are forwarded to Chem::DX::Reader.open.


[View source]
def self.from_io(io : IO, format : IO::ByteFormat) : self #

Reads a grid from io in the given format. See also: IO#read_bytes. Raises IO::EOFError if there is missing data.


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

Returns the grid encoded in input using the Chem::VASP::Locpot file format. Arguments are forwarded to Chem::VASP::Locpot::Reader.open.


[View source]
def self.new(dim : Dimensions, bounds : Parallelepiped, source_file : String | Path | Nil = nil) #

[View source]
def self.new(dim : Dimensions, bounds : Parallelepiped, initial_value : Float64, source_file : String | Path | Nil = nil) #

[View source]
def self.new(dim : Dimensions, bounds : Parallelepiped, source_file : String | Path | Nil = nil, &block : Location -> Number) #

[View source]
def self.read(input : IO | Path | String, format : Chem::Format | String) : self #

Returns the grid encoded in the specified file using format. Raises ArgumentError if format has required arguments or cannot read Chem::Spatial::Grid.

The supported file formats are Chem::Cube, Chem::DX, Chem::VASP::Chgcar, Chem::VASP::Locpot. Use the .from_* methods to customize how the object is decoded in the corresponding file format if possible.


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

Returns the grid encoded in the specified file. The file format is chosen based on the filename (see Chem::Format#from_filename). Raises ArgumentError if the file format cannot be determined.

The supported file formats are the following:

Use the .from_* methods to customize how the object is decoded in the corresponding file format if possible.


[View source]
def self.vdw_mask(structure : Structure, dim : Dimensions, bounds : Parallelepiped | 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 Parallelepiped[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]

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 : Vec3) : Float64 | Nil #

TODO add interpolation (check ARBInterp)


[View source]
def bounds : Parallelepiped #

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

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

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

[View source]
def coords_at?(loc : Location) : Vec3 | 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}, Parallelepiped[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(& : Vec3 -> ) : Nil #

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

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

[View source]
def each_with_coords(& : Float64, Vec3 -> ) : 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 : Vec3) : 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 : Vec3) : 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, Vec3 -> Number) : self #

[View source]
def map_with_coords!(& : Float64, Vec3 -> 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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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(& : Vec3 -> 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}, Parallelepiped[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!(& : Vec3 -> 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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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}, Parallelepiped[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 : FloatTriple #

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

Returns the number of elements in this container.


[View source]
def source_file : Path | Nil #

[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, structure : Structure) : Nil #

Writes the grid to output using the Chem::VASP::Chgcar file format. Arguments are fowarded to Chem::VASP::Chgcar::Writer.open.


[View source]
def to_chgcar(structure : Structure) : String #

Returns a string representation of the grid using the Chem::VASP::Chgcar file format. Arguments are fowarded to Chem::VASP::Chgcar::Writer.open.


[View source]
def to_cube(output : IO | Path | String, atoms : AtomCollection) : Nil #

Writes the grid to output using the Chem::Cube file format. Arguments are fowarded to Chem::Cube::Writer.open.


[View source]
def to_cube(atoms : AtomCollection) : String #

Returns a string representation of the grid using the Chem::Cube file format. Arguments are fowarded to Chem::Cube::Writer.open.


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

Writes the grid to output using the Chem::DX file format. Arguments are fowarded to Chem::DX::Writer.open.


[View source]
def to_dx : String #

Returns a string representation of the grid using the Chem::DX file format. Arguments are fowarded to Chem::DX::Writer.open.


[View source]
def to_io(io : IO, format : IO::ByteFormat = :system_endian) : Nil #

Writes the binary representation of the grid to io in the given format. See also IO#write_bytes. Raises ArgumentError is the encoding is not UTF-8.


[View source]
def to_locpot(output : IO | Path | String, structure : Structure) : Nil #

Writes the grid to output using the Chem::VASP::Locpot file format. Arguments are fowarded to Chem::VASP::Locpot::Writer.open.


[View source]
def to_locpot(structure : Structure) : String #

Returns a string representation of the grid using the Chem::VASP::Locpot file format. Arguments are fowarded to Chem::VASP::Locpot::Writer.open.


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

[View source]
def unsafe_fetch(index : 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]
def write(output : IO | Path | String, format : Chem::Format | String) : Nil #

Writes the grid to output using format. Raises ArgumentError if format has required arguments or cannot write Chem::Spatial::Grid.

The supported file formats are Chem::DX. Use the #to_* methods to customize how the object is written in the corresponding file format if possible.


[View source]
def write(path : Path | String) : Nil #

Writes the grid to the specified file. The file format is chosen based on the filename (see Chem::Format#from_filename). Raises ArgumentError if the file format cannot be determined.

The supported file formats are the following:

Use the #to_* methods to customize how the object is written in the corresponding file format if possible.


[View source]