Class: CrystalCell::Cell

Inherits:
Object
  • Object
show all
Includes:
Mageo
Defined in:
lib/crystalcell/cell.rb,
lib/crystalcell/periodiccell.rb

Overview

Class for crystal cell with lattice axes and atoms. Symmetry operations are not considered in this class. A sub class SymmetricCell can do, which overrides equal_in_delta methods.

Written by Ippei Kishida [2010-12-19].

Cell

Note: Cell クラスは元素情報をネイティブには持たない

Direct Known Subclasses

PeriodicCell

Defined Under Namespace

Classes: ArgumentError, AxesMismatchError, AxesRangeError, NoAtomError, NoSpglibError, SameAxesError, TypeError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(axes, atoms = []) ⇒ Cell

Argument ‘axes’ must have :to_a method and expressed in 3x3 Array.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/crystalcell/cell.rb', line 64

def initialize(axes, atoms = [])
    #raise CellTypeError unless axes.is_a?(Axes)
    if axes.class == CrystalCell::LatticeAxes
        @axes = axes
    else
        @axes = CrystalCell::LatticeAxes.new( axes.to_a )
    end

    atoms.each do |atom|
        #pp atom
        unless atom.is_a?(CrystalCell::Atom)
            raise CellTypeError,
                "#{atom} is not a kind of CrystalCell::Atom."
        end
    end
    @atoms = atoms
end

Instance Attribute Details

#atomsObject (readonly)

Returns the value of attribute atoms.



60
61
62
# File 'lib/crystalcell/cell.rb', line 60

def atoms
  @atoms
end

#axesObject (readonly)

Returns the value of attribute axes.



60
61
62
# File 'lib/crystalcell/cell.rb', line 60

def axes
  @axes
end

#commentObject

Returns the value of attribute comment.



61
62
63
# File 'lib/crystalcell/cell.rb', line 61

def comment
  @comment
end

#element_namesObject (readonly)

Returns the value of attribute element_names.



60
61
62
# File 'lib/crystalcell/cell.rb', line 60

def element_names
  @element_names
end

Instance Method Details

#==(other) ⇒ Object

等価判定。「==」による等価判定は実数の等価判定と同じく、基本的には使うべきではない。しかし、これを定義しておくとテストが楽になることが多い。



192
193
194
195
196
197
198
# File 'lib/crystalcell/cell.rb', line 192

def ==( other )
    #pp axes;
    #pp other.axes;

    return false unless self.axes == other.axes #equal_in_delta( 0.0, 0.0, 0.0 ) とすると計算誤差でうまくいかないことがある。
    equal_atoms_in_delta?( other, 0.0 )
end

#add_atom(atom) ⇒ Object

セルに原子を追加する。

Raises:

  • (CellTypeError)


83
84
85
86
87
# File 'lib/crystalcell/cell.rb', line 83

def add_atom(atom)
    #raise "Cell::add_atom, 2nd argument must be Array." if pos.class != Array
    raise CellTypeError unless atom.is_a?(CrystalCell::Atom)
    @atoms << atom
end

#atoms_in_supercell(a_min, a_max, b_min, b_max, c_min, c_max) ⇒ Object

セルを拡張したスーパーセルを考えたとき、中に含まれる原子のリストを返す。引数の意味は以下の通り。a_min : a 軸方向のセルの方向を整数で示したときの最小値a_max : a 軸方向のセルの方向を整数で示したときの最大値b_min : b 軸方向のセルの方向を整数で示したときの最小値b_max : b 軸方向のセルの方向を整数で示したときの最大値c_min : c 軸方向のセルの方向を整数で示したときの最小値c_max : c 軸方向のセルの方向を整数で示したときの最大値-1, 1, -1, 1, -1, 1 と指定すれば 3x3x3 の 27倍体積の構造になる。



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/crystalcell/cell.rb', line 149

