crysna 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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/CHANGES +24 -0
  4. data/Gemfile +26 -0
  5. data/Gemfile.lock +91 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +19 -0
  8. data/Rakefile +53 -0
  9. data/VERSION +1 -0
  10. data/bin/checkmodel +66 -0
  11. data/bin/collectcell +92 -0
  12. data/bin/fitframe +68 -0
  13. data/bin/occupiedpolyhedralsite +96 -0
  14. data/bin/occupiedscattersite +77 -0
  15. data/bin/site2poscar +66 -0
  16. data/bin/site2pov +30 -0
  17. data/bin/sitecombination +69 -0
  18. data/bin/sitemigration +80 -0
  19. data/bin/sitemigrationdistance +87 -0
  20. data/bin/siteoperation +109 -0
  21. data/bin/sitesingle +36 -0
  22. data/bin/siteuniq +32 -0
  23. data/bin/symidsite +65 -0
  24. data/bin/transitcell +46 -0
  25. data/crysna.gemspec +219 -0
  26. data/lib/crysna.rb +26 -0
  27. data/lib/crysna/atom.rb +97 -0
  28. data/lib/crysna/cell.rb +314 -0
  29. data/lib/crysna/frameatom.rb +13 -0
  30. data/lib/crysna/frameinterstitialcell.rb +309 -0
  31. data/lib/crysna/interstitialatom.rb +13 -0
  32. data/lib/crysna/modelstructure.rb +333 -0
  33. data/lib/crysna/optionmanager.rb +177 -0
  34. data/lib/crysna/site.rb +35 -0
  35. data/lib/crysna/siteconfiguration.rb +26 -0
  36. data/lib/crysna/sitenamelabeledcell.rb +220 -0
  37. data/lib/crysna/siteoperation.rb +56 -0
  38. data/lib/crysna/sitewithposition.rb +24 -0
  39. data/lib/crysna/transitionfinder.rb +448 -0
  40. data/lib/crysna/transitionfinder/cell.rb +144 -0
  41. data/lib/crysna/transitionfinder/cellmanager.rb +129 -0
  42. data/lib/crysna/transitionfinder/edge.rb +54 -0
  43. data/test/.gitignore +1 -0
  44. data/test/cell_orig/POSCAR +17 -0
  45. data/test/cell_orig/model.yaml +122 -0
  46. data/test/collectcells/.gitignore +2 -0
  47. data/test/collectcells/model.yaml +154 -0
  48. data/test/collectcells/nooutcar/minexpconfiguration.yaml +22 -0
  49. data/test/collectcells/normal-higher/OUTCAR +2406 -0
  50. data/test/collectcells/normal-higher/minexpconfiguration.yaml +22 -0
  51. data/test/collectcells/normal-lower/OUTCAR +2406 -0
  52. data/test/collectcells/normal-lower/minexpconfiguration.yaml +22 -0
  53. data/test/collectcells/normal/OUTCAR +2406 -0
  54. data/test/collectcells/normal/minexpconfiguration.yaml +22 -0
  55. data/test/collectcells/normalB/OUTCAR +2406 -0
  56. data/test/collectcells/normalB/minexpconfiguration.yaml +22 -0
  57. data/test/collectcells/unfinished/OUTCAR +40702 -0
  58. data/test/collectcells/unfinished/minexpconfiguration.yaml +22 -0
  59. data/test/collectcells/unidentified/OUTCAR +3541 -0
  60. data/test/collectcells/unidentified/minexpconfiguration.yaml +2 -0
  61. data/test/fitmodelstructure/.gitignore +1 -0
  62. data/test/fitmodelstructure/AgI/CONTCAR +17 -0
  63. data/test/fitmodelstructure/AgI/fitmodelstructure.log +1161 -0
  64. data/test/fitmodelstructure/AgI/model.yaml +45 -0
  65. data/test/fitmodelstructure/normal/CONTCAR +17 -0
  66. data/test/fitmodelstructure/normal/fitmodelstructure.log +5063 -0
  67. data/test/fitmodelstructure/normal/model.yaml +122 -0
  68. data/test/fitmodelstructure/unidentified/CONTCAR +44 -0
  69. data/test/fitmodelstructure/unidentified/fitmodelstructure.log +8833 -0
  70. data/test/fitmodelstructure/unidentified/model.yaml +154 -0
  71. data/test/helper.rb +17 -0
  72. data/test/identifypolyhedralsites/.gitignore +1 -0
  73. data/test/identifypolyhedralsites/identifyatomsites.log +333 -0
  74. data/test/identifypolyhedralsites/normal/fitmodelstructure.yaml +60 -0
  75. data/test/identifypolyhedralsites/normal/model.yaml +122 -0
  76. data/test/identifypolyhedralsites/unidentified/fitmodelstructure.yaml +2 -0
  77. data/test/identifypolyhedralsites/unidentified/model.yaml +154 -0
  78. data/test/identifypolyhedralsites/volumemismatch/fitmodelstructure.yaml +101 -0
  79. data/test/identifypolyhedralsites/volumemismatch/model.yaml +154 -0
  80. data/test/identifyscattersites/CONTCAR +17 -0
  81. data/test/identifyscattersites/POSCAR +12 -0
  82. data/test/identifyscattersites/fitmodelstructure.log +1 -0
  83. data/test/identifyscattersites/fitmodelstructure.yaml +0 -0
  84. data/test/identifyscattersites/identifyscattersites.yaml +5 -0
  85. data/test/identifyscattersites/model.yaml +45 -0
  86. data/test/minexpconfiguration/.gitignore +1 -0
  87. data/test/minexpconfiguration/collective/AgI/.gitignore +2 -0
  88. data/test/minexpconfiguration/collective/AgI/siteoperations.yaml +51265 -0
  89. data/test/minexpconfiguration/collective/AgI/sitesingle.yaml +15 -0
  90. data/test/minexpconfiguration/collective/AgI/test.sh +2 -0
  91. data/test/minexpconfiguration/normal/identifysites.yaml +22 -0
  92. data/test/minexpconfiguration/normal/minexpconfiguration.log +0 -0
  93. data/test/minexpconfiguration/normal/siteoperations.yaml +1793 -0
  94. data/test/minexpconfiguration/unidentified/identifysites.yaml +2 -0
  95. data/test/minexpconfiguration/unidentified/siteoperations.yaml +1793 -0
  96. data/test/sitecombination/initsites.yaml +7 -0
  97. data/test/sitecombination/initsites_test2.yaml +8 -0
  98. data/test/sitecombination/sitecombination.yaml +29 -0
  99. data/test/siteconfiguration/elements-sitenames.yaml +2 -0
  100. data/test/siteconfiguration/latticeaxes.yaml +3 -0
  101. data/test/siteconfiguration/sitenames-coordinates.yaml +4 -0
  102. data/test/sitemigrationsdistance/model.yaml +45 -0
  103. data/test/siteoperations/.gitignore +2 -0
  104. data/test/siteoperations/model.yaml +43 -0
  105. data/test/siteoperations/symmetryoperations.yaml +1441 -0
  106. data/test/sitesingle/.gitignore +1 -0
  107. data/test/sitesingle/sitecombination.yaml +29 -0
  108. data/test/siteuniq/minexpconfiguration.yaml +15 -0
  109. data/test/siteuniq/siteuniq.yaml +8 -0
  110. data/test/test_atom.rb +206 -0
  111. data/test/test_cell.rb +604 -0
  112. data/test/test_commands.rb +340 -0
  113. data/test/test_crystana.rb +7 -0
  114. data/test/test_frameatom.rb +22 -0
  115. data/test/test_frameinterstitialcell.rb +939 -0
  116. data/test/test_interstitialatom.rb +22 -0
  117. data/test/test_modelstructure.rb +807 -0
  118. data/test/test_optionmanager.rb +172 -0
  119. data/test/test_site.rb +40 -0
  120. data/test/test_siteconfiguration.rb +29 -0
  121. data/test/test_sitenamelabeledcell.rb +528 -0
  122. data/test/test_siteoperation.rb +79 -0
  123. data/test/test_sitewithposition.rb +20 -0
  124. data/test/test_transitionfinder.rb +432 -0
  125. data/test/transitcell/.gitignore +2 -0
  126. data/test/transitcell/collectcells.yaml +51 -0
  127. data/test/transitcell/sitemigrations.yaml +8 -0
  128. data/test/transitcell/siteoperations.yaml +17 -0
  129. data/test/transitcell/transitcell.log +1342 -0
  130. data/test/transitionfinder/collectcells.yaml +81 -0
  131. data/test/transitionfinder/sitemigrations.yaml +33 -0
  132. data/test/transitionfinder/siteoperations.yaml +16 -0
  133. data/test/transitionfinder/test_cell.rb +287 -0
  134. data/test/transitionfinder/test_cellmanager.rb +185 -0
  135. data/test/transitionfinder/test_edge.rb +49 -0
  136. data/test/uniquesitesgenerator/elements-sitenames.yaml +1 -0
  137. data/test/uniquesitesgenerator/siteoperations.yaml +9 -0
  138. metadata +406 -0
