class FrameInterstitialCell

Cell modeled by frame atoms and interstitial atoms.
All atoms must be FrameAtom or InterstitialAtom class instance,
not CrystalCell::Atom class instance.

class FrameInterstitialCell < CrystalCell::PeriodicCell

Public Class Methods

new(axes, atoms = []) click to toggle source

Argument ‘atoms’ must be Array of atoms whose class is FrameAtom or InterstitialAtom. If ‘atoms’ includes CrystalCell::Atom class instance, raise exception ‘FrameInterstitialCell::TypeError’

Calls superclass method
# File lib/crysna/frameinterstitialcell.rb, line 29
def initialize(axes, atoms = [])
  atoms.each_with_index do |atom, i|
    unless (atom.is_a?(FrameAtom) || (atom.is_a?(InterstitialAtom)))
      raise TypeError,
      "'atoms' includes #{atom.class} at #{i}."
    end
  end
  super(axes, atoms)
end

Public Instance Methods

elements_names() click to toggle source

保持する原子群について、 元素ごとに分類し、元素を鍵とするハッシュとする。 ハッシュの値はその元素の原子それぞれに付けられた名前で、ソート済の配列。

# File lib/crysna/frameinterstitialcell.rb, line 181
def elements_names
  results = {}
  @atoms.each do |atom|
    elem = atom.element
    if results[elem]
      results[elem] << atom.name
    else
      results[elem] = [atom.name]
    end
  end
  results.each_value do |val|
    val.sort!
  end
  return results
end
frame_atoms() click to toggle source

@atoms のうち、 FrameAtom のものだけをまとめて Array にして返す。

# File lib/crysna/frameinterstitialcell.rb, line 153
def frame_atoms
  @atoms.select{|atom| atom.class == FrameAtom}
end
frame_cell() click to toggle source

@atoms のうち、 FrameAtom のものだけを含んだ FrameInterstitialCell を返す。

# File lib/crysna/frameinterstitialcell.rb, line 164
def frame_cell
  result = FrameInterstitialCell.new(@axes, frame_atoms)
  result.comment = self.comment
  return result
end
interstitial_atoms() click to toggle source

@atoms のうち、 InterstitialAtom のものだけをまとめて Array にして返す。

# File lib/crysna/frameinterstitialcell.rb, line 158
def interstitial_atoms
  @atoms.select{|atom| atom.class == InterstitialAtom}
end
interstitial_cell() click to toggle source

@atoms のうち、 InterstitialAtom のものだけを含んだ FrameInterstitialCell を返す。

# File lib/crysna/frameinterstitialcell.rb, line 172
def interstitial_cell
  result = FrameInterstitialCell.new(@axes, interstitial_atoms)
  result.comment = self.comment
  return result
end
optimized_cell( sites, log_io ) click to toggle source
サイト情報を参照して、骨格原子が最適になるように
48個の変換操作・並進移動を然るべく施したセル( self.class のインスタンス )を返す。
非破壊的
引数 sites はサイト情報を入れたハッシュ。キーがサイト名、値がサイトの内部座標。
サイトと原子が一対一対応する候補が見つからない場合は
例外 FrameInterstitialCell::NoMatchError を返す。
原子の順番は非保存。

def most_matching_cell_pair(sites, log_io )