def atoms_in_supercell( a_min, a_max, b_min, b_max, c_min, c_max )
    results = []
    @atoms.each do |atom|
        a_min.upto( a_max ) do |a|
            b_min.upto( b_max ) do |b|
                c_min.upto( c_max ) do |c|
                    results << CrystalCell::Atom.new( atom.element, (atom.position.to_v3di + [ a, b, c ].to_v3di).to_a )
                end
            end
        end
    end
    results
end

#axis_independencies(symprec, angle_tolerance) ⇒ Object

Return information of axes symmetry. E.g.,

true , true , true

when a = b = c, like cubic

true , false, false

when a = b != c, like hexagonal, trigonal, tetragonal

false, true , false

(same as above)

false, false, true

(same as above)

false, false, false

when a != b != c, like triclinic, monoclinic, orthorhombic



473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# File 'lib/crystalcell/cell.rb', line 473

def axis_independencies(symprec, angle_tolerance)
    rotations = symmetry_operations(symprec, angle_tolerance).map {|oper| oper[:rotation]}

    results = [true, true, true]
    rotations.each do |rot|
        3.times do |i|
            3.times do |j|
                next if rot[i][j] == 0
                next if i == j
                results[i] = false
                results[j] = false
            end
        end
    end
    return results
end

#calc_volumeObject

Calculate volume.



293
294
295
296
297
# File 'lib/crystalcell/cell.rb', line 293

def calc_volume
    axes = @axes.to_a.map { |i| Vector3D[*i] }
    vA, vB, vC = axes[0..2]
    Vector3D.scalar_triple_product( vA, vB, vC ).abs
end

#cell_of_elements(elems) ⇒ Object

Generate a new cell with the same lattice consants, containing atoms of indicated elements. Argument ‘elems’ must be an array of element names. 含まれる @atoms の順序は、保存される。元素ごとに並び換えたりしない。CrystalCell::Atom.element が elems の要素のどれかと完全一致しているもののみ対象となる。サブクラスのインスタンスで実行した場合には、サブクラスのインスタンスとして生成する。



306
307
308
309
310
311
312
# File 'lib/crystalcell/cell.rb', line 306

def cell_of_elements( elems )
    result = self.class.new( @axes )
    @atoms.each do |atom|
        result.add_atom(atom) if elems.include?( atom.element )
    end
    return result
end

#center_of_atomsObject

Return arithmetic mean of atomic positions in an internal coordinates. Raise ‘Cell::NoAtomError’ if no atoms included in self.



282
283
284
285
286
287
288
289
290
# File 'lib/crystalcell/cell.rb', line 282

def center_of_atoms
    raise CrystalCell::Cell::NoAtomError if @atoms.size == 0

    vec = Vector3DInternal[ 0.0, 0.0, 0.0 ]
    @atoms.each { |i|
        3.times { |j| vec[j] += i.position[j] }
    }
    vec *= 1.0/ @atoms.size
end

#delete_atom(i) ⇒ Object

Delete an atom from a cell. i は Cell クラスが保持している原子の番号。Cell クラスは原子を配列として保持しており、その番号を指定すると考えると分かり易かろう。



93
94
95
96
# File 'lib/crystalcell/cell.rb', line 93

def delete_atom( i )
    #raise "CrystalCell::Atom ID[#{i}] not exist" if @atoms[i] == nil
    @atoms.delete_at( i )
end

#distance(pos0, pos1) ⇒ Object

2つの地点間の距離を返す。それぞれ、内部座標 Vector3DInternal クラスインスタンスなら絶対座標に変換される。絶対座標ならばそのまま計算する。Vector3D か Vector3DInternal 以外のクラスなら例外 Cell::TypeError を投げる。周期性を考慮したりはしない。周期性を考慮した距離は PeriodicCell#nearest_distance で行うべき。



206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/crystalcell/cell.rb', line 206

def distance( pos0, pos1 )
    if ((pos0.class != Vector3DInternal) && (pos0.class != Vector3D))
        raise CrystalCell::Cell::TypeError
    end
    if ((pos1.class != Vector3DInternal) && (pos1.class != Vector3D))
        raise CrystalCell::Cell::TypeError
    end

    v0 = pos0.to_v3d(@axes) if pos0.class == Vector3DInternal
    v1 = pos1.to_v3d(@axes) if pos1.class == Vector3DInternal

    (v0 - v1).r
