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

セル内の原子は、内部的には配列として保持するが、
この順序は制御できないものとする。
たとえば Li, Ge, O の順序にソートされているなどと思ってはいけない。
順序に依存するプログラムを作ってはいけない。

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

ボツ案:
たとえば構成元素の情報を持ち、
さらに Atom クラスインスタンスも持つとする。
原子の追加の仕方によっては、
Atoms クラスの元素情報と矛盾するという状況は十分に考えられる。

構成元素として Li があっても、
Li 原子リストが空リストだったらその元素はあると判定されるべきか、
疑問が生じる。

Direct Known Subclasses

PeriodicCell, Povray::Cell

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.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/crystalcell/cell.rb', line 53

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|
    unless atom.is_a?(CrystalCell::Atom)
      raise CellTypeError,
        "#{atom} is not a kind of CrystalCell::Atom."
    end
  end
  @atoms = atoms
  @symprec = 1.0E-10
  @angle_tolerance = -1.0
end

Instance Attribute Details

#angle_toleranceObject

Returns the value of attribute angle_tolerance.



47
48
49
# File 'lib/crystalcell/cell.rb', line 47

def angle_tolerance
  @angle_tolerance
end

#atomsObject (readonly)

Returns the value of attribute atoms.



46
47
48
# File 'lib/crystalcell/cell.rb', line 46

def atoms
  @atoms
end

#axesObject (readonly)

Returns the value of attribute axes.



46
47
48
# File 'lib/crystalcell/cell.rb', line 46

def axes
  @axes
end

#commentObject

Returns the value of attribute comment.



47
48
49
# File 'lib/crystalcell/cell.rb', line 47

def comment
  @comment
end

#element_namesObject (readonly)

Returns the value of attribute element_names.



46
47
48
# File 'lib/crystalcell/cell.rb', line 46

def element_names
  @element_names
end

#symprecObject

Returns the value of attribute symprec.



47
48
49
# File 'lib/crystalcell/cell.rb', line 47

def symprec
  @symprec
end

Instance Method Details

#==(other) ⇒ Object

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



184
185
186
187
188
189
190
# File 'lib/crystalcell/cell.rb', line 184

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)


73
74
75
76
77
# File 'lib/crystalcell/cell.rb', line 73

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倍の原子数になる。



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/crystalcell/cell.rb', line 139

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

#brv_latticeObject



508
509
510
# File 'lib/crystalcell/cell.rb', line 508

def brv_lattice
  get_spg_dataset[10]
end

#brv_positionsObject



516
517
518
# File 'lib/crystalcell/cell.rb', line 516

def brv_positions
  get_spg_dataset[12]
end

#brv_typesObject



512
513
514
# File 'lib/crystalcell/cell.rb', line 512

def brv_types
  get_spg_dataset[11]
end

#calc_volumeObject

Calculate volume.



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

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 の要素のどれかと完全一致しているもののみ対象となる。 サブクラスのインスタンスで実行した場合には、 サブクラスのインスタンスとして生成する。



298
299
300
301
302
303
304
# File 'lib/crystalcell/cell.rb', line 298

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.



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

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 クラスは原子を配列として保持しており、 その番号を指定すると考えると分かり易かろう。



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

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 で行うべき。



198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/crystalcell/cell.rb', line 198

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

#elementsObject

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



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

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

#equal_atoms_in_delta?(other, position_tolerance) ⇒ Boolean

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

Returns:

  • (Boolean)


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

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, position_tolerance) ⇒ Boolean

等価判定。 格子定数の長さの比率の許容値、格子定数の角度の許容値、原子座標の許容値。 def equal_in_delta?( other, length_ratio, angle_tolerance, position_tolerance )

Returns:

  • (Boolean)


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

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

#equal_lattice_in_delta?(other, length_ratio) ⇒ Boolean

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

Returns:

  • (Boolean)


158
159
160
161
162
# File 'lib/crystalcell/cell.rb', line 158

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

