class Crysna::ModelStructure

サイト情報などを抽象化したモデル構造を扱うクラス。 内部的に、格子定数 1.0 の cubic なセルと見做して処理している。 initialize 時点で作った以降は内部データが変更されないことを前提としている。 (@polyhedra を作っておいた方が速そうとか思ったが、テストしてみたら変わらんかった。)

体積関係のエラーチェックは、ininialize 時には行わない。

Constants

ELEMENT
TOLERANCE

Attributes

axes[R]
frame_elements[R]
frame_sites[R]
octahedral_sites[R]
scatter_sites[R]
symmetry_operations[R]
tetrahedral_sites[R]

Public Class Methods

load_file(yaml) click to toggle source

yaml から生成

# File lib/crysna/modelstructure.rb, line 92
def self.load_file(yaml)
  hash = YAML.load_file(yaml)
  self.new(hash)
end
new(hash) click to toggle source
# 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

check_volume() click to toggle source
# 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
find_interstitial_site(position, tolerance, log_io) click to toggle source

与えられた座標が、どの格子間サイトを占有するか、そのサイト名の文字列を返す。 第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
find_nearest_frame_site(position) click to toggle source

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
find_site(position, sites) click to toggle source
# 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
ideal_interstitial_sites() click to toggle source

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
polyhedra() click to toggle source
# File lib/crysna/modelstructure.rb, line 255
def polyhedra
  tetrahedra(@tetrahedral_sites).merge(octahedra(@octahedral_sites))
end
select_interstitial_sites27(position, tolerance, log_io) click to toggle source

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
site_names() click to toggle source

Return all site names.

# File lib/crysna/modelstructure.rb, line 123
def site_names
  polyhedra.keys
end

Private Instance Methods

choose_zyx(candidates) click to toggle source
# 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
octahedra(sites) click to toggle source
# 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
tetrahedra(sites) click to toggle source
# 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
to_coord(name, translation) click to toggle source

サイト名と並進操作の組み合わせから 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