end

#dump_poscar(element_order, io = nil) ⇒ Object

Dump string in POSCAR format. Argument <io> can be a file handle or nil. POSCAR を作るには、元素の順番を指定する必要があるのでそれを element_order で指定している。element_order の要素と == で一致する CrystalCell::Atom instance をそれぞれ全て出力する。e.g.,

cell.dump_poscar( STDOUT ) #=> output to stdout.
cell.dump_poscar( fileIo ) #=> output to file.
cell.dump_poscar( nil ) #=> return in String instance.
cell.dump_poscar                #=> return in String instance.


231
232
233
234
235
236
237
# File 'lib/crystalcell/cell.rb', line 231

def dump_poscar( element_order, io = nil )
    if (io == nil)
        return create_poscar( element_order )
    else
        io.puts create_poscar( element_order )
    end
end

#elementsObject

全ての原子の元素情報のリストを返す。unique なものを抽出したりはしない。unique なものが必要なら返り値に .uniq をつければ良い。e.g., #=> [‘Li’, ‘N’, ‘Li’] e.g., #=> [0, 1, 2, 1]



103
104
105
# File 'lib/crystalcell/cell.rb', line 103

def elements
    @atoms.collect{ |i| i.element }
end

#equal_atoms_in_delta?(other, position_tolerance) ⇒ Boolean

含まれる全原子が等価比較で一対一対応が付けられれば true を返す。Cell に保持される順番に関係なく、等価な原子同士が一対一に対応づけられるかでチェックする。

Returns:

  • (Boolean)


176
177
178
179
# File 'lib/crystalcell/cell.rb', line 176

def equal_atoms_in_delta?( other, position_tolerance )
    return false unless Mapping::map?(@atoms, other.atoms ){ |i,j| i.equal_in_delta?( j, position_tolerance ) }
    return true
end

#equal_in_delta?(other, length_ratio, angle_tolerance, position_tolerance) ⇒ Boolean

等価判定。格子定数の長さの比率の許容値、格子定数の角度の許容値、原子座標の許容値。

Returns:

  • (Boolean)


183
184
185
186
187
# File 'lib/crystalcell/cell.rb', line 183

def equal_in_delta?( other, length_ratio, angle_tolerance, position_tolerance )
    return false unless equal_lattice_in_delta?(other, length_ratio, angle_tolerance)
    return false unless equal_atoms_in_delta?(other, position_tolerance)
    return true
end

#equal_lattice_in_delta?(other, length_ratio, angle_tolerance) ⇒ Boolean

他のセルと格子定数が等価であれば true を、そうでなければ false を返す。other: 他のセルlength_ratio: 長さ(a, b, c) の許容値を比で指定angle_tolerance: 角度(alpha, beta, gamma) の許容値を角度の値で指定

Returns:

  • (Boolean)


167
168
169
170
171
# File 'lib/crystalcell/cell.rb', line 167

def equal_lattice_in_delta?( other, length_ratio, angle_tolerance )
    @axes.equal_in_delta?(
        CrystalCell::LatticeAxes.new( other.axes.to_a ), length_ratio, angle_tolerance
    )
end

#exchange_axes(axis_ids) ⇒ Object

exchange_axes! の非破壊版。



431
432
433
434
435
# File 'lib/crystalcell/cell.rb', line 431

def exchange_axes( axis_ids )
    result = Marshal.load( Marshal.dump( self ) )
    result.exchange_axes!( axis_ids )
    return result
end

#exchange_axes!(axis_ids) ⇒ Object

2つの格子ベクトルを交換する破壊的メソッド。Argument ‘axis_ids’ must have 2 items of integer. 0, 1, and 2 mean x, y, and z axes, respectively. この範囲の整数でなければ例外 Cell::AxesRangeError. axis_ids に含まれる 2つの数字が同じならば例外 Cell::SameAxesError.