# File lib/crysna/frameinterstitialcell.rb, line 47
def optimized_cell( sites, log_io )
  # 48個のセルに対し、サイト数ごとに roughly translate したセルを作る。
  # サイト数が 8 ならば 48*8 個の Array になる。
  cells = equivalent_cells.map{ |cell|
    tmp = []
    sites.each do |name, vec|
      translation_rough = vec - cell.frame_atoms[0].position
      t_cell = cell.translate(translation_rough)
      t_cell.comment += sprintf(
        "; Rough translation: (% 7.5f, % 7.5f, % 7.5f), 0th atom of frame to #{name}",
        * (translation_rough)
      )
      tmp << t_cell
    end
    tmp
  }.flatten

  p_io = $stderr
  p_io = File.open(File::NULL, "w") if $test == true
  pbar = ProgressBar.new("FrameInterstitialCell::optimized_cell", cells.size, p_io)

  # 操作と評価を全てに対して行う。
  min_cell = nil
  min_sum  = nil
  cells.each do |cell|
    pbar.inc
    # SiteNameLabeledCell に変換できない cell が含まれるため、
    # min_by メソッドは使えない。

    log_io.puts("    #{cell.comment.sub("; ", " \\\n    ")}")
    log_io.puts("        atom coordinates:")
    cell.atoms.each_with_index do |atom, index|
      log_io.printf("          (% 7.5f, % 7.5f, % 7.5f), Element:%s, %02i-th atom.\n",
        * (atom.position), atom.element, index )
    end

    f_cell = cell.frame_cell
    i_cell = cell.interstitial_cell.to_pcell.to_cell.to_ficell([])

    # SiteNameLabeledCell に変換
    begin
      snlcell = f_cell.to_snlcell(sites)
    rescue SiteNameLabeledCell::InitializeError
      log_io.puts "            Not correspond atoms for sitenames."
      log_io.puts
      next # 一対一対応しない場合、無視する。
    end

    # 原子の重心とサイトの重心が一致するように
    # 精密化した translation を行う。
    translation_prec =
      snlcell.center_of_sites - snlcell.center_of_atoms
    snlcell.translate!(translation_prec)
    i_cell.translate!(translation_prec)
    [snlcell, i_cell].each do |cell|
      cell.comment += sprintf(
        "; Precise translation: (% 7.5f, % 7.5f, % 7.5f)\n",
        * (translation_prec)
      )
    end

    # log 出力
    log_io.puts("            Found frame atoms:")
    frame_atoms.each do |atom|
      log_io.printf("          (% 7.5f, % 7.5f % 7.5f), #{atom.name}\n",
        * atom.position
      )
    end

    # 二乗和で評価
    sum = snlcell.sum_square_distances
    log_io.printf( "            Sum of square distances: %8.4f.\n\n", sum )
    if (min_sum == nil) || (sum < min_sum)
      min_sum = sum
      min_cell = snlcell.to_cell.to_ficell.unite(i_cell)
    end
  end
  pbar.finish

  unless min_cell.class == FrameInterstitialCell
    raise NoMatchError,
    "Class of min_cell is #{min_cell.class}"
  end

  log_io.puts(    "    Chosen cell: #{min_cell.comment}")
  log_io.puts(    "                    sum of square distances: #{min_sum}")

  # サイト情報をここで全部書き出しておく。
  log_io.puts("        Atoms:")
  min_cell.atoms.each do |atom|
    log_io.printf(
      "            (% 7.5f, % 7.5f, % 7.5f), element %s, %s.\n",
      * atom.position,
      atom.element, 
      atom.name,
    )
  end
  #logs.each do |line|
  # log_io.puts line
  #end
  log_io.puts

  return min_cell
end

Private Instance Methods

equivalent_cells() click to toggle source

セルに対して格子ベクトルの反転・交換操作を組み合わせた結果生じる 全 48 個のセルを配列にして返す。 鏡像操作は含まれない。 交換 → 反転 の順で操作を施す。 返される配列内部の順序は、以下の組み合わせ。

範囲  a反転 b反転 c反転
00-05       x           x           x
06-11       o           x           x
12-17       x           o           x
18-23       o           o           x
24-29       x           x           o
30-35       o           x           o
36-41       x           o           o
42-47       o           o           o

各々の範囲中では、6要素が以下の順序。
    00: a,b,c(a,b 交換→ a,b交換)(交換なし)
    01: b,c,a(a,b 交換→ b,c交換)
    02: c,a,b(a,b 交換→ c,a交換)
    03: b,a,c(a,b 交換)
    04: a,c,b(b,c 交換)
    05: c,b,a(c,a 交換)
# File lib/crysna/frameinterstitialcell.rb, line 222
def equivalent_cells
  results = []
  gen_size = 48

  gen_size.times do |i|
    cell = Marshal.load(Marshal.dump(self))
    comments = [
      sprintf("Equivalent cell No.%02d: Original", i)
    ]
    case i % 6
    when 0
      #"do nothing"
    when 1
      cell.exchange_axes!([0,1])
      cell.exchange_axes!([1,2])
      comments << "Axes exchanged(a,b,c -> b,c,a)"
    when 2
      cell.exchange_axes!([0,1])
      cell.exchange_axes!([2,0]) 
      comments << "Axes exchanged(a,b,c -> c,a,b)"
    when 3
      cell.exchange_axes!([0,1]) 
      comments << "Axes exchanged(a,b,c -> b,a,c)"
    when 4
      cell.exchange_axes!([1,2]) 
      comments << "Axes exchanged(a,b,c -> a,c,b)"
    when 5
      cell.exchange_axes!([2,0]) 
      comments << "Axes exchanged(a,b,c -> c,b,a)"
    end

    if ((i / 6) % 2 == 1)
      cell.inverse_axis!(0)
      comments << "Axis 'a' inversed"
    end
    if ((i / 12) % 2 == 1)
      cell.inverse_axis!(1)
      comments << "Axis 'b' inversed"
    end
    if ((i / 24) % 2 == 1)
      cell.inverse_axis!(2)
      comments << "Axis 'c' inversed"
    end

    #if ((i / 48) % 2 == 1)
    # cell.reflect!
    # comments << "Reflected"
    #end

    cell.comment = comments.join(" -> ") + "."
    results << cell
  end
  return results
end