crystalcell 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/lib/crystalcell/atom.rb +133 -0
- data/lib/crystalcell/cell.rb +547 -0
- data/lib/crystalcell/element.rb +148 -0
- data/lib/crystalcell/latticeaxes.rb +210 -0
- data/lib/crystalcell/periodiccell.rb +277 -0
- data/lib/crystalcell.rb +0 -0
- data/test/helper.rb +17 -0
- data/test/test_atom.rb +210 -0
- data/test/test_cell.rb +1167 -0
- data/test/test_element.rb +656 -0
- data/test/test_latticeaxes.rb +294 -0
- data/test/test_periodiccell.rb +965 -0
- metadata +197 -0
@@ -0,0 +1,210 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
gem "mageo"
|
3
|
+
require "mageo/vector3d.rb"
|
4
|
+
require "mageo/vector3dinternal.rb"
|
5
|
+
require "mageo/axes.rb"
|
6
|
+
gem "malge"
|
7
|
+
require "malge/simultaneousequations.rb"
|
8
|
+
|
9
|
+
# Class to deal with lattice axes in three dimensional space,
|
10
|
+
# related to cell parameter and crystal axes.
|
11
|
+
# Lattice information cannot be changed after initialized.
|
12
|
+
#
|
13
|
+
# When lattice axes of three vectors are given,
|
14
|
+
# lattice axes are automatically converted as below.
|
15
|
+
# - c axes in internal axis is along to z axis in cartesian axis.
|
16
|
+
# - b axes in internal axis is on the b-c plane.
|
17
|
+
# - a axes in internal axis is set to be a right-hand system.
|
18
|
+
# E.g.,
|
19
|
+
# [ [0.5, 0.5, 0.0], [0.5, 0.0, 0.5], [0.0, 0.5, 0.5] ]
|
20
|
+
# will be converted to
|
21
|
+
# [ [0.57735, 0.20412, 0.35355],
|
22
|
+
# [0.00000, 0.61237, 0.35355],
|
23
|
+
# [0.00000, 0.00000, 0.70710]]
|
24
|
+
class LatticeAxes < Axes
|
25
|
+
|
26
|
+
class InitializeError < Exception; end
|
27
|
+
class ArgumentError < Exception; end
|
28
|
+
class TypeError < Exception; end
|
29
|
+
|
30
|
+
include Math
|
31
|
+
|
32
|
+
# Argument 'vectors' is three vectors with the order of a, b, c.
|
33
|
+
# If you want to make LatticeAxes instances from lattice constants,
|
34
|
+
# you should convert to axes with LatticeAxes.lc_to_axes
|
35
|
+
# 任意の向きのベクトルを渡しても、必ず triangulate する。
|
36
|
+
def initialize(vectors)
|
37
|
+
raise InitializeError, "#{vectors.inspect}" unless vectors.size == 3
|
38
|
+
|
39
|
+
if vectors.class == LatticeAxes
|
40
|
+
@axes = vectors
|
41
|
+
else
|
42
|
+
begin
|
43
|
+
vectors = self.class.triangulate(vectors)
|
44
|
+
rescue Vector3D::RangeError
|
45
|
+
raise InitializeError, "#{vectors.inspect}"
|
46
|
+
end
|
47
|
+
|
48
|
+
super(vectors)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
## Class methods
|
54
|
+
|
55
|
+
# Convert six lattice constants to three axes.
|
56
|
+
# Set true to the argument of 'righthand' if a assumed lattice has righthand axis system.
|
57
|
+
def self.lc_to_axes(lc, righthand = true)
|
58
|
+
raise ArgumentError if (lc.size != 6)
|
59
|
+
|
60
|
+
a = lc[0]
|
61
|
+
b = lc[1]
|
62
|
+
c = lc[2]
|
63
|
+
alpha = (2.0*PI) * lc[3] / 360.0 # radian
|
64
|
+
beta = (2.0*PI) * lc[4] / 360.0 # radian
|
65
|
+
gamma = (2.0*PI) * lc[5] / 360.0 # radian
|
66
|
+
|
67
|
+
v_c = Vector3D[0.0, 0.0, c]
|
68
|
+
v_b = Vector3D[0.0, b * Math::sin(alpha), b * Math::cos(alpha)]
|
69
|
+
v_a_z = a * Math::cos(beta)
|
70
|
+
v_a_y = (a * (Math::cos(gamma) - Math::cos(alpha) * Math::cos(beta)))/ Math::sin(alpha)
|
71
|
+
v_a_x = Math::sqrt(a**2 - v_a_y**2 - v_a_z**2)
|
72
|
+
v_a_x *= -1.0 if righthand == false
|
73
|
+
v_a = Vector3D[v_a_x, v_a_y, v_a_z]
|
74
|
+
return [v_a, v_b, v_c]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Convert three axes to six lattice constants.
|
78
|
+
def self.axes_to_lc(axes)
|
79
|
+
axes.collect!{|i| Vector3D[*i] }
|
80
|
+
a = axes[0].r
|
81
|
+
b = axes[1].r
|
82
|
+
c = axes[2].r
|
83
|
+
alpha = axes[1].angle_degree(axes[2])
|
84
|
+
beta = axes[2].angle_degree(axes[0])
|
85
|
+
gamma = axes[0].angle_degree(axes[1])
|
86
|
+
return [ a, b, c, alpha, beta, gamma ]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return true if the relation of vector order is righthand system.
|
90
|
+
def self.righthand?(axes)
|
91
|
+
axes.map! { |i| Vector3D[*i] }
|
92
|
+
return true if Vector3D.scalar_triple_product(*axes) > 0.0
|
93
|
+
return false
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return true if the relation of vector order is lefthand system.
|
97
|
+
def self.lefthand?(axes)
|
98
|
+
axes.map! { |i| Vector3D[*i] }
|
99
|
+
return true if Vector3D.scalar_triple_product(*axes) < 0.0
|
100
|
+
return false
|
101
|
+
end
|
102
|
+
|
103
|
+
# Convert three axes to three axes with rules below:
|
104
|
+
# c axis is along z axis in cartesian system.
|
105
|
+
# b axis is on y-z plane in cartesian system.
|
106
|
+
# Return an array of three Vector3D instances.
|
107
|
+
# This class does not convert righthand to lefthand system.
|
108
|
+
# The name of this method 'triangulate' originates from the
|
109
|
+
# matrix indicating the vectors being triangular matrix.
|
110
|
+
#
|
111
|
+
# クラスメソッドは廃止の方向で。
|
112
|
+
# vectors の数をチェックするのは initialize でやるべきことだろうし、
|
113
|
+
# LatticeAxes クラスインスタンス以外で triangulate を使う場面が想像できない。
|
114
|
+
#
|
115
|
+
def self.triangulate(vectors)
|
116
|
+
vectors.map! { |i| Vector3D[*i] }
|
117
|
+
raise InitializeError if self.dependent?(vectors)
|
118
|
+
lc = self.axes_to_lc(vectors)
|
119
|
+
righthand = self.righthand?(vectors)
|
120
|
+
return self.lc_to_axes(lc, righthand)
|
121
|
+
end
|
122
|
+
|
123
|
+
## Instance methods.
|
124
|
+
|
125
|
+
# Get lattice constants in six values.
|
126
|
+
def get_lattice_constants
|
127
|
+
return LatticeAxes.axes_to_lc(@axes)
|
128
|
+
end
|
129
|
+
|
130
|
+
def righthand?
|
131
|
+
self.class.righthand?(@axes)
|
132
|
+
end
|
133
|
+
|
134
|
+
def lefthand?
|
135
|
+
self.class.lefthand?(@axes)
|
136
|
+
end
|
137
|
+
|
138
|
+
# This class is obsoleted. [2011-12-22]
|
139
|
+
## Convert internal coordinates to cartesian coordinates.
|
140
|
+
## Return a Vector3DInternal class instance, which is not a cartesian vector.
|
141
|
+
#def internal2cartesian(internal_coord)
|
142
|
+
# Vector3DInternal[ *internal_coord ].to_v3d(axes)
|
143
|
+
#end
|
144
|
+
|
145
|
+
# This class is obsoleted. [2011-12-22]
|
146
|
+
## Convert cartesian coordinates to internal coordinates.
|
147
|
+
## Return a Vector3D class instance, which is a cartesian vector.
|
148
|
+
#def cartesian2internal(cartesian_coord)
|
149
|
+
# #pp cartesian_coord
|
150
|
+
# Vector3D[ *cartesian_coord ].to_v3di(axes)
|
151
|
+
#end
|
152
|
+
|
153
|
+
# Compare <other> LatticeAxes instance.
|
154
|
+
# <length_ratio> is tolerance of ratio in length of axes.
|
155
|
+
# <angle_tolerance> is tolerance of value in angle between axes.
|
156
|
+
def equal_in_delta?(other, length_ratio, angle_tolerance)
|
157
|
+
length_a = self .get_lattice_constants[0..2]
|
158
|
+
length_b = other.get_lattice_constants[0..2]
|
159
|
+
3.times do |i|
|
160
|
+
return false unless ((length_a[i] - length_b[i]).abs <= length_ratio)
|
161
|
+
end
|
162
|
+
|
163
|
+
angle_a = self .get_lattice_constants[3..5]
|
164
|
+
angle_b = other.get_lattice_constants[3..5]
|
165
|
+
3.times do |i|
|
166
|
+
return false unless ((angle_a[i] - angle_b[i]).abs <= angle_tolerance)
|
167
|
+
end
|
168
|
+
return true
|
169
|
+
end
|
170
|
+
|
171
|
+
def ==(other)
|
172
|
+
3.times do |i|
|
173
|
+
3.times do |j|
|
174
|
+
return false if self[i][j] != other[i][j]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
return true
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def triangulate
|
183
|
+
#self.class.triangulate(@axes)
|
184
|
+
# rotate(2, 2, 1)
|
185
|
+
# rotate(2, 0, 2)
|
186
|
+
# rotate(1, 2, 1)
|
187
|
+
end
|
188
|
+
|
189
|
+
#private
|
190
|
+
|
191
|
+
## 保持する全ての軸を回転する。
|
192
|
+
## 以下の index は Axes クラス保持している配列における index。
|
193
|
+
## target_index 回転の際の角度を決めるベクトルの
|
194
|
+
## self が保持する内部座標軸の index。
|
195
|
+
## center_axis_index 回転の中心軸のベクトルの index in x, y, z。
|
196
|
+
## plane_axis_index) 回転の目的地となる平面を、中心軸と共に構成するベクトルの index
|
197
|
+
## in x, y, z。
|
198
|
+
#def rotate(target_index, center_axis_index, plane_axis_index)
|
199
|
+
# theta =
|
200
|
+
|
201
|
+
# axes[target_index]
|
202
|
+
#
|
203
|
+
# HERE
|
204
|
+
|
205
|
+
# self.each do |vector|
|
206
|
+
# vector
|
207
|
+
# end
|
208
|
+
#end
|
209
|
+
|
210
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pp"
|
4
|
+
require "crystalcell/cell.rb"
|
5
|
+
|
6
|
+
# Class for crystal cell with periodic boundary.
|
7
|
+
# Coordinates of atoms are kept in the region of 0 <= x_i < 1 of internal coordinate.
|
8
|
+
# Written by Ippei Kishida [2010-12-19].
|
9
|
+
class PeriodicCell < Cell
|
10
|
+
class TypeError < Exception; end
|
11
|
+
|
12
|
+
def initialize( *args )
|
13
|
+
super( *args )
|
14
|
+
reset_positions_inside
|
15
|
+
end
|
16
|
+
|
17
|
+
#ある内部座標から、別のある座標とそれと周期的に等価な座標への距離が
|
18
|
+
#tolerance 以下のものを探し、条件を満たすセルの方向を配列にまとめて返す。
|
19
|
+
#
|
20
|
+
#内部的に、一度 0以上1未満の座標に変換しようかと思ったが、
|
21
|
+
#境界付近で問題が生じうる。
|
22
|
+
#
|
23
|
+
#周囲 27 セルしか考慮しない。
|
24
|
+
#美しさを求めるならば tolerance を完全に含む大きさのスーパーセルにすべきだが、
|
25
|
+
#実装が面倒なわりに滅多に使われることがなさそうなので。
|
26
|
+
#出力の順番は、上位の要素が小さなものから順。
|
27
|
+
#(距離の短いものから順という考え方もなくはないが。)
|
28
|
+
#
|
29
|
+
#tolerance を省略( = nil を与えれば )、27セルの中にある全ての方向を返す。
|
30
|
+
def directions_within_distance( pos0, pos1, tolerance = nil )
|
31
|
+
if pos0.class != Vector3DInternal
|
32
|
+
raise TypeError, "pos0 is not a Vector3DInternal class instance."
|
33
|
+
end
|
34
|
+
if pos1.class != Vector3DInternal
|
35
|
+
raise TypeError, "pos1 is not a Vector3DInternal class instance."
|
36
|
+
end
|
37
|
+
|
38
|
+
pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di
|
39
|
+
pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di
|
40
|
+
|
41
|
+
#pp pos0
|
42
|
+
|
43
|
+
results = []
|
44
|
+
(-1).upto(1) do |x|
|
45
|
+
(-1).upto(1) do |y|
|
46
|
+
(-1).upto(1) do |z|
|
47
|
+
shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ]
|
48
|
+
d = distance( pos0, ( pos1 + shift))
|
49
|
+
#tolerance が nil ならば距離判定なしで登録。
|
50
|
+
#tolerance が非 nil ならばその値で距離判定して登録。
|
51
|
+
if ( ( ! tolerance ) || ( d < tolerance ) )
|
52
|
+
results << [ x, y, z]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
return results
|
58
|
+
end
|
59
|
+
|
60
|
+
# 周期性を考慮して、
|
61
|
+
# 1個目の内部座標( pos0 ) から見て、
|
62
|
+
# 一番近い 2個目の内部座標に等価なサイトの属するセルの方向を返す。
|
63
|
+
# 返り値は Vector3DInternal で
|
64
|
+
# 内部座標が 0.0 <= r < 1.0 になっていることを前提とする。
|
65
|
+
# (なお、この外側であってもこの範囲に入るように周期移動する。)
|
66
|
+
# 座標 0.0 付近の境界は計算誤差の為におかしなことになり易いので注意が必要。
|
67
|
+
#
|
68
|
+
# NOTE: nearest_direction というメソッド名はどうかと思う。
|
69
|
+
# nearest_lattice_vector とか?
|
70
|
+
# 良い名前があれば、リファクタリング対象。
|
71
|
+
#
|
72
|
+
# NOTE: 0〜1の外側なら内側に入れる、という処理は混乱し易い。
|
73
|
+
# 例外にした方が良いのではないだろうか。
|
74
|
+
def nearest_direction( pos0, pos1 )
|
75
|
+
if pos0.class != Vector3DInternal
|
76
|
+
raise TypeError,
|
77
|
+
"pos0.class is not a Vector3DInternal"
|
78
|
+
end
|
79
|
+
if pos1.class != Vector3DInternal
|
80
|
+
raise TypeError,
|
81
|
+
"pos1.class is not a Vector3DInternal"
|
82
|
+
end
|
83
|
+
|
84
|
+
pos0 = pos0.map{ |i| i - i.floor }.to_a.to_v3di
|
85
|
+
pos1 = pos1.map{ |i| i - i.floor }.to_a.to_v3di
|
86
|
+
|
87
|
+
#set first value
|
88
|
+
min = distance( pos0, pos1 )
|
89
|
+
result = Vector3DInternal[ 0, 0, 0 ]
|
90
|
+
|
91
|
+
#find
|
92
|
+
(-1).upto(1) do |x|
|
93
|
+
(-1).upto(1) do |y|
|
94
|
+
(-1).upto(1) do |z|
|
95
|
+
shift = Vector3DInternal[ x.to_f, y.to_f, z.to_f ]
|
96
|
+
d = (pos0 - (pos1 + shift)).to_v3d(@axes).r
|
97
|
+
|
98
|
+
if d < min
|
99
|
+
result = Vector3DInternal[ x, y, z]
|
100
|
+
min = d
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
return result
|
106
|
+
end
|
107
|
+
|
108
|
+
#周期性を考慮し、2つの地点間の最短距離を返す。
|
109
|
+
#pos0, pos1 には内部座標を指定する。
|
110
|
+
#内部的には周囲 3x3x3 のセル中の27地点のうち最も近いものを得る。
|
111
|
+
#旧 minimum_distance
|
112
|
+
def nearest_distance( pos0, pos1 )
|
113
|
+
[pos0, pos1].each_with_index do |pos, index|
|
114
|
+
unless pos.class == Vector3DInternal
|
115
|
+
raise TypeError,
|
116
|
+
"#{index} th argument: #{pos.inspect}, #{pos.class}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
pos0 = Vector3DInternal[* pos0.map{ |i| i - i.floor }]
|
121
|
+
pos1 = Vector3DInternal[* pos1.map{ |i| i - i.floor }]
|
122
|
+
direction = nearest_direction( pos0, pos1 )
|
123
|
+
3.times do |i|
|
124
|
+
pos1[ i ] += direction[ i ]
|
125
|
+
end
|
126
|
+
distance( pos0, pos1 )
|
127
|
+
end
|
128
|
+
|
129
|
+
#条件にマッチした原子間距離を見つけて、端点の内部座標の組を要素とする配列を返す。
|
130
|
+
#返される配列は3重配列になる。
|
131
|
+
#e.g.,
|
132
|
+
# [
|
133
|
+
# [ [0.1, 0.1, 0.1], [0.2, 0.2, 0.2] ],
|
134
|
+
# [ [0.1, 0.1, 0.1], [0.3, 0.4, 0.5] ],
|
135
|
+
# [ [0.9, 0.9, 0.9], [0.8, 0.8, 0.8] ]
|
136
|
+
# ]
|
137
|
+
#@bonds への登録はしない。
|
138
|
+
#elem0, elem1 は bond の両端の原子の組み合わせを指定。
|
139
|
+
#elem0, elem1 はメソッド内部で反転したものもチェックするので、順序が反対でも同じ結果になる。
|
140
|
+
#O-O のように同じ元素を指定することもでき、
|
141
|
+
#この場合向きの異なる 2つの bond が重複したりしない。
|
142
|
+
#d_min, d_max は距離の範囲を指定する。
|
143
|
+
#境界値そのものが問題になることは少ないだろう。
|
144
|
+
#d_min <= d <= d_max(以上、以下) としておくが、計算誤差のためにこれはほぼ無意味だ。
|
145
|
+
#見つけて配列として返すだけで、登録はしない。
|
146
|
+
#
|
147
|
+
#以下の案は棄却。
|
148
|
+
# - いかなる元素でもマッチ。
|
149
|
+
# - 距離の上限を無効。
|
150
|
+
#理由は、
|
151
|
+
# - プログラムが煩雑になる。
|
152
|
+
# - 引数の型が統一されない。
|
153
|
+
# - ほとんど使用する機会がなさそう。
|
154
|
+
# 大抵は元素を指定するし、元素を指定しない bond を描く状況は考えにくい。
|
155
|
+
# これが必要ならメソッドの外で組み合わせを作ってそれぞれで呼べば良い。
|
156
|
+
# - 大抵は距離の上限を定めるし、上限なしではハリネズミになるだけ。
|
157
|
+
# また、プログラム上は距離の上限を 3x3x3 スーパーセルに制限したりするが、
|
158
|
+
# 論理的に距離の上限なしってのは無限のセルを対象にすることになり、整合性がとれない。
|
159
|
+
def find_bonds( elem0, elem1, d_min, d_max )
|
160
|
+
results = []
|
161
|
+
atoms.each do |inner_atom|
|
162
|
+
atoms_in_supercell( -1, 1, -1, 1, -1, 1 ).each do |outer_atom|
|
163
|
+
#元素の種類による判定
|
164
|
+
ie = inner_atom.element
|
165
|
+
oe = outer_atom.element
|
166
|
+
next unless ( (( ie == elem0 ) && ( oe == elem1 )) || (( ie == elem1 ) && ( oe == elem0 ))) #elem0, elem1 と同じ構成
|
167
|
+
|
168
|
+
#距離による判定
|
169
|
+
ip = inner_atom.position
|
170
|
+
op = outer_atom.position
|
171
|
+
next if distance( ip, op ) < d_min
|
172
|
+
next if distance( ip, op ) > d_max
|
173
|
+
next if distance( ip, op ) == 0.0 #同一サイト判定
|
174
|
+
|
175
|
+
#重複判定, 正順か逆順が既に含まれていれば無視。
|
176
|
+
next if ( results.include?( [ ip, op ] ) || results.include?( [ op, ip ] ) )
|
177
|
+
|
178
|
+
results << [ ip, op ]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
return results
|
182
|
+
end
|
183
|
+
|
184
|
+
#引数 distance 以下の間の距離にある原子のペアを列挙して返す。
|
185
|
+
#この際、あくまで unique なものを返し、A-B があれば B-A はリストに含まれない。
|
186
|
+
#
|
187
|
+
#返す形式は以下のような形式。
|
188
|
+
#[ [0, 1, [0, 0, 0], [0, 1, [0, 0, 1] ]
|
189
|
+
#最初の 0, 1 はそれぞれ配列 @atoms 内の番号で、
|
190
|
+
#0番目の原子からから同じセル内の 1番目の原子
|
191
|
+
#0番目の原子からから[0,0,1]方向に隣接するセル内の 1番目の原子を意味する。
|
192
|
+
#起点となる原子の番号と(上記 0 )と目的地の原子の番号(上記 1)が同じこともありうる。
|
193
|
+
#異なる場合は起点となる原子の番号が、目的地の原子の番号より若くなる。
|
194
|
+
#
|
195
|
+
#自分と同じ番号の場合は、同じセルを除外する。
|
196
|
+
#すなわち、[0, 0, [0, 0, 0] や [1, 1, [0, 0, 0] は含まれない。
|
197
|
+
def pairs_within_distance( distance )
|
198
|
+
results = []
|
199
|
+
n_atom = @atoms.size
|
200
|
+
n_atom.times do |i|
|
201
|
+
pos_i = @atoms[i].position
|
202
|
+
|
203
|
+
n_atom.times do |j|
|
204
|
+
pos_j = @atoms[j].position
|
205
|
+
|
206
|
+
directions_within_distance( pos_i, pos_j, distance ).each do |dir|
|
207
|
+
#next if ( i==j && dir==[0,0,0] ) #距離0の自分自身。下の条件に含まれる。
|
208
|
+
next if ( dir==[0,0,0] && i >= j ) #同じセル内は番号が若い方からのみ。
|
209
|
+
results << [ i, j, dir ]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
return results
|
214
|
+
end
|
215
|
+
|
216
|
+
# Functions as like Cell.add_atom, but the coordinates are converted to the region of 0 <= x_i < 1.
|
217
|
+
def add_atom( *args )
|
218
|
+
super( *args )
|
219
|
+
reset_positions_inside
|
220
|
+
end
|
221
|
+
|
222
|
+
# Functions as like Cell.rotate!, but the coordinates are converted to the region of 0 <= x_i < 1.
|
223
|
+
# Dependent function 'rotate' functions the same.
|
224
|
+
def rotate!( *args )
|
225
|
+
super( *args )
|
226
|
+
reset_positions_inside
|
227
|
+
end
|
228
|
+
|
229
|
+
# Functions as like Cell.translate!, but the coordinates are converted to the region of 0 <= x_i < 1.
|
230
|
+
# Dependent function 'translate' functions the same.
|
231
|
+
def translate!( *args )
|
232
|
+
super( *args )
|
233
|
+
reset_positions_inside
|
234
|
+
end
|
235
|
+
|
236
|
+
# Return a new instance converted to Cell class.
|
237
|
+
def to_cell
|
238
|
+
tmp = Cell.new( @axes.to_a )
|
239
|
+
tmp.comment = self.comment
|
240
|
+
@atoms.each do |atom|
|
241
|
+
tmp.add_atom(atom)
|
242
|
+
end
|
243
|
+
return tmp
|
244
|
+
end
|
245
|
+
|
246
|
+
undef center_of_atoms
|
247
|
+
|
248
|
+
# superclass の inverse_axis! を行ったあと、
|
249
|
+
# 原子の座標をセル内部に移す。
|
250
|
+
def inverse_axis!( axis_id )
|
251
|
+
result = Marshal.load( Marshal.dump( self ) )
|
252
|
+
super( axis_id )
|
253
|
+
reset_positions_inside
|
254
|
+
end
|
255
|
+
|
256
|
+
private
|
257
|
+
|
258
|
+
# Reset internal coordinates of all atoms inside the region of 0 <= x_i < 1.
|
259
|
+
def reset_positions_inside
|
260
|
+
@atoms.each do |atom|
|
261
|
+
coords = atom.position
|
262
|
+
atom.set_position(coords.map {|coord| coord - coord.floor}.to_a.to_v3di)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
class Cell
|
269
|
+
#require "Crystal/PeriodicCell.rb"
|
270
|
+
# Return a new instance converted to PeriodicCell class.
|
271
|
+
def to_pcell
|
272
|
+
atoms = Marshal.load(Marshal.dump(@atoms))
|
273
|
+
result = PeriodicCell.new( @axes.to_a, atoms )
|
274
|
+
result.comment = self.comment
|
275
|
+
return result
|
276
|
+
end
|
277
|
+
end
|
data/lib/crystalcell.rb
ADDED
File without changes
|
data/test/helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
require 'crystalcell'
|
15
|
+
|
16
|
+
class Test::Unit::TestCase
|
17
|
+
end
|