Raises:



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/crystalcell/cell.rb', line 410

def exchange_axes!( axis_ids )
    raise ArgumentError if axis_ids.size != 2
    axis_ids.each{ |i| raise AxesRangeError if ( i < 0 || 2 < i ) }
    raise CrystalCell::Cell::SameAxesError if ( axis_ids[0] == axis_ids[1] )

    #格子定数を交換。
    axes = @axes.axes
    axes[ axis_ids[0]], axes[ axis_ids[1]] = axes[ axis_ids[1]], axes[ axis_ids[0]]
    @axes = CrystalCell::LatticeAxes.new( axes )

    #内部座標を交換。
    new_atoms = []
    @atoms.each do |atom|
        new_pos = atom.position
        new_pos[ axis_ids[0]], new_pos[ axis_ids[1]] =
            new_pos[ axis_ids[1]], new_pos[ axis_ids[0]]
        new_atoms << CrystalCell::Atom.new( atom.element, new_pos, atom.name, atom.movable_flags )
    end
end

#inverse_axis(axis_id) ⇒ Object

inverse_axis! の非破壊版。



398
399
400
401
402
# File 'lib/crystalcell/cell.rb', line 398

def inverse_axis( axis_id )
    result = Marshal.load( Marshal.dump( self ) )
    result.inverse_axis!( axis_id )
    return result
end

#inverse_axis!(axis_id) ⇒ Object

任意の格子軸のベクトルを反転する破壊的メソッド。大まかなイメージとしては、格子軸の原点をセルを構成する8つの頂点のどれかに移動する操作と考えれば良い。

セルの形状、内部のモチーフは保存する。原子の絶対座標は移動せず、内部座標の表現が変わる。引数 axis_id は 0, 1, 2 のいずれかの値を取り、それぞれ x, y, z軸を表す。x, y, z軸の関係は、右手系と左手系が入れ替わる。



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/crystalcell/cell.rb', line 339

def inverse_axis!( axis_id )
    axis_id = axis_id.to_i
    raise CrystalCell::Cell::AxesRangeError if ( axis_id < 0 || 2 < axis_id )

    axes = []
    3.times do |i|
        if ( i == axis_id )
            axes << @axes[ i ] * (-1.0)
        else
            axes << @axes[ i ]
        end
    end
    @axes = CrystalCell::LatticeAxes.new( axes )

    atoms = []
    @atoms.each do |atom|
        position = []
        3.times do |i|
            if i == axis_id
                position[i] = atom.position[i] * (-1)
            else
                position[i] = atom.position[i]
            end
        end
        atom.position = Vector3DInternal[*position]
    end
end

#operate(rotation, translation) ⇒ Object

rotation と translation からなる操作(e.g., 対称操作) を加えたセルを返す。



453
454
455
456
457
458
459
460
461
462
463
464
# File 'lib/crystalcell/cell.rb', line 453

