Class: CrystalCell::PeriodicCell
- Defined in:
- lib/crystalcell/periodiccell.rb
Overview
Class for crystal cell with periodic boundary. Coordinates of atoms are kept in the region of 0 <= x_i < 1 of internal coordinate.
Defined Under Namespace
Classes: TypeError
Instance Attribute Summary
Attributes inherited from Cell
#angle_tolerance, #atoms, #axes, #comment, #element_names, #symprec
Instance Method Summary collapse
-
#add_atom(*args) ⇒ Object
Functions as like CrystalCell::Cell.add_atom, but the coordinates are converted to the region of 0 <= x_i < 1.
-
#directions_within_distance(pos0, pos1, tolerance = nil) ⇒ Object
ある内部座標から、別のある座標とそれと周期的に等価な座標への距離が tolerance 以下のものを探し、条件を満たすセルの方向を配列にまとめて返す。.
-
#find_bonds(elem0, elem1, d_min, d_max) ⇒ Object
条件にマッチした原子間距離を見つけて、端点の内部座標の組を要素とする配列を返す。 返される配列は3重配列になる。 e.g., [ [ [0.1, 0.1, 0.1], [0.2, 0.2, 0.2] ], [ [0.1, 0.1, 0.1], [0.3, 0.4, 0.5] ], [ [0.9, 0.9, 0.9], [0.8, 0.8, 0.8] ] ] elem0, elem1 は bond の両端の原子の組み合わせを指定。 elem0, elem1 はメソッド内部で反転したものもチェックするので、順序が反対でも同じ結果になる。 O-O のように同じ元素を指定することもでき、 この場合向きの異なる 2つの bond が重複したりしない。 d_min, d_max は距離の範囲を指定する。 境界値そのものが問題になることは少ないだろう。 d_min <= d <= d_max(以上、以下) としておくが、計算誤差のためにこれはほぼ無意味だ。 見つけて配列として返すだけで、登録はしない。.
-
#initialize(*args) ⇒ PeriodicCell
constructor
A new instance of PeriodicCell.
-
#inverse_axis!(axis_id) ⇒ Object
superclass の inverse_axis! を行ったあと、 原子の座標をセル内部に移す。.
-
#nearest_direction(pos0, pos1) ⇒ Object
周期性を考慮して、 1個目の内部座標( pos0 ) から見て、 一番近い 2個目の内部座標に等価なサイトの属するセルの方向を返す。 返り値は Vector3DInternal で 内部座標が 0.0 <= r < 1.0 になっていることを前提とする。 (なお、この外側であってもこの範囲に入るように周期移動する。) 座標 0.0 付近の境界は計算誤差の為におかしなことになり易いので注意が必要。.
-
#nearest_distance(pos0, pos1) ⇒ Object
周期性を考慮し、2つの地点間の最短距離を返す。 pos0, pos1 には内部座標を指定する。 内部的には周囲 3x3x3 のセル中の27地点のうち最も近いものを得る。 旧 minimum_distance.
-
#pairs_within_distance(distance) ⇒ Object
引数 distance 以下の間の距離にある原子のペアを列挙して返す。 この際、あくまで unique なものを返し、A-B があれば B-A はリストに含まれない。.
-
#rotate!(*args) ⇒ Object
Functions as like CrystalCell::Cell.rotate!, but the coordinates are converted to the region of 0 <= x_i < 1.
-
#to_cell ⇒ Object
Return a new instance converted to CrystalCell::Cell class.
-
#translate!(*args) ⇒ Object
Functions as like CrystalCell::Cell.translate!, but the coordinates are converted to the region of 0 <= x_i < 1.
Methods inherited from Cell
#==, #atoms_in_supercell, #brv_lattice, #brv_positions, #brv_types, #calc_volume, #cell_of_elements, #center_of_atoms, #delete_atom, #distance, #elements, #equal_atoms_in_delta?, #equal_in_delta?, #equal_lattice_in_delta?, #exchange_axes, #exchange_axes!, #hall_symbol, #hallnum, #inverse_axis, #o_shift, #operate, #positions, #reflect, #reflect!, #rotate, #rotations, #select_indices, #set_elements, #setting, #spg, #spgnum, #symmetry_operations, #t_mat, #to_pcell, #translate, #translations, #unite, #wyckoffs
Constructor Details
#initialize(*args) ⇒ PeriodicCell
Returns a new instance of PeriodicCell.
9 10 11 12 |
# File 'lib/crystalcell/periodiccell.rb', line 9 def initialize( *args ) super( *args ) reset_positions_inside end |
Instance Method Details
#add_atom(*args) ⇒ Object
Functions as like CrystalCell::Cell.add_atom, but the coordinates are converted to the region of 0 <= x_i < 1.
218 219 220 221 |
# File 'lib/crystalcell/periodiccell.rb', line 218 def add_atom( *args ) super( *args ) reset_positions_inside end |
#directions_within_distance(pos0, pos1, tolerance = nil) ⇒ Object
ある内部座標から、別のある座標とそれと周期的に等価な座標への距離が tolerance 以下のものを探し、条件を満たすセルの方向を配列にまとめて返す。
内部的に、一度 0以上1未満の座標に変換しようかと思ったが、 境界付近で問題が生じうる。
周囲 27 セルしか考慮しない。 美しさを求めるならば tolerance を完全に含む大きさのスーパーセルにすべきだが、 実装が面倒なわりに滅多に使われることがなさそうなので。 出力の順番は、上位の要素が小さなものから順。 (距離の短いものから順という考え方もなくはないが。)
tolerance を省略( = nil を与えれば )、27セルの中にある全ての方向を返す。
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/crystalcell/periodiccell.rb', line 27 def directions_within_distance( pos0, pos1, tolerance = nil ) if pos0.class != Vector3DInternal raise TypeError, "pos0 is not a Vector3DInternal class instance." end if pos1.class != Vector3DInternal raise TypeError, "pos1 is not a Vector3DInternal class instance." end pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di results = [] (-1).upto(1) do |x| (-1).upto(1) do |y| (-1).upto(1) do |z| shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ] d = distance( pos0, ( pos1 + shift)) #tolerance が nil ならば距離判定なしで登録。 #tolerance が非 nil ならばその値で距離判定して登録。 if ( ( ! tolerance ) || ( d < tolerance ) ) results << [ x, y, z] end end end end return results end |
#find_bonds(elem0, elem1, d_min, d_max) ⇒ Object
条件にマッチした原子間距離を見つけて、端点の内部座標の組を要素とする配列を返す。 返される配列は3重配列になる。 e.g., [
[ [0.1, 0.1, 0.1], [0.2, 0.2, 0.2] ],
[ [0.1, 0.1, 0.1], [0.3, 0.4, 0.5] ],
[ [0.9, 0.9, 0.9], [0.8, 0.8, 0.8] ]
] elem0, elem1 は bond の両端の原子の組み合わせを指定。 elem0, elem1 はメソッド内部で反転したものもチェックするので、順序が反対でも同じ結果になる。 O-O のように同じ元素を指定することもでき、 この場合向きの異なる 2つの bond が重複したりしない。 d_min, d_max は距離の範囲を指定する。 境界値そのものが問題になることは少ないだろう。 d_min <= d <= d_max(以上、以下) としておくが、計算誤差のためにこれはほぼ無意味だ。 見つけて配列として返すだけで、登録はしない。
以下の案は棄却。
-
いかなる元素でもマッチ。
-
距離の上限を無効。
理由は、
-
プログラムが煩雑になる。
-
引数の型が統一されない。
-
ほとんど使用する機会がなさそう。
大抵は元素を指定するし、元素を指定しない bond を描く状況は考えにくい。 これが必要ならメソッドの外で組み合わせを作ってそれぞれで呼べば良い。
-
大抵は距離の上限を定めるし、上限なしではハリネズミになるだけ。
また、プログラム上は距離の上限を 3x3x3 スーパーセルに制限したりするが、 論理的に距離の上限なしってのは無限のセルを対象にすることになり、整合性がとれない。
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/crystalcell/periodiccell.rb', line 154 def find_bonds( elem0, elem1, d_min, d_max ) results = [] atoms.each do |inner_atom| atoms_in_supercell( -1, 1, -1, 1, -1, 1 ).each do |outer_atom| #元素の種類による判定 ie = inner_atom.element oe = outer_atom.element next unless ( (( ie == elem0 ) && ( oe == elem1 )) || (( ie == elem1 ) && ( oe == elem0 ))) #elem0, elem1 と同じ構成 #next unless (( ie == elem0 ) && ( oe == elem1 )) #elem0, elem1 と同じ構成 #距離による判定 ip = inner_atom.position op = outer_atom.position next if distance( ip, op ) < d_min.to_f next if distance( ip, op ) > d_max.to_f next if distance( ip, op ) == 0.0 ## Check identical site #重複判定, 正順か逆順が既に含まれていれば無視。 next if ( results.include?( [ ip, op ] ) || results.include?( [ op, ip ] ) ) if (ie == elem0) results << [ ip, op ] else results << [ op, ip ] end end end return results end |
#inverse_axis!(axis_id) ⇒ Object
superclass の inverse_axis! を行ったあと、 原子の座標をセル内部に移す。
249 250 251 252 253 |
# File 'lib/crystalcell/periodiccell.rb', line 249 def inverse_axis!( axis_id ) #result = Marshal.load( Marshal.dump( self ) ) super( axis_id ) reset_positions_inside end |
#nearest_direction(pos0, pos1) ⇒ Object
周期性を考慮して、 1個目の内部座標( pos0 ) から見て、 一番近い 2個目の内部座標に等価なサイトの属するセルの方向を返す。 返り値は Vector3DInternal で 内部座標が 0.0 <= r < 1.0 になっていることを前提とする。 (なお、この外側であってもこの範囲に入るように周期移動する。) 座標 0.0 付近の境界は計算誤差の為におかしなことになり易いので注意が必要。
NOTE: nearest_direction というメソッド名はどうかと思う。 nearest_lattice_vector とか? 良い名前があれば、リファクタリング対象。
NOTE: 0〜1の外側なら内側に入れる、という処理は混乱し易い。 例外にした方が良いのではないだろうか。
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/crystalcell/periodiccell.rb', line 69 def nearest_direction( pos0, pos1 ) if pos0.class != Vector3DInternal raise TypeError, "pos0.class is not a Vector3DInternal" end if pos1.class != Vector3DInternal raise TypeError, "pos1.class is not a Vector3DInternal" end pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di #set first value min = distance( pos0, pos1 ) result = Vector3DInternal[ 0, 0, 0 ] #find (-1).upto(1) do |x| (-1).upto(1) do |y| (-1).upto(1) do |z| shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ] d = (pos0 - (pos1 + shift)).to_v3d(@axes).r if d < min result = Vector3DInternal[ x, y, z] min = d end end end end return result end |
#nearest_distance(pos0, pos1) ⇒ Object
周期性を考慮し、2つの地点間の最短距離を返す。 pos0, pos1 には内部座標を指定する。 内部的には周囲 3x3x3 のセル中の27地点のうち最も近いものを得る。 旧 minimum_distance
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/crystalcell/periodiccell.rb', line 107 def nearest_distance( pos0, pos1 ) [pos0, pos1].each_with_index do |pos, index| unless pos.class == Vector3DInternal raise TypeError, "#{index} th argument: #{pos.inspect}, #{pos.class}" end end pos0 = Vector3DInternal[* pos0.map{ |i| i - i.floor }] pos1 = Vector3DInternal[* pos1.map{ |i| i - i.floor }] direction = self.nearest_direction( pos0, pos1 ) 3.times do |i| pos1[ i ] += direction[ i ] end distance( pos0, pos1 ) end |
#pairs_within_distance(distance) ⇒ Object
引数 distance 以下の間の距離にある原子のペアを列挙して返す。 この際、あくまで unique なものを返し、A-B があれば B-A はリストに含まれない。
返す形式は以下のような形式。
- [0, 1, [0, 0, 0], [0, 1, [0, 0, 1
-
]
最初の 0, 1 はそれぞれ配列 @atoms 内の番号で、 0番目の原子からから同じセル内の 1番目の原子 0番目の原子からから方向に隣接するセル内の 1番目の原子を意味する。 起点となる原子の番号と(上記 0 )と目的地の原子の番号(上記 1)が同じこともありうる。 異なる場合は起点となる原子の番号が、目的地の原子の番号より若くなる。
自分と同じ番号の場合は、同じセルを除外する。 すなわち、[0, 0, [0, 0, 0] や [1, 1, [0, 0, 0] は含まれない。
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/crystalcell/periodiccell.rb', line 198 def pairs_within_distance( distance ) results = [] n_atom = @atoms.size n_atom.times do |i| pos_i = @atoms[i].position n_atom.times do |j| pos_j = @atoms[j].position directions_within_distance( pos_i, pos_j, distance ).each do |dir| #next if ( i==j && dir==[0,0,0] ) #距離0の自分自身。下の条件に含まれる。 next if ( dir==[0,0,0] && i >= j ) #同じセル内は番号が若い方からのみ。 results << [ i, j, dir ] end end end return results end |
#rotate!(*args) ⇒ Object
Functions as like CrystalCell::Cell.rotate!, but the coordinates are converted to the region of 0 <= x_i < 1. Dependent function ‘rotate’ functions the same.
225 226 227 228 |
# File 'lib/crystalcell/periodiccell.rb', line 225 def rotate!( *args ) super( *args ) reset_positions_inside end |
#to_cell ⇒ Object
Return a new instance converted to CrystalCell::Cell class.
238 239 240 241 242 243 244 245 |
# File 'lib/crystalcell/periodiccell.rb', line 238 def to_cell tmp = CrystalCell::Cell.new( @axes.to_a ) tmp.comment = self.comment @atoms.each do |atom| tmp.add_atom(atom) end return tmp end |
#translate!(*args) ⇒ Object
Functions as like CrystalCell::Cell.translate!, but the coordinates are converted to the region of 0 <= x_i < 1. Dependent function ‘translate’ functions the same.
232 233 234 235 |
# File 'lib/crystalcell/periodiccell.rb', line 232 def translate!( *args ) super( *args ) reset_positions_inside end |