class Crysna::ModelStructure
サイト情報などを抽象化したモデル構造を扱うクラス。 内部的に、格子定数 1.0 の cubic なセルと見做して処理している。 initialize 時点で作った以降は内部データが変更されないことを前提としている。 (@polyhedra を作っておいた方が速そうとか思ったが、テストしてみたら変わらんかった。)
体積関係のエラーチェックは、ininialize 時には行わない。
Constants
- ELEMENT
- TOLERANCE
Attributes
Public Class Methods
yaml から生成
# File lib/crysna/modelstructure.rb, line 92 def self.load_file(yaml) hash = YAML.load_file(yaml) self.new(hash) end
# File lib/crysna/modelstructure.rb, line 30 def initialize(hash) ##不可欠な要素がなければ例外、というのはやめる[2014-12-09] #raise InitializeError if hash["frame_elements"] == nil #raise InitializeError if hash["frame_sites"] == nil #raise InitializeError if hash["symmetry_operations"] == nil items = [ "frame_elements", "frame_sites", "symmetry_operations", "tetrahedral_sites", "octahedral_sites", "scatter_sites", ] hash.keys.each do |key| #未知の項目があれば例外。 raise InitializeError, "#{key} is not found" unless items.include?(key) end @axes = CrystalCell::LatticeAxes.new([ [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], ]) @frame_elements = hash["frame_elements"] @frame_sites = {} hash["frame_sites"].each do |name, pos| @frame_sites[name] = pos.to_v3di end @symmetry_operations = hash["symmetry_operations"] || [] @tetrahedral_sites = hash["tetrahedral_sites"] || [] @octahedral_sites = hash["octahedral_sites" ] || [] @scatter_sites = {} if hash["scatter_sites"] hash["scatter_sites"].each do |name, pos| @scatter_sites[name] = pos.to_v3di end end ##Check coodinates are 0 <= x,y,z < 1 ## This check is not needed. [2013-07-30] #frame_sites.each do |name, vec| # vec.each do |x| # raise SiteRangeError, "#{name}, #{vec.to_s}" if (x < 0.0 || 1.0 <= x) # end #end ## This check is not needed. [2013-07-30] #polyhedra.each do |name, polyhedron | # vec = polyhedron.center # vec.each do |x| # raise SiteRangeError, "#{name}, #{vec.to_s}" if (x < 0.0 || 1.0 <= x) # end #end end
Public Instance Methods
# File lib/crysna/modelstructure.rb, line 97 def check_volume #def check_sanity #### check invalid symmetry operation @symmetry_operations.each do |ope| #pp ope unless ope.has_key?("rotation") && ope.has_key?("translation") message = "invalid symmetry_operation, #{@symmetry_operations}" raise InitializeError, message end end #体積の総和が 1.0 に近いか。 sum_volumes = polyhedra.values.inject(0.0) do |sum, polyhedron| sum += polyhedron.volume end unless (sum_volumes - 1.0).abs < TOLERANCE message = "Sum of volumes(#{sum_volumes}) not 1.0: \n" polyhedra.each do |key, polyhedron| message += sprintf("%10s: %10.5f\n", key, polyhedron.volume) end raise SiteVolumeError, message end end
与えられた座標が、どの格子間サイトを占有するか、そのサイト名の文字列を返す。 第1引数は対象の内部座標
内部座標 positions が 0-1 の範囲になければ、 自動的にセル内部に周期的並進移動させる。
前提として全てのサイトが完全直交のような形になっていなければならないが、 このチェックはこのメソッドに食わせる以前にチェックされているものとする。 どのサイトにも該当しない場合についても、 このメソッドに食わせる以前にチェックされているものとする。
リストのどの格子間サイトにも含まれていないと判定されれば RuntimeError を投げる。 しかしこれは正しくサイト設定されていれば起こらない筈。TODO そうでもなさそう。見つからなければ SiteDetectionError
を投げる。 が、演算誤差はやっぱり怖いので tolerance を設定する。 periodic cell でtranslate したセルの座標は隙間ができそう。
# File lib/crysna/modelstructure.rb, line 142 def find_interstitial_site(position, tolerance, log_io) candidates = select_interstitial_sites27(position, tolerance, log_io) raise SiteDetectionError if candidates.empty? choose_zyx(candidates).name end
Assuming a cubic cell with lattice constants of 1,1,1,90,90,90.
# File lib/crysna/modelstructure.rb, line 211 def find_nearest_frame_site(position) find_site(position, @frame_sites) end
# File lib/crysna/modelstructure.rb, line 215 def find_site(position, sites) position = Mageo::Vector3D[* position] position -= position.floor distance = 1.0/0.0 minimum_global_vector = [0, 0, 0] minimum_site_name = nil sites.each do |fsite| site_name = fsite[0] site_vector = fsite[1].to_v3d([[1,0,0], [0,1,0], [0,0,1]]) (-1).upto(1).each do |x| (-1).upto(1).each do |y| (-1).upto(1).each do |z| tmp_vec = position - (site_vector + Mageo::Vector3D[x,y,z]) new_distance = tmp_vec.r if distance > new_distance minimum_global_vector = [x,y,z] minimum_site_name = site_name distance = new_distance end end end end end return [minimum_site_name, minimum_global_vector] end
Return a hash whose keys and values are site names and center point of polyhedra, respectively.
# File lib/crysna/modelstructure.rb, line 249 def ideal_interstitial_sites results = {} polyhedra.each {|name, polyhedron| results[name] = polyhedron.center.internal_coordinates(@axes)} results end
# File lib/crysna/modelstructure.rb, line 255 def polyhedra tetrahedra(@tetrahedral_sites).merge(octahedra(@octahedral_sites)) end
Return Array of all sites which include the position among surrounding 3x3x3 cells. If the position is on the border, the results should be plural.
# File lib/crysna/modelstructure.rb, line 151 def select_interstitial_sites27(position, tolerance, log_io) #MEMO: #概念的には position に対して 多面体を 3x3x3 = 27個用意してどれかに入るか、 #というチェックを行うのだが、 #実装としては多面体は1つ固定して、 position を 27 個移動させてどれかが入るか、 #というチェックを行う。 #数学的に等価な判定になるし、後者の方が速度的に有利。 #セルの内側であることを保証する。 position = Mageo::Vector3D[*position] position -= position.floor results = [] polyhedra.each do |name, polyhedron| log_io.puts(" Checking #{name}:") log_io.puts(" Vertices:") polyhedron.vertices.each do |vertex| log_io.printf( " (% 7.5f, % 7.5f, % 7.5f)inter.\n", * vertex ) end log_io.puts( " Generating 3x3x3 periodic coordinates of atom:") log_io.printf(" orig = (% 5.3f, % 5.3f, % 5.3f)\n", * position) (-1).upto(1).each do |c| (-1).upto(1).each do |b| (-1).upto(1).each do |a| log_io.printf(" orig+(% 1d, % 1d, % 1d)inter.", a, b, c) pos = position.to_v3d + Mageo::Vector3D[-1.0, 0.0, 0.0] * a + Mageo::Vector3D[ 0.0, -1.0, 0.0] * b + Mageo::Vector3D[ 0.0, 0.0, -1.0] * c #-1 factor is because polyhedron is fix and position is shifted. log_io.printf(" -> (% 7.3f, % 7.3f, % 7.3f)cart. : ", * pos ) if polyhedron.include?(pos, tolerance) log_io.puts("Inside.") log_io.puts(" Found at atom with translation in internal vector [#{a}, #{b}, #{c}].") results << Crysna::SiteWithPosition.new( name, Mageo::Vector3DInternal[a,b,c], polyhedron.center.internal_coordinates(@axes) ) else log_io.puts("Outside.") end end end end log_io.puts end return results end
Return all site names.
# File lib/crysna/modelstructure.rb, line 123 def site_names polyhedra.keys end
Private Instance Methods
# File lib/crysna/modelstructure.rb, line 304 def choose_zyx(candidates) result = candidates[0] candidates[1..-1].each do |site| if result.coordinates[2] < site.coordinates[2] result = site next elsif result.coordinates[2] > site.coordinates[2] next end if result.coordinates[1] < site.coordinates[1] result = site next elsif result.coordinates[1] > site.coordinates[1] next end if result.coordinates[0] < site.coordinates[0] result = site next elsif result.coordinates[0] > site.coordinates[0] next end raise "Must not happen! Exist the same site?" end return result end
# File lib/crysna/modelstructure.rb, line 279 def octahedra(sites) results = {} sites.each do |site_name, vertex_pairs| coords_pairs = vertex_pairs.map do |vertex_pair| vertex_pair.map{|vertex| to_coord(* vertex)} end begin results[site_name] = Mageo::Octahedron.new(coords_pairs) rescue Mageo::Octahedron::InitializeError message = "Volume of #{site_name} is zero." raise SiteVolumeError , message end end return results end
# File lib/crysna/modelstructure.rb, line 263 def tetrahedra(sites) results = {} sites.each do |site_name, vertices| coords = vertices.map do |vertex| to_coord(vertex[0], vertex[1]) end begin results[site_name] = Mageo::Tetrahedron.new(coords) rescue Mageo::Tetrahedron::InitializeError message = "Volume of #{site_name} is zero." raise SiteVolumeError , message end end return results end
サイト名と並進操作の組み合わせから cartesian 座標を得る。
# File lib/crysna/modelstructure.rb, line 297 def to_coord(name, translation) unless @frame_sites.keys.include?(name) raise NoEntrySiteError, "#{name} not in #{@frame_sites.keys}" end (@frame_sites[name] + Mageo::Vector3DInternal[*translation]).to_v3d(@axes) end