#exchange_axes(axis_ids) ⇒ Object

exchange_axes! の非破壊版。



393
394
395
396
397
# File 'lib/crystalcell/cell.rb', line 393

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:



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/crystalcell/cell.rb', line 372

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

#hall_symbolObject



480
481
482
# File 'lib/crystalcell/cell.rb', line 480

def hall_symbol
  get_spg_dataset[3]
end

#hallnumObject



476
477
478
# File 'lib/crystalcell/cell.rb', line 476

def hallnum
  get_spg_dataset[2]
end

#inverse_axis(axis_id) ⇒ Object

inverse_axis! の非破壊版。



360
361
362
363
364
# File 'lib/crystalcell/cell.rb', line 360

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つの頂点のどれかに移動する操作と考えれば良い。

ただし厳密には、格子ベクトルは LatticeAxes.new によって triangulate されるため、
b 軸を反転させた時は a 軸も反転する。( b 軸の y成分を正にするため)
c 軸を反転させた時は a, b 軸も反転する。( c 軸の z成分を正にするため)

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



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/crystalcell/cell.rb', line 331

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

#o_shiftObject



492
493
494
# File 'lib/crystalcell/cell.rb', line 492

def o_shift
  get_spg_dataset[6]
end

#operate(rotation, translation) ⇒ Object

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



415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/crystalcell/cell.rb', line 415

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

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



98
99
100
# File 'lib/crystalcell/cell.rb', line 98

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

#reflectObject

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



407
408
409
410
411
# File 'lib/crystalcell/cell.rb', line 407

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

#reflect!Object

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



400
401
402
403
404
# File 'lib/crystalcell/cell.rb', line 400

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.



252
253
254
255
256
# File 'lib/crystalcell/cell.rb', line 252

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.



235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/crystalcell/cell.rb', line 235

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

#rotationsObject



496
497
498
# File 'lib/crystalcell/cell.rb', line 496

def rotations
  get_spg_dataset[7]
end

#select_indices(&block) ⇒ Object

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



105
106
107
# File 'lib/crystalcell/cell.rb', line 105

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.



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/crystalcell/cell.rb', line 118

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

#settingObject



484
485
486
# File 'lib/crystalcell/cell.rb', line 484

def setting
  get_spg_dataset[4]
end

#spgObject



472
473
474
# File 'lib/crystalcell/cell.rb', line 472

def spg
  get_spg_dataset[1]
end

#spgnumObject



468
469
470
# File 'lib/crystalcell/cell.rb', line 468

def spgnum
  get_spg_dataset[0]
end

#symmetry_operationsObject

Return rotations and translation of symmetry operations.



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

def symmetry_operations
  #dataset = get_spg_dataset(symprec, angle_tolerance)

  #pp dataset;exit

  results = []
  rotations.size.times do |index|
    results << {
      :rotation => rotations[index],
      :translation => translations[index]
    }
  end
  return results
end

#t_matObject



488
489
490
# File 'lib/crystalcell/cell.rb', line 488

def t_mat
  get_spg_dataset[5]
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

#to_povcellObject

ptg_symbol, ptg_num, trans_mat = getptg(rotations)



522
523
524
525
# File 'lib/crystalcell/cell.rb', line 522

def to_povcell
  #pp @axes
  CrystalCell::Povray::Cell.new(@axes, @atoms)
end

#translate(ary) ⇒ Object

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



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

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

#translate!(ary) ⇒ Object

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



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

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

#translationsObject



500
501
502
# File 'lib/crystalcell/cell.rb', line 500

def translations
  get_spg_dataset[8]
end

#unite(cell) ⇒ Object

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



312
313
314
315
316
317
318
319
# File 'lib/crystalcell/cell.rb', line 312

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

#wyckoffsObject



504
505
506
# File 'lib/crystalcell/cell.rb', line 504

def wyckoffs
  get_spg_dataset[9]
end