crystalcell 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +23 -1
- data/Gemfile +9 -9
- data/VERSION +1 -1
- data/crystalcell.gemspec +41 -29
- data/lib/crystalcell/atom.rb +116 -118
- data/lib/crystalcell/cell.rb +542 -586
- data/lib/crystalcell/element.rb +49 -140
- data/lib/crystalcell/latticeaxes.rb +198 -185
- data/lib/crystalcell/periodiccell.rb +246 -244
- data/lib/crystalcell/povray/cell.rb +110 -0
- data/lib/crystalcell/povray/cylinder.rb +12 -0
- data/lib/crystalcell/povray/element.rb +284 -0
- data/lib/crystalcell/povray/sphere.rb +12 -0
- data/lib/crystalcell/povray/triangle.rb +17 -0
- data/lib/crystalcell/povray.rb +7 -0
- data/lib/crystalcell.rb +5 -1
- data/test/povray/test_cell.rb +179 -0
- data/test/povray/test_element.rb +230 -0
- data/test/test_atom.rb +198 -198
- data/test/test_cell.rb +1481 -1464
- data/test/test_element.rb +422 -642
- data/test/test_latticeaxes.rb +299 -283
- data/test/test_periodiccell.rb +928 -953
- metadata +63 -62
@@ -1,276 +1,278 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
|
3
|
-
require "pp"
|
4
|
-
|
5
3
|
# Class for crystal cell with periodic boundary.
|
6
4
|
# Coordinates of atoms are kept in the region of 0 <= x_i < 1 of internal coordinate.
|
7
5
|
class CrystalCell::PeriodicCell < CrystalCell::Cell
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
class TypeError < Exception; end
|
8
|
+
|
9
|
+
def initialize( *args )
|
10
|
+
super( *args )
|
11
|
+
reset_positions_inside
|
12
|
+
end
|
13
|
+
|
14
|
+
#ある内部座標から、別のある座標とそれと周期的に等価な座標への距離が
|
15
|
+
#tolerance 以下のものを探し、条件を満たすセルの方向を配列にまとめて返す。
|
16
|
+
#
|
17
|
+
#内部的に、一度 0以上1未満の座標に変換しようかと思ったが、
|
18
|
+
#境界付近で問題が生じうる。
|
19
|
+
#
|
20
|
+
#周囲 27 セルしか考慮しない。
|
21
|
+
#美しさを求めるならば tolerance を完全に含む大きさのスーパーセルにすべきだが、
|
22
|
+
#実装が面倒なわりに滅多に使われることがなさそうなので。
|
23
|
+
#出力の順番は、上位の要素が小さなものから順。
|
24
|
+
#(距離の短いものから順という考え方もなくはないが。)
|
25
|
+
#
|
26
|
+
#tolerance を省略( = nil を与えれば )、27セルの中にある全ての方向を返す。
|
27
|
+
def directions_within_distance( pos0, pos1, tolerance = nil )
|
28
|
+
if pos0.class != Vector3DInternal
|
29
|
+
raise TypeError, "pos0 is not a Vector3DInternal class instance."
|
14
30
|
end
|
15
|
-
|
16
|
-
|
17
|
-
#tolerance 以下のものを探し、条件を満たすセルの方向を配列にまとめて返す。
|
18
|
-
#
|
19
|
-
#内部的に、一度 0以上1未満の座標に変換しようかと思ったが、
|
20
|
-
#境界付近で問題が生じうる。
|
21
|
-
#
|
22
|
-
#周囲 27 セルしか考慮しない。
|
23
|
-
#美しさを求めるならば tolerance を完全に含む大きさのスーパーセルにすべきだが、
|
24
|
-
#実装が面倒なわりに滅多に使われることがなさそうなので。
|
25
|
-
#出力の順番は、上位の要素が小さなものから順。
|
26
|
-
#(距離の短いものから順という考え方もなくはないが。)
|
27
|
-
#
|
28
|
-
#tolerance を省略( = nil を与えれば )、27セルの中にある全ての方向を返す。
|
29
|
-
def directions_within_distance( pos0, pos1, tolerance = nil )
|
30
|
-
if pos0.class != Vector3DInternal
|
31
|
-
raise TypeError, "pos0 is not a Vector3DInternal class instance."
|
32
|
-
end
|
33
|
-
if pos1.class != Vector3DInternal
|
34
|
-
raise TypeError, "pos1 is not a Vector3DInternal class instance."
|
35
|
-
end
|
36
|
-
|
37
|
-
pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di
|
38
|
-
pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di
|
39
|
-
|
40
|
-
#pp pos0
|
41
|
-
|
42
|
-
results = []
|
43
|
-
(-1).upto(1) do |x|
|
44
|
-
(-1).upto(1) do |y|
|
45
|
-
(-1).upto(1) do |z|
|
46
|
-
shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ]
|
47
|
-
d = distance( pos0, ( pos1 + shift))
|
48
|
-
#tolerance が nil ならば距離判定なしで登録。
|
49
|
-
#tolerance が非 nil ならばその値で距離判定して登録。
|
50
|
-
if ( ( ! tolerance ) || ( d < tolerance ) )
|
51
|
-
results << [ x, y, z]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
return results
|
31
|
+
if pos1.class != Vector3DInternal
|
32
|
+
raise TypeError, "pos1 is not a Vector3DInternal class instance."
|
57
33
|
end
|
58
34
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def nearest_direction( pos0, pos1 )
|
74
|
-
if pos0.class != Vector3DInternal
|
75
|
-
raise TypeError,
|
76
|
-
"pos0.class is not a Vector3DInternal"
|
35
|
+
pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di
|
36
|
+
pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di
|
37
|
+
|
38
|
+
results = []
|
39
|
+
(-1).upto(1) do |x|
|
40
|
+
(-1).upto(1) do |y|
|
41
|
+
(-1).upto(1) do |z|
|
42
|
+
shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ]
|
43
|
+
d = distance( pos0, ( pos1 + shift))
|
44
|
+
#tolerance が nil ならば距離判定なしで登録。
|
45
|
+
#tolerance が非 nil ならばその値で距離判定して登録。
|
46
|
+
if ( ( ! tolerance ) || ( d < tolerance ) )
|
47
|
+
results << [ x, y, z]
|
48
|
+
end
|
77
49
|
end
|
78
|
-
|
79
|
-
raise TypeError,
|
80
|
-
"pos1.class is not a Vector3DInternal"
|
81
|
-
end
|
82
|
-
|
83
|
-
pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di
|
84
|
-
pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di
|
85
|
-
|
86
|
-
#set first value
|
87
|
-
min = distance( pos0, pos1 )
|
88
|
-
result = Vector3DInternal[ 0, 0, 0 ]
|
89
|
-
|
90
|
-
#find
|
91
|
-
(-1).upto(1) do |x|
|
92
|
-
(-1).upto(1) do |y|
|
93
|
-
(-1).upto(1) do |z|
|
94
|
-
shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ]
|
95
|
-
d = (pos0 - (pos1 + shift)).to_v3d(@axes).r
|
96
|
-
|
97
|
-
if d < min
|
98
|
-
result = Vector3DInternal[ x, y, z]
|
99
|
-
min = d
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
return result
|
50
|
+
end
|
105
51
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
52
|
+
return results
|
53
|
+
end
|
54
|
+
|
55
|
+
# 周期性を考慮して、
|
56
|
+
# 1個目の内部座標( pos0 ) から見て、
|
57
|
+
# 一番近い 2個目の内部座標に等価なサイトの属するセルの方向を返す。
|
58
|
+
# 返り値は Vector3DInternal で
|
59
|
+
# 内部座標が 0.0 <= r < 1.0 になっていることを前提とする。
|
60
|
+
# (なお、この外側であってもこの範囲に入るように周期移動する。)
|
61
|
+
# 座標 0.0 付近の境界は計算誤差の為におかしなことになり易いので注意が必要。
|
62
|
+
#
|
63
|
+
# NOTE: nearest_direction というメソッド名はどうかと思う。
|
64
|
+
# nearest_lattice_vector とか?
|
65
|
+
# 良い名前があれば、リファクタリング対象。
|
66
|
+
#
|
67
|
+
# NOTE: 0〜1の外側なら内側に入れる、という処理は混乱し易い。
|
68
|
+
# 例外にした方が良いのではないだろうか。
|
69
|
+
def nearest_direction( pos0, pos1 )
|
70
|
+
if pos0.class != Vector3DInternal
|
71
|
+
raise TypeError,
|
72
|
+
"pos0.class is not a Vector3DInternal"
|
126
73
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
#e.g.,
|
131
|
-
# [
|
132
|
-
# [ [0.1, 0.1, 0.1], [0.2, 0.2, 0.2] ],
|
133
|
-
# [ [0.1, 0.1, 0.1], [0.3, 0.4, 0.5] ],
|
134
|
-
# [ [0.9, 0.9, 0.9], [0.8, 0.8, 0.8] ]
|
135
|
-
# ]
|
136
|
-
#@bonds への登録はしない。
|
137
|
-
#elem0, elem1 は bond の両端の原子の組み合わせを指定。
|
138
|
-
#elem0, elem1 はメソッド内部で反転したものもチェックするので、順序が反対でも同じ結果になる。
|
139
|
-
#O-O のように同じ元素を指定することもでき、
|
140
|
-
#この場合向きの異なる 2つの bond が重複したりしない。
|
141
|
-
#d_min, d_max は距離の範囲を指定する。
|
142
|
-
#境界値そのものが問題になることは少ないだろう。
|
143
|
-
#d_min <= d <= d_max(以上、以下) としておくが、計算誤差のためにこれはほぼ無意味だ。
|
144
|
-
#見つけて配列として返すだけで、登録はしない。
|
145
|
-
#
|
146
|
-
#以下の案は棄却。
|
147
|
-
# - いかなる元素でもマッチ。
|
148
|
-
# - 距離の上限を無効。
|
149
|
-
#理由は、
|
150
|
-
# - プログラムが煩雑になる。
|
151
|
-
# - 引数の型が統一されない。
|
152
|
-
# - ほとんど使用する機会がなさそう。
|
153
|
-
# 大抵は元素を指定するし、元素を指定しない bond を描く状況は考えにくい。
|
154
|
-
# これが必要ならメソッドの外で組み合わせを作ってそれぞれで呼べば良い。
|
155
|
-
# - 大抵は距離の上限を定めるし、上限なしではハリネズミになるだけ。
|
156
|
-
# また、プログラム上は距離の上限を 3x3x3 スーパーセルに制限したりするが、
|
157
|
-
# 論理的に距離の上限なしってのは無限のセルを対象にすることになり、整合性がとれない。
|
158
|
-
def find_bonds( elem0, elem1, d_min, d_max )
|
159
|
-
results = []
|
160
|
-
atoms.each do |inner_atom|
|
161
|
-
atoms_in_supercell( -1, 1, -1, 1, -1, 1 ).each do |outer_atom|
|
162
|
-
#元素の種類による判定
|
163
|
-
ie = inner_atom.element
|
164
|
-
oe = outer_atom.element
|
165
|
-
next unless ( (( ie == elem0 ) && ( oe == elem1 )) || (( ie == elem1 ) && ( oe == elem0 ))) #elem0, elem1 と同じ構成
|
166
|
-
|
167
|
-
#距離による判定
|
168
|
-
ip = inner_atom.position
|
169
|
-
op = outer_atom.position
|
170
|
-
next if distance( ip, op ) < d_min
|
171
|
-
next if distance( ip, op ) > d_max
|
172
|
-
next if distance( ip, op ) == 0.0 #同一サイト判定
|
173
|
-
|
174
|
-
#重複判定, 正順か逆順が既に含まれていれば無視。
|
175
|
-
next if ( results.include?( [ ip, op ] ) || results.include?( [ op, ip ] ) )
|
176
|
-
|
177
|
-
results << [ ip, op ]
|
178
|
-
end
|
179
|
-
end
|
180
|
-
return results
|
74
|
+
if pos1.class != Vector3DInternal
|
75
|
+
raise TypeError,
|
76
|
+
"pos1.class is not a Vector3DInternal"
|
181
77
|
end
|
182
78
|
|
183
|
-
|
184
|
-
|
185
|
-
#
|
186
|
-
#返す形式は以下のような形式。
|
187
|
-
#[ [0, 1, [0, 0, 0], [0, 1, [0, 0, 1] ]
|
188
|
-
#最初の 0, 1 はそれぞれ配列 @atoms 内の番号で、
|
189
|
-
#0番目の原子からから同じセル内の 1番目の原子
|
190
|
-
#0番目の原子からから[0,0,1]方向に隣接するセル内の 1番目の原子を意味する。
|
191
|
-
#起点となる原子の番号と(上記 0 )と目的地の原子の番号(上記 1)が同じこともありうる。
|
192
|
-
#異なる場合は起点となる原子の番号が、目的地の原子の番号より若くなる。
|
193
|
-
#
|
194
|
-
#自分と同じ番号の場合は、同じセルを除外する。
|
195
|
-
#すなわち、[0, 0, [0, 0, 0] や [1, 1, [0, 0, 0] は含まれない。
|
196
|
-
def pairs_within_distance( distance )
|
197
|
-
results = []
|
198
|
-
n_atom = @atoms.size
|
199
|
-
n_atom.times do |i|
|
200
|
-
pos_i = @atoms[i].position
|
79
|
+
pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di
|
80
|
+
pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di
|
201
81
|
|
202
|
-
|
203
|
-
|
82
|
+
#set first value
|
83
|
+
min = distance( pos0, pos1 )
|
84
|
+
result = Vector3DInternal[ 0, 0, 0 ]
|
204
85
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
86
|
+
#find
|
87
|
+
(-1).upto(1) do |x|
|
88
|
+
(-1).upto(1) do |y|
|
89
|
+
(-1).upto(1) do |z|
|
90
|
+
shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ]
|
91
|
+
d = (pos0 - (pos1 + shift)).to_v3d(@axes).r
|
92
|
+
|
93
|
+
if d < min
|
94
|
+
result = Vector3DInternal[ x, y, z]
|
95
|
+
min = d
|
96
|
+
end
|
211
97
|
end
|
212
|
-
|
98
|
+
end
|
213
99
|
end
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
100
|
+
return result
|
101
|
+
end
|
102
|
+
|
103
|
+
#周期性を考慮し、2つの地点間の最短距離を返す。
|
104
|
+
#pos0, pos1 には内部座標を指定する。
|
105
|
+
#内部的には周囲 3x3x3 のセル中の27地点のうち最も近いものを得る。
|
106
|
+
#旧 minimum_distance
|
107
|
+
def nearest_distance( pos0, pos1 )
|
108
|
+
[pos0, pos1].each_with_index do |pos, index|
|
109
|
+
unless pos.class == Vector3DInternal
|
110
|
+
raise TypeError,
|
111
|
+
"#{index} th argument: #{pos.inspect}, #{pos.class}"
|
112
|
+
end
|
219
113
|
end
|
220
114
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
115
|
+
pos0 = Vector3DInternal[* pos0.map{ |i| i - i.floor }]
|
116
|
+
pos1 = Vector3DInternal[* pos1.map{ |i| i - i.floor }]
|
117
|
+
direction = self.nearest_direction( pos0, pos1 )
|
118
|
+
3.times do |i|
|
119
|
+
pos1[ i ] += direction[ i ]
|
226
120
|
end
|
121
|
+
distance( pos0, pos1 )
|
122
|
+
end
|
123
|
+
|
124
|
+
#条件にマッチした原子間距離を見つけて、端点の内部座標の組を要素とする配列を返す。
|
125
|
+
#返される配列は3重配列になる。
|
126
|
+
#e.g.,
|
127
|
+
# [
|
128
|
+
# [ [0.1, 0.1, 0.1], [0.2, 0.2, 0.2] ],
|
129
|
+
# [ [0.1, 0.1, 0.1], [0.3, 0.4, 0.5] ],
|
130
|
+
# [ [0.9, 0.9, 0.9], [0.8, 0.8, 0.8] ]
|
131
|
+
# ]
|
132
|
+
#@bonds への登録はしない。
|
133
|
+
#elem0, elem1 は bond の両端の原子の組み合わせを指定。
|
134
|
+
#elem0, elem1 はメソッド内部で反転したものもチェックするので、順序が反対でも同じ結果になる。
|
135
|
+
#O-O のように同じ元素を指定することもでき、
|
136
|
+
#この場合向きの異なる 2つの bond が重複したりしない。
|
137
|
+
#d_min, d_max は距離の範囲を指定する。
|
138
|
+
#境界値そのものが問題になることは少ないだろう。
|
139
|
+
#d_min <= d <= d_max(以上、以下) としておくが、計算誤差のためにこれはほぼ無意味だ。
|
140
|
+
#見つけて配列として返すだけで、登録はしない。
|
141
|
+
#
|
142
|
+
#以下の案は棄却。
|
143
|
+
# - いかなる元素でもマッチ。
|
144
|
+
# - 距離の上限を無効。
|
145
|
+
#理由は、
|
146
|
+
# - プログラムが煩雑になる。
|
147
|
+
# - 引数の型が統一されない。
|
148
|
+
# - ほとんど使用する機会がなさそう。
|
149
|
+
# 大抵は元素を指定するし、元素を指定しない bond を描く状況は考えにくい。
|
150
|
+
# これが必要ならメソッドの外で組み合わせを作ってそれぞれで呼べば良い。
|
151
|
+
# - 大抵は距離の上限を定めるし、上限なしではハリネズミになるだけ。
|
152
|
+
# また、プログラム上は距離の上限を 3x3x3 スーパーセルに制限したりするが、
|
153
|
+
# 論理的に距離の上限なしってのは無限のセルを対象にすることになり、整合性がとれない。
|
154
|
+
def find_bonds( elem0, elem1, d_min, d_max )
|
155
|
+
results = []
|
156
|
+
atoms.each do |inner_atom|
|
157
|
+
atoms_in_supercell( -1, 1, -1, 1, -1, 1 ).each do |outer_atom|
|
158
|
+
#元素の種類による判定
|
159
|
+
ie = inner_atom.element
|
160
|
+
oe = outer_atom.element
|
161
|
+
|
162
|
+
next unless ( (( ie == elem0 ) && ( oe == elem1 )) || (( ie == elem1 ) && ( oe == elem0 ))) #elem0, elem1 と同じ構成
|
163
|
+
#next unless (( ie == elem0 ) && ( oe == elem1 )) #elem0, elem1 と同じ構成
|
164
|
+
|
165
|
+
#距離による判定
|
166
|
+
ip = inner_atom.position
|
167
|
+
op = outer_atom.position
|
168
|
+
next if distance( ip, op ) < d_min.to_f
|
169
|
+
next if distance( ip, op ) > d_max.to_f
|
170
|
+
next if distance( ip, op ) == 0.0 ## Check identical site
|
171
|
+
|
172
|
+
#重複判定, 正順か逆順が既に含まれていれば無視。
|
173
|
+
next if ( results.include?( [ ip, op ] ) || results.include?( [ op, ip ] ) )
|
174
|
+
if (ie == elem0)
|
175
|
+
results << [ ip, op ]
|
176
|
+
else
|
177
|
+
results << [ op, ip ]
|
178
|
+
end
|
227
179
|
|
228
|
-
|
229
|
-
# Dependent function 'translate' functions the same.
|
230
|
-
def translate!( *args )
|
231
|
-
super( *args )
|
232
|
-
reset_positions_inside
|
180
|
+
end
|
233
181
|
end
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
182
|
+
return results
|
183
|
+
end
|
184
|
+
|
185
|
+
#引数 distance 以下の間の距離にある原子のペアを列挙して返す。
|
186
|
+
#この際、あくまで unique なものを返し、A-B があれば B-A はリストに含まれない。
|
187
|
+
#
|
188
|
+
#返す形式は以下のような形式。
|
189
|
+
#[ [0, 1, [0, 0, 0], [0, 1, [0, 0, 1] ]
|
190
|
+
#最初の 0, 1 はそれぞれ配列 @atoms 内の番号で、
|
191
|
+
#0番目の原子からから同じセル内の 1番目の原子
|
192
|
+
#0番目の原子からから[0,0,1]方向に隣接するセル内の 1番目の原子を意味する。
|
193
|
+
#起点となる原子の番号と(上記 0 )と目的地の原子の番号(上記 1)が同じこともありうる。
|
194
|
+
#異なる場合は起点となる原子の番号が、目的地の原子の番号より若くなる。
|
195
|
+
#
|
196
|
+
#自分と同じ番号の場合は、同じセルを除外する。
|
197
|
+
#すなわち、[0, 0, [0, 0, 0] や [1, 1, [0, 0, 0] は含まれない。
|
198
|
+
def pairs_within_distance( distance )
|
199
|
+
results = []
|
200
|
+
n_atom = @atoms.size
|
201
|
+
n_atom.times do |i|
|
202
|
+
pos_i = @atoms[i].position
|
203
|
+
|
204
|
+
n_atom.times do |j|
|
205
|
+
pos_j = @atoms[j].position
|
206
|
+
|
207
|
+
directions_within_distance( pos_i, pos_j, distance ).each do |dir|
|
208
|
+
#next if ( i==j && dir==[0,0,0] ) #距離0の自分自身。下の条件に含まれる。
|
209
|
+
next if ( dir==[0,0,0] && i >= j ) #同じセル内は番号が若い方からのみ。
|
210
|
+
results << [ i, j, dir ]
|
241
211
|
end
|
242
|
-
|
212
|
+
end
|
243
213
|
end
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
214
|
+
return results
|
215
|
+
end
|
216
|
+
|
217
|
+
# Functions as like CrystalCell::Cell.add_atom, but the coordinates are converted to the region of 0 <= x_i < 1.
|
218
|
+
def add_atom( *args )
|
219
|
+
super( *args )
|
220
|
+
reset_positions_inside
|
221
|
+
end
|
222
|
+
|
223
|
+
# Functions as like CrystalCell::Cell.rotate!, but the coordinates are converted to the region of 0 <= x_i < 1.
|
224
|
+
# Dependent function 'rotate' functions the same.
|
225
|
+
def rotate!( *args )
|
226
|
+
super( *args )
|
227
|
+
reset_positions_inside
|
228
|
+
end
|
229
|
+
|
230
|
+
# Functions as like CrystalCell::Cell.translate!, but the coordinates are converted to the region of 0 <= x_i < 1.
|
231
|
+
# Dependent function 'translate' functions the same.
|
232
|
+
def translate!( *args )
|
233
|
+
super( *args )
|
234
|
+
reset_positions_inside
|
235
|
+
end
|
236
|
+
|
237
|
+
# Return a new instance converted to CrystalCell::Cell class.
|
238
|
+
def to_cell
|
239
|
+
tmp = CrystalCell::Cell.new( @axes.to_a )
|
240
|
+
tmp.comment = self.comment
|
241
|
+
@atoms.each do |atom|
|
242
|
+
tmp.add_atom(atom)
|
253
243
|
end
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
244
|
+
return tmp
|
245
|
+
end
|
246
|
+
|
247
|
+
#undef center_of_atoms
|
248
|
+
|
249
|
+
# superclass の inverse_axis! を行ったあと、
|
250
|
+
# 原子の座標をセル内部に移す。
|
251
|
+
def inverse_axis!( axis_id )
|
252
|
+
result = Marshal.load( Marshal.dump( self ) )
|
253
|
+
super( axis_id )
|
254
|
+
reset_positions_inside
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
# Reset internal coordinates of all atoms inside the region of 0 <= x_i < 1.
|
260
|
+
def reset_positions_inside
|
261
|
+
@atoms.each do |atom|
|
262
|
+
coords = atom.position
|
263
|
+
atom.set_position(coords.map {|coord| coord - coord.floor}.to_a.to_v3di)
|
263
264
|
end
|
265
|
+
end
|
264
266
|
|
265
267
|
end
|
266
268
|
|
267
269
|
class CrystalCell::Cell
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
270
|
+
#require "Crystal/PeriodicCell.rb"
|
271
|
+
# Return a new instance converted to PeriodicCell class.
|
272
|
+
def to_pcell
|
273
|
+
atoms = Marshal.load(Marshal.dump(@atoms))
|
274
|
+
result = CrystalCell::PeriodicCell.new( @axes.to_a, atoms )
|
275
|
+
result.comment = self.comment
|
276
|
+
return result
|
277
|
+
end
|
276
278
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#! /usr/bin/env ruby # coding: utf-8
|
2
|
+
|
3
|
+
class CrystalCell::Povray::Cell < CrystalCell::Cell
|
4
|
+
RADIUS_RATIO = 0.3
|
5
|
+
|
6
|
+
LATTICE_RADIUS = 0.1
|
7
|
+
LATTICE_COLOR = [0.50, 0.50, 0.50]
|
8
|
+
BOND_RADIUS = 0.05
|
9
|
+
BOND_COLOR = [0.75, 0.75, 0.75]
|
10
|
+
|
11
|
+
# povray 形式の文字列を返す。
|
12
|
+
def to_pov
|
13
|
+
return atoms_to_pov + bonds_to_pov + lattice_to_pov
|
14
|
+
end
|
15
|
+
|
16
|
+
#private
|
17
|
+
|
18
|
+
# 原子を描画するための pov 形式文字列を返す。
|
19
|
+
# 周期境界近傍の原子が tolerance 未満ならば、反対側のセル境界にも描画する。
|
20
|
+
def atoms_to_povs(tolerance = 0.0)
|
21
|
+
results = []
|
22
|
+
atoms.each do |atom|
|
23
|
+
periodic_translations(atom.position, tolerance).each do |translation|
|
24
|
+
results << atom_to_pov(atom.translate(translation))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
results
|
28
|
+
end
|
29
|
+
|
30
|
+
# 原子間の連結棒を描画するための pov 形式文字列を返す。
|
31
|
+
# E.g.,
|
32
|
+
# elem0 = 'O'
|
33
|
+
# elem1 = 'Li'
|
34
|
+
# min_distance = 0.0
|
35
|
+
# max_distance = 1.0
|
36
|
+
# 上記の指定の場合、O-O 間かつ距離が 0.0〜1.0 の原子間のみ
|
37
|
+
# bond を出力する。
|
38
|
+
def bonds_to_povs(elem0, elem1, min_distance, max_distance)
|
39
|
+
results = []
|
40
|
+
cell = self.to_pcell
|
41
|
+
cell.find_bonds(elem0, elem1, min_distance, max_distance).each do |pair|
|
42
|
+
cart0 = pair[0].to_v3d(self.axes)
|
43
|
+
cart1 = pair[1].to_v3d(self.axes)
|
44
|
+
midpoint = Mageo::Vector3D.midpoint(cart0, cart1)
|
45
|
+
results << Mageo::Cylinder.new(
|
46
|
+
[cart0, midpoint], BOND_RADIUS).to_pov(
|
47
|
+
CrystalCell::Povray::Element.color(elem0)) + "\n"
|
48
|
+
results << Mageo::Cylinder.new(
|
49
|
+
[cart1, midpoint], BOND_RADIUS).to_pov(
|
50
|
+
CrystalCell::Povray::Element.color(elem1)) + "\n"
|
51
|
+
end
|
52
|
+
results
|
53
|
+
end
|
54
|
+
|
55
|
+
# 格子の棒を描画するための pov 形式文字列を返す。
|
56
|
+
def lattice_to_povs
|
57
|
+
v000 = Vector3DInternal[ 0.0, 0.0, 0.0 ].to_v3d(self.axes)
|
58
|
+
v001 = Vector3DInternal[ 0.0, 0.0, 1.0 ].to_v3d(self.axes)
|
59
|
+
v010 = Vector3DInternal[ 0.0, 1.0, 0.0 ].to_v3d(self.axes)
|
60
|
+
v011 = Vector3DInternal[ 0.0, 1.0, 1.0 ].to_v3d(self.axes)
|
61
|
+
v100 = Vector3DInternal[ 1.0, 0.0, 0.0 ].to_v3d(self.axes)
|
62
|
+
v101 = Vector3DInternal[ 1.0, 0.0, 1.0 ].to_v3d(self.axes)
|
63
|
+
v110 = Vector3DInternal[ 1.0, 1.0, 0.0 ].to_v3d(self.axes)
|
64
|
+
v111 = Vector3DInternal[ 1.0, 1.0, 1.0 ].to_v3d(self.axes)
|
65
|
+
|
66
|
+
results = []
|
67
|
+
results << Mageo::Cylinder.new([v000, v001], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
68
|
+
results << Mageo::Cylinder.new([v010, v011], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
69
|
+
results << Mageo::Cylinder.new([v100, v101], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
70
|
+
results << Mageo::Cylinder.new([v110, v111], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
71
|
+
results << Mageo::Cylinder.new([v000, v010], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
72
|
+
results << Mageo::Cylinder.new([v100, v110], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
73
|
+
results << Mageo::Cylinder.new([v001, v011], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
74
|
+
results << Mageo::Cylinder.new([v101, v111], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
75
|
+
results << Mageo::Cylinder.new([v000, v100], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
76
|
+
results << Mageo::Cylinder.new([v001, v101], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
77
|
+
results << Mageo::Cylinder.new([v010, v110], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
78
|
+
results << Mageo::Cylinder.new([v011, v111], LATTICE_RADIUS).to_pov(LATTICE_COLOR).to_s + "\n"
|
79
|
+
results
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def atom_to_pov(atom)
|
85
|
+
color = CrystalCell::Povray::Element.color( atom.element)
|
86
|
+
radius = CrystalCell::Povray::Element.draw_radius(atom.element) * RADIUS_RATIO
|
87
|
+
Mageo::Sphere.new(atom.position.to_v3d(axes), radius).to_pov(color) +
|
88
|
+
" // #{atom.element}" + "\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
def periodic_translations(pos, tolerance)
|
92
|
+
results = [Vector3DInternal[0.0, 0.0, 0.0]]
|
93
|
+
|
94
|
+
3.times do |axis|
|
95
|
+
tmp = Marshal.load(Marshal.dump(results))
|
96
|
+
if (pos[axis] < tolerance)
|
97
|
+
translation = Vector3DInternal[0.0, 0.0, 0.0]
|
98
|
+
translation[axis] += 1.0
|
99
|
+
tmp.each {|vec| results << vec + translation }
|
100
|
+
end
|
101
|
+
if (1.0 - tolerance < pos[axis])
|
102
|
+
translation = Vector3DInternal[0.0, 0.0, 0.0]
|
103
|
+
translation[axis] -= 1.0
|
104
|
+
tmp.each {|vec| results << vec + translation }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
results
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#! /usr/bin/env ruby # coding: utf-8
|
2
|
+
|
3
|
+
class Mageo::Cylinder
|
4
|
+
# povray 形式の文字列を返す。
|
5
|
+
# color は Float による配列。通常、0〜1の範囲。
|
6
|
+
def to_pov(color)
|
7
|
+
sprintf( "object { cylinder{ <% 7.4f, % 7.4f, % 7.4f>, <% 7.4f, % 7.4f, % 7.4f>, %7.4f } pigment { color rgb <%4.2f, %4.2f, %4.2f> } }",
|
8
|
+
*positions[0], *positions[1], radius, *color)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|