def operate(rotation, translation)
    rotation = Matrix[*rotation]
    translation = translation.to_v3d
    new_atoms = atoms.map do |atom|
        position = atom.position.to_v3d([
            [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
        ])
        new_pos = (rotation * position + translation).to_a.to_v3di
        CrystalCell::Atom.new(atom.element, new_pos, atom.name)
    end
    CrystalCell::Cell.new(@axes, new_atoms)
end

#positionsObject

全ての原子の位置情報のリストを返す。



108
109
110
# File 'lib/crystalcell/cell.rb', line 108

def positions
    @atoms.collect{ |i| i.position }
end

#reflectObject

鏡像となるセルに変換する非破壊的メソッド。



445
446
447
448
449
# File 'lib/crystalcell/cell.rb', line 445

def reflect
    result = Marshal.load( Marshal.dump( self ) )
    result.reflect!
    return result
end

#reflect!Object

鏡像となるセルに変換する破壊的メソッド。



438
439
440
441
442
# File 'lib/crystalcell/cell.rb', line 438

def reflect!
    axes = @axes.to_a
    axes[0][0] *= -1
    @axes = CrystalCell::LatticeAxes.new( axes )
end

#rotate(matrix) ⇒ Object

Cell rotation.( Nondestructive method) Argument ‘matrix’ is 3x3 Array of float. This method does not modify the position to the range between 0 and 1, even if it was out of range.



260
261
262
263
264
# File 'lib/crystalcell/cell.rb', line 260

def rotate( matrix )
    t = Marshal.load( Marshal.dump( self ) )
    t.rotate!( matrix )
    return t
end

#rotate!(matrix) ⇒ Object

Cell rotation.( Destructive method) Argument ‘matrix’ is 3x3 Array of float. This method does not modify the position to the range between 0 and 1, even if it was out of range.



243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/crystalcell/cell.rb', line 243

def rotate!( matrix )
    @atoms.each { |atom|
        old_pos = atom.position
        new_pos = [0.0, 0.0, 0.0]
        3.times do |y|
            3.times do |x|
                new_pos[y] += (matrix[y][x] * old_pos[x])
            end
        end
        atom.set_position( new_pos )
    }
end

#select_indices(&block) ⇒ Object

元素情報が elem の原子の index を配列にまとめて返す。index は原子の永続的な id ではない。Array#select は index ではなく要素そのものを配列にして返すので、少し違う。



115
116
117
# File 'lib/crystalcell/cell.rb', line 115

def select_indices( &block )
    return @atoms.select_indices( &block )
end

#set_elements(elems) ⇒ Object

Set element name to each atom in self. Argument ‘elems’ is a list of new names, which has [] method. e.g.,

1. Array, [ 'Li', 'O' ]
2. Hash , { 0 => 'Li', 1 => 'O' ]
3. Hash , { 'Li' => 'Na' }
  1. and 2. of the above examples induce the same result.

Case 1. can be convenient for element names of array from POTCAR.

The atoms with the name which is not included the hash key do not change their names.



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/crystalcell/cell.rb', line 128

def set_elements( elems )
    @atoms.each do |atom|
        begin
            new_elem = elems[ atom.element ]
        rescue
            next
        end
        next if new_elem == nil
        atom.element = new_elem
    end
end

#to_pcellObject

require “Crystal/PeriodicCell.rb” Return a new instance converted to PeriodicCell class.



270
271
272
273
274
275
# File 'lib/crystalcell/periodiccell.rb', line 270

def to_pcell
    atoms = Marshal.load(Marshal.dump(@atoms))
    result = CrystalCell::PeriodicCell.new( @axes.to_a, atoms )
    result.comment = self.comment
    return result
end

#translate(ary) ⇒ Object

並進移動を行う非破壊的メソッド。ary は Float 3 要素の配列。



274
275
276
277
278
# File 'lib/crystalcell/cell.rb', line 274

def translate( ary )
    t = Marshal.load( Marshal.dump( self ) )
    t.translate!( ary )
    return t
end

#translate!(ary) ⇒ Object

並進移動を行う破壊的メソッド。ary は Float 3 要素の配列。



268
269
270
# File 'lib/crystalcell/cell.rb', line 268

def translate!( ary )
    @atoms.each { |atom| atom.translate!( ary ) }
end

#unite(cell) ⇒ Object

格子定数の同じ2つのセルを合わせて、全ての原子が含まれる1つのセルを返す非破壊的メソッド。2つのセルの格子定数が異なれば例外 Cell::AxesMismatchError を発生させる。内部的には @atoms はレシーバの @atoms のあとに引数の @atoms を追加した形になる。comment は空文字になる。原子座標の重複チェックなどは行わない。



320
321
322
323
324
325
326
327
# File 'lib/crystalcell/cell.rb', line 320

def unite( cell )
    #raise Cell::AxesMismatchError unless @axes == cell.axes
    result = Marshal.load( Marshal.dump( self ) )
    cell.atoms.each do |atom|
        result.add_atom(atom)
    end
    return result
end