@@ -0,0 +1,309 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ #require "crystal/cell2.rb"
5
+ #require "mathextension/octahedron.rb"
6
+ #require "mathextension/tetrahedron.rb"
7
+ #require "mathextension/vector3d.rb"
8
+ #require "mathextension/vector3dinternal.rb"
9
+ #require "crysna/frameatom.rb"
10
+ #require "crysna/interstitialatom.rb"
11
+ #require "crysna/sitenamelabeledcell.rb"
12
+ #require "progressbar"
13
+
14
+ #
15
+ # Cell modeled by frame atoms and interstitial atoms.
16
+ # All atoms must be FrameAtom or InterstitialAtom class instance,
17
+ # not CrystalCell::Atom class instance.
18
+ #
19
+ #class FrameInterstitialCell < CrystalCell::PeriodicCell
20
+ class FrameInterstitialCell < CrystalCell::Cell
21
+
22
+ class TypeError < Exception; end
23
+ class NoMatchError < Exception; end
24
+
25
+ # Argument 'atoms' must be Array of atoms
26
+ # whose class is FrameAtom or InterstitialAtom.
27
+ # If 'atoms' includes CrystalCell::Atom class instance,
28
+ # raise exception 'FrameInterstitialCell::TypeError'
29
+ def initialize(axes, atoms = [])
30
+ atoms.each_with_index do |atom, i|
31
+ unless (atom.is_a?(FrameAtom) || (atom.is_a?(InterstitialAtom)))
32
+ raise TypeError,
33
+ "'atoms' includes #{atom.class} at #{i}."
34
+ end
35
+ end
36
+ super(axes, atoms)
37
+ end
38
+
39
+ # サイト情報を参照して、骨格原子が最適になるように
40
+ # 48個の変換操作・並進移動を然るべく施したセル( self.class のインスタンス )を返す。
41
+ # 非破壊的
42
+ # 引数 sites はサイト情報を入れたハッシュ。キーがサイト名、値がサイトの内部座標。
43
+ # サイトと原子が一対一対応する候補が見つからない場合は
44
+ # 例外 FrameInterstitialCell::NoMatchError を返す。
45
+ # 原子の順番は非保存。
46
+ #def most_matching_cell_pair(sites, log_io )
47
+ def optimized_cell( sites, log_io )
48
+ # 48個のセルに対し、サイト数ごとに roughly translate したセルを作る。
49
+ # サイト数が 8 ならば 48*8 個の Array になる。
50
+ cells = equivalent_cells.map{ |cell|
51
+ tmp = []
52
+ sites.each do |name, vec|
53
+ translation_rough = vec - cell.frame_atoms[0].position
54
+ t_cell = cell.translate(translation_rough)
55
+ t_cell.comment += sprintf(
56
+ "; Rough translation: (% 7.5f, % 7.5f, % 7.5f), 0th atom of frame to #{name}",
57
+ * (translation_rough)
58
+ )
59
+ tmp << t_cell
60
+ end
61
+ tmp
62
+ }.flatten
63
+
64
+ p_io = $stderr
65
+ p_io = File.open(File::NULL, "w") if $test == true
66
+ pbar = ProgressBar.new("FrameInterstitialCell::optimized_cell", cells.size, p_io)
67
+
68
+ # 操作と評価を全てに対して行う。
69
+ min_cell = nil
70
+ min_sum = nil
71
+ cells.each do |cell|
72
+ pbar.inc
73
+ # SiteNameLabeledCell に変換できない cell が含まれるため、
74
+ # min_by メソッドは使えない。
75
+
76
+ log_io.puts(" #{cell.comment.sub("; ", " \\\n ")}")
77
+ log_io.puts(" atom coordinates:")
78
+ cell.atoms.each_with_index do |atom, index|
79
+ log_io.printf(" (% 7.5f, % 7.5f, % 7.5f), Element:%s, %02i-th atom.\n",
80
+ * (atom.position), atom.element, index )
81
+ end
82
+
83
+ f_cell = cell.frame_cell
84
+ i_cell = cell.interstitial_cell.to_pcell.to_cell.to_ficell([])
85
+
86
+ # SiteNameLabeledCell に変換
87
+ begin
88
+ snlcell = f_cell.to_snlcell(sites)
89
+ rescue SiteNameLabeledCell::InitializeError
90
+ log_io.puts " Not correspond atoms for sitenames."
91
+ log_io.puts
92
+ next # 一対一対応しない場合、無視する。
93
+ end
94
+
95
+ # 原子の重心とサイトの重心が一致するように
96
+ # 精密化した translation を行う。
97
+ translation_prec =
98
+ snlcell.center_of_sites - snlcell.center_of_atoms
99
+ snlcell.translate!(translation_prec)
100
+ i_cell.translate!(translation_prec)
101
+ [snlcell, i_cell].each do |cell|
102
+ cell.comment += sprintf(
103
+ "; Precise translation: (% 7.5f, % 7.5f, % 7.5f)\n",
104
+ * (translation_prec)
105
+ )
106
+ end
107
+
108
+ # log 出力
109
+ log_io.puts(" Found frame atoms:")
110
+ frame_atoms.each do |atom|
111
+ log_io.printf(" (% 7.5f, % 7.5f % 7.5f), #{atom.name}\n",
112
+ * atom.position
113
+ )
114
+ end
115
+
116
+ # 二乗和で評価
117
+ sum = snlcell.sum_square_distances
118
+ log_io.printf( " Sum of square distances: %8.4f.\n\n", sum )
119
+ if (min_sum == nil) || (sum < min_sum)
120
+ min_sum = sum
121
+ min_cell = snlcell.to_cell.to_ficell.unite(i_cell)
122
+ end
123
+ end
124
+ pbar.finish
125
+
126
+ unless min_cell.class == FrameInterstitialCell
127
+ raise NoMatchError,
128
+ "Class of min_cell is #{min_cell.class}"
129
+ end
130
+
131
+ log_io.puts( " Chosen cell: #{min_cell.comment}")
132
+ log_io.puts( " sum of square distances: #{min_sum}")
133
+
134
+ # サイト情報をここで全部書き出しておく。
135
+ log_io.puts(" Atoms:")
136
+ min_cell.atoms.each do |atom|
137
+ log_io.printf(
138
+ " (% 7.5f, % 7.5f, % 7.5f), element %s, %s.\n",
139
+ * atom.position,
140
+ atom.element,
141
+ atom.name,
142
+ )
143
+ end
144
+ #logs.each do |line|
145
+ # log_io.puts line
146
+ #end
147
+ log_io.puts
148
+
149
+ return min_cell
150
+ end
151
+
152
+ # @atoms のうち、 FrameAtom のものだけをまとめて Array にして返す。
153
+ def frame_atoms
154
+ @atoms.select{|atom| atom.class == FrameAtom}
155
+ end
156
+
157
+ # @atoms のうち、 InterstitialAtom のものだけをまとめて Array にして返す。
158
+ def interstitial_atoms
159
+ @atoms.select{|atom| atom.class == InterstitialAtom}
160
+ end
161
+
162
+ # @atoms のうち、 FrameAtom のものだけを含んだ
163
+ # FrameInterstitialCell を返す。
164
+ def frame_cell
165
+ result = FrameInterstitialCell.new(@axes, frame_atoms)
166
+ result.comment = self.comment
167
+ return result
168
+ end
169
+
170
+ # @atoms のうち、 InterstitialAtom のものだけを含んだ
171
+ # FrameInterstitialCell を返す。
172
+ def interstitial_cell
173
+ result = FrameInterstitialCell.new(@axes, interstitial_atoms)
174
+ result.comment = self.comment
175
+ return result
176
+ end
177
+
178
+ # 保持する原子群について、
179
+ # 元素ごとに分類し、元素を鍵とするハッシュとする。
180
+ # ハッシュの値はその元素の原子それぞれに付けられた名前で、ソート済の配列。
181
+ def elements_names
182
+ results = {}
183
+ @atoms.each do |atom|
184
+ elem = atom.element
185
+ if results[elem]
186
+ results[elem] << atom.name
187
+ else
188
+ results[elem] = [atom.name]
189
+ end
190
+ end
191
+ results.each_value do |val|
192
+ val.sort!
193
+ end
194
+ return results
195
+ end
196
+
197
+ private
198
+
199
+ # セルに対して格子ベクトルの反転・交換操作を組み合わせた結果生じる
200
+ # 全 48 個のセルを配列にして返す。
201
+ # 鏡像操作は含まれない。
202
+ # 交換 → 反転 の順で操作を施す。
203
+ # 返される配列内部の順序は、以下の組み合わせ。
204
+ #
205
+ # 範囲 a反転 b反転 c反転
206
+ # 00-05 x x x
207
+ # 06-11 o x x
208
+ # 12-17 x o x
209
+ # 18-23 o o x
210
+ # 24-29 x x o
211
+ # 30-35 o x o
212
+ # 36-41 x o o
213
+ # 42-47 o o o
214
+ #
215
+ # 各々の範囲中では、6要素が以下の順序。
216
+ # 00: a,b,c(a,b 交換→ a,b交換)(交換なし)
217
+ # 01: b,c,a(a,b 交換→ b,c交換)
218
+ # 02: c,a,b(a,b 交換→ c,a交換)
219
+ # 03: b,a,c(a,b 交換)
220
+ # 04: a,c,b(b,c 交換)
221
+ # 05: c,b,a(c,a 交換)
222
+ def equivalent_cells
223
+ results = []
224
+ gen_size = 48
225
+
226
+ gen_size.times do |i|
227
+ cell = Marshal.load(Marshal.dump(self))
228
+ comments = [
229
+ sprintf("Equivalent cell No.%02d: Original", i)
230
+ ]
231
+ case i % 6
232
+ when 0
233
+ #"do nothing"
234
+ when 1
235
+ cell.exchange_axes!([0,1])
236
+ cell.exchange_axes!([1,2])
237
+ comments << "Axes exchanged(a,b,c -> b,c,a)"
238
+ when 2
239
+ cell.exchange_axes!([0,1])
240
+ cell.exchange_axes!([2,0])
241
+ comments << "Axes exchanged(a,b,c -> c,a,b)"
242
+ when 3
243
+ cell.exchange_axes!([0,1])
244
+ comments << "Axes exchanged(a,b,c -> b,a,c)"
245
+ when 4
246
+ cell.exchange_axes!([1,2])
247
+ comments << "Axes exchanged(a,b,c -> a,c,b)"
248
+ when 5
249
+ cell.exchange_axes!([2,0])
250
+ comments << "Axes exchanged(a,b,c -> c,b,a)"
251
+ end
252
+
253
+ if ((i / 6) % 2 == 1)
254
+ cell.inverse_axis!(0)
255
+ comments << "Axis 'a' inversed"
256
+ end
257
+ if ((i / 12) % 2 == 1)
258
+ cell.inverse_axis!(1)
259
+ comments << "Axis 'b' inversed"
260
+ end
261
+ if ((i / 24) % 2 == 1)
262
+ cell.inverse_axis!(2)
263
+ comments << "Axis 'c' inversed"
264
+ end
265
+
266
+ #if ((i / 48) % 2 == 1)
267
+ # cell.reflect!
268
+ # comments << "Reflected"
269
+ #end
270
+
271
+ cell.comment = comments.join(" -> ") + "."
272
+ results << cell
273
+ end
274
+ return results
275
+ end
276
+
277
+ end
278
+
279
+ class CrystalCell::Cell
280
+ # frame_atom_ids で 骨格原子になるべき原子の indices を与える。
281
+ # frame_atom_ids が与えなければ、全ての原子が FrameAtom になる。
282
+ # 全ての原子を InterstitialAtom にするには、空配列を与えれば良い。
283
+ def to_ficell(frame_atom_ids = nil)
284
+ result = FrameInterstitialCell.new(@axes)
285
+ result.comment = self.comment
286
+ if frame_atom_ids
287
+ @atoms.each_with_index do |atom, i|
288
+ if frame_atom_ids.include?(i)
289
+ added_atom = FrameAtom.new(
290
+ atom.element, atom.position, atom.name, atom.movable_flags
291
+ )
292
+ else
293
+ added_atom = InterstitialAtom.new(
294
+ atom.element, atom.position, atom.name, atom.movable_flags
295
+ )
296
+ end
297
+ result.add_atom(added_atom)
298
+ end
299
+ else
300
+ @atoms.each_with_index do |atom, i|
301
+ added_atom = FrameAtom.new(
302
+ atom.element, atom.position, atom.name, atom.movable_flags
303
+ )
304
+ result.add_atom(added_atom)
305
+ end
306
+ end
307
+ return result
308
+ end
309
+ end
@@ -0,0 +1,13 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ #require "crystal/atom2.rb"
5
+
6
+ #
7
+ # Class for frame atom.
8
+ # This class is the same as CrystalCell::Atom, except for the class name.
9
+ # This class is assumed to be used in FrameInterstitialCell.
10
+ #
11
+ class InterstitialAtom < CrystalCell::Atom
12
+ end
13
+
@@ -0,0 +1,333 @@
1
+ #! /usr/bin/env ruby
2
+ #coding: utf-8
3
+
4
+ require "yaml"
5
+
6
+ #サイト情報などを抽象化したモデル構造を扱うクラス。
7
+ #内部的に、格子定数 1.0 の cubic なセルと見做して処理している。
8
+ #initialize 時点で作った以降は内部データが変更されないことを前提としている。
9
+ #(@polyhedra を作っておいた方が速そうとか思ったが、テストしてみたら変わらんかった。)
10
+ #
11
+ #体積関係のエラーチェックは、ininialize 時には行わない。
12
+ class Crysna::ModelStructure #< CrystalCell::Cell
13
+ ELEMENT = 0
14
+ TOLERANCE = 1.0E-10
15
+
16
+ class InitializeError < Exception; end
17
+ class SiteVolumeError < Exception; end
18
+ class NoEntrySiteError < Exception; end
19
+ class SiteRangeError < Exception; end
20
+ class SiteDetectionError < Exception; end
21
+
22
+ attr_reader :symmetry_operations,
23
+ :frame_elements,
24
+ :frame_sites,
25
+ :axes,
26
+ :octahedral_sites,
27
+ :tetrahedral_sites,
28
+ :scatter_sites
29
+
30
+ def initialize(hash)
31
+ ##不可欠な要素がなければ例外、というのはやめる[2014-12-09]
32
+ #raise InitializeError if hash["frame_elements"] == nil
33
+ #raise InitializeError if hash["frame_sites"] == nil
34
+ #raise InitializeError if hash["symmetry_operations"] == nil
35
+
36
+ items = [
37
+ "frame_elements",
38
+ "frame_sites",
39
+ "symmetry_operations",
40
+ "tetrahedral_sites",
41
+ "octahedral_sites",
42
+ "scatter_sites",
43
+ ]
44
+ hash.keys.each do |key|
45
+ #未知の項目があれば例外。
46
+ raise InitializeError, "#{key} is not found" unless items.include?(key)
47
+ end
48
+
49
+ @axes = CrystalCell::LatticeAxes.new([
50
+ [1.0, 0.0, 0.0],
51
+ [0.0, 1.0, 0.0],
52
+ [0.0, 0.0, 1.0],
53
+ ])
54
+
55
+ @frame_elements = hash["frame_elements"]
56
+
57
+ @frame_sites = {}
58
+ hash["frame_sites"].each do |name, pos|
59
+ @frame_sites[name] = pos.to_v3di
60
+ end
61
+
62
+ @symmetry_operations = hash["symmetry_operations"] || []
63
+ @tetrahedral_sites = hash["tetrahedral_sites"] || []
64
+ @octahedral_sites = hash["octahedral_sites" ] || []
65
+
66
+ @scatter_sites = {}
67
+ if hash["scatter_sites"]
68
+ hash["scatter_sites"].each do |name, pos|
69
+ @scatter_sites[name] = pos.to_v3di
70
+ end
71
+ end
72
+
73
+ ##Check coodinates are 0 <= x,y,z < 1
74
+ ## This check is not needed. [2013-07-30]
75
+ #frame_sites.each do |name, vec|
76
+ # vec.each do |x|
77
+ # raise SiteRangeError, "#{name}, #{vec.to_s}" if (x < 0.0 || 1.0 <= x)
78
+ # end
79
+ #end
80
+
81
+ ## This check is not needed. [2013-07-30]
82
+ #polyhedra.each do |name, polyhedron |
83
+ # vec = polyhedron.center
84
+ # vec.each do |x|
85
+ # raise SiteRangeError, "#{name}, #{vec.to_s}" if (x < 0.0 || 1.0 <= x)
86
+ # end
87
+ #end
88
+
89
+ end
90
+
91
+ #yaml から生成
92
+ def self.load_file(yaml)
93
+ hash = YAML.load_file(yaml)
94
+ self.new(hash)
95
+ end
96
+
97
+ def check_volume
98
+ #def check_sanity
99
+ #### check invalid symmetry operation
100
+ @symmetry_operations.each do |ope|
101
+ #pp ope
102
+ unless ope.has_key?("rotation") && ope.has_key?("translation")
103
+ message = "invalid symmetry_operation, #{@symmetry_operations}"
104
+ raise InitializeError, message
105
+ end
106
+ end
107
+
108
+
109
+ #体積の総和が 1.0 に近いか。
110
+ sum_volumes = polyhedra.values.inject(0.0) do |sum, polyhedron|
111
+ sum += polyhedron.volume
112
+ end
113
+ unless (sum_volumes - 1.0).abs < TOLERANCE
114
+ message = "Sum of volumes(#{sum_volumes}) not 1.0: \n"
115
+ polyhedra.each do |key, polyhedron|
116
+ message += sprintf("%10s: %10.5f\n", key, polyhedron.volume)
117
+ end
118
+ raise SiteVolumeError, message
119
+ end
120
+ end
121
+
122
+ #Return all site names.
123
+ def site_names
124
+ polyhedra.keys
125
+ end
126
+
127
+
128
+ #与えられた座標が、どの格子間サイトを占有するか、そのサイト名の文字列を返す。
129
+ #第1引数は対象の内部座標
130
+ # 内部座標 positions が 0-1 の範囲になければ、
131
+ # 自動的にセル内部に周期的並進移動させる。
132
+ #
133
+ #前提として全てのサイトが完全直交のような形になっていなければならないが、
134
+ #このチェックはこのメソッドに食わせる以前にチェックされているものとする。
135
+ #どのサイトにも該当しない場合についても、
136
+ #このメソッドに食わせる以前にチェックされているものとする。
137
+ #
138
+ #リストのどの格子間サイトにも含まれていないと判定されれば RuntimeError を投げる。
139
+ #しかしこれは正しくサイト設定されていれば起こらない筈。TODO そうでもなさそう。[2013-08-03]見つからなければ SiteDetectionError を投げる。
140
+ #が、演算誤差はやっぱり怖いので tolerance を設定する。
141
+ #periodic cell でtranslate したセルの座標は隙間ができそう。
142
+ def find_interstitial_site(position, tolerance, log_io)
143
+ candidates = select_interstitial_sites27(position, tolerance, log_io)
144
+ raise SiteDetectionError if candidates.empty?
145
+ choose_zyx(candidates).name
146
+ end
147
+
148
+ #Return Array of all sites which include the position
149
+ #among surrounding 3x3x3 cells.
150
+ #If the position is on the border, the results should be plural.
151
+ def select_interstitial_sites27(position, tolerance, log_io)
152
+ #MEMO:
153
+ #概念的には position に対して 多面体を 3x3x3 = 27個用意してどれかに入るか、
154
+ #というチェックを行うのだが、
155
+ #実装としては多面体は1つ固定して、 position を 27 個移動させてどれかが入るか、
156
+ #というチェックを行う。
157
+ #数学的に等価な判定になるし、後者の方が速度的に有利。
158
+
159
+ #セルの内側であることを保証する。
160
+ position = Mageo::Vector3D[*position]
161
+ position -= position.floor
162
+
163
+ results = []
164
+ polyhedra.each do |name, polyhedron|
165
+ log_io.puts(" Checking #{name}:")
166
+
167
+ log_io.puts(" Vertices:")
168
+ polyhedron.vertices.each do |vertex|
169
+ log_io.printf(
170
+ " (% 7.5f, % 7.5f, % 7.5f)inter.\n", * vertex
171
+ )
172
+ end
173
+
174
+ log_io.puts( " Generating 3x3x3 periodic coordinates of atom:")
175
+ log_io.printf(" orig = (% 5.3f, % 5.3f, % 5.3f)\n",
176
+ * position)
177
+ (-1).upto(1).each do |c|
178
+ (-1).upto(1).each do |b|
179
+ (-1).upto(1).each do |a|
180
+ log_io.printf(" orig+(% 1d, % 1d, % 1d)inter.", a, b, c)
181
+ pos = position.to_v3d +
182
+ Mageo::Vector3D[-1.0, 0.0, 0.0] * a +
183
+ Mageo::Vector3D[ 0.0, -1.0, 0.0] * b +
184
+ Mageo::Vector3D[ 0.0, 0.0, -1.0] * c
185
+ #-1 factor is because polyhedron is fix and position is shifted.
186
+
187
+ log_io.printf(" -> (% 7.3f, % 7.3f, % 7.3f)cart. : ",
188
+ * pos )
189
+ if polyhedron.include?(pos, tolerance)
190
+ log_io.puts("Inside.")
191
+ log_io.puts(" Found at atom with translation in internal vector [#{a}, #{b}, #{c}].")
192
+
193
+ results << Crysna::SiteWithPosition.new(
194
+ name,
195
+ Mageo::Vector3DInternal[a,b,c],
196
+ polyhedron.center.internal_coordinates(@axes)
197
+ )
198
+ else
199
+ log_io.puts("Outside.")
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ log_io.puts
206
+ end
207
+ return results
208
+ end
209
+
210
+ #Assuming a cubic cell with lattice constants of 1,1,1,90,90,90.
211
+ def find_nearest_frame_site(position)
212
+ find_site(position, @frame_sites)
213
+ end
214
+
215
+ def find_site(position, sites)
216
+ position = Mageo::Vector3D[* position]
217
+ position -= position.floor
218
+
219
+ distance = 1.0/0.0
220
+ minimum_global_vector = [0, 0, 0]
221
+ minimum_site_name = nil
222
+ sites.each do |fsite|
223
+ site_name = fsite[0]
224
+ site_vector = fsite[1].to_v3d([[1,0,0], [0,1,0], [0,0,1]])
225
+
226
+
227
+ (-1).upto(1).each do |x|
228
+ (-1).upto(1).each do |y|
229
+ (-1).upto(1).each do |z|
230
+ tmp_vec =
231
+ position - (site_vector + Mageo::Vector3D[x,y,z])
232
+ new_distance = tmp_vec.r
233
+
234
+ if distance > new_distance
235
+ minimum_global_vector = [x,y,z]
236
+ minimum_site_name = site_name
237
+ distance = new_distance
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ return [minimum_site_name, minimum_global_vector]
245
+ end
246
+
247
+ #Return a hash whose keys and values are site names and center point of polyhedra,
248
+ #respectively.
249
+ def ideal_interstitial_sites
250
+ results = {}
251
+ polyhedra.each {|name, polyhedron| results[name] = polyhedron.center.internal_coordinates(@axes)}
252
+ results
253
+ end
254
+
255
+ def polyhedra
256
+ tetrahedra(@tetrahedral_sites).merge(octahedra(@octahedral_sites))
257
+ end
258
+
259
+ private
260
+
261
+ #
262
+ #
263
+ def tetrahedra(sites)
264
+ results = {}
265
+ sites.each do |site_name, vertices|
266
+ coords = vertices.map do |vertex|
267
+ to_coord(vertex[0], vertex[1])
268
+ end
269
+ begin
270
+ results[site_name] = Mageo::Tetrahedron.new(coords)
271
+ rescue Mageo::Tetrahedron::InitializeError
272
+ message = "Volume of #{site_name} is zero."
273
+ raise SiteVolumeError , message
274
+ end
275
+ end
276
+ return results
277
+ end
278
+
279
+ def octahedra(sites)
280
+ results = {}
281
+ sites.each do |site_name, vertex_pairs|
282
+ coords_pairs = vertex_pairs.map do |vertex_pair|
283
+ vertex_pair.map{|vertex| to_coord(* vertex)}
284
+ end
285
+
286
+ begin
287
+ results[site_name] = Mageo::Octahedron.new(coords_pairs)
288
+ rescue Mageo::Octahedron::InitializeError
289
+ message = "Volume of #{site_name} is zero."
290
+ raise SiteVolumeError , message
291
+ end
292
+ end
293
+ return results
294
+ end
295
+
296
+ #サイト名と並進操作の組み合わせから cartesian 座標を得る。
297
+ def to_coord(name, translation)
298
+ unless @frame_sites.keys.include?(name)
299
+ raise NoEntrySiteError, "#{name} not in #{@frame_sites.keys}"
300
+ end
301
+ (@frame_sites[name] + Mageo::Vector3DInternal[*translation]).to_v3d(@axes)
302
+ end
303
+
304
+ def choose_zyx(candidates)
305
+ result = candidates[0]
306
+ candidates[1..-1].each do |site|
307
+ if result.coordinates[2] < site.coordinates[2]
308
+ result = site
309
+ next
310
+ elsif result.coordinates[2] > site.coordinates[2]
311
+ next
312
+ end
313
+
314
+ if result.coordinates[1] < site.coordinates[1]
315
+ result = site
316
+ next
317
+ elsif result.coordinates[1] > site.coordinates[1]
318
+ next
319
+ end
320
+
321
+ if result.coordinates[0] < site.coordinates[0]
322
+ result = site
323
+ next
324
+ elsif result.coordinates[0] > site.coordinates[0]
325
+ next
326
+ end
327
+ raise "Must not happen! Exist the same site?"
328
+ end
329
+ return result
330
+ end
331
+
332
+ end
333
+