Class: VaspUtils::Poscar

Inherits:
Object
  • Object
show all
Defined in:
lib/vasputils/poscar.rb

Overview

Class to manage POSCAR format of VASP.

parse と dump のどちらかだけでなく、両方を統括して扱うクラス。

MEMO POSCAR 自身は元素の情報を持っていない。 POSCAR が native に持っている情報だけを取り扱う。 Poscar では個々の原子が何の element であるかという情報を取り扱わない。 1番目の種類の原子種が何かだけを扱う。 こうしておくことで POTCAR がない環境でも POSCAR を扱うことができる。

VASP 5 系を使うようになれば事情が変わるだろう。

Defined Under Namespace

Classes: ElementMismatchError, ParseError

Class Method Summary collapse

Class Method Details

.dump(cell, elems, io, version = 5) ⇒ Object

POSCAR 形式で書き出す。 cell は Cell クラスインスタンスと同等のメソッドを持つもの。 elems は書き出す元素の順番。

elems  cell の持つ元素リストとマッチしなければ
例外 Poscar::ElementMismatchError を投げる。

io は書き出すファイルハンドル。 ‘version’ indicates a poscar style for vasp 4 or 5.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/vasputils/poscar.rb', line 115

def self.dump(cell, elems, io, version = 5)
  unless (Mapping::map?(cell.elements.uniq, elems){ |i, j| i == j })
    raise ElementMismatchError,
    "elems [#{elems.join(",")}] mismatches to cell.elements [#{cell.elements.join(",")}."
  end

  io.puts cell.comment
  io.puts "1.0" #scale
  3.times do |i|
    io.printf("  % 18.15f  % 18.15f  % 18.15f\n", cell.axes[i][0], cell.axes[i][1], cell.axes[i][2]
    )
  end

  # Element symbols for vasp 5.
  if version >= 5
    io.puts cell.atoms.map {|atom| atom.element}.uniq.join(" ")
  end

  # Atom numbers.
  elem_list = Hash.new
  elems.each do |elem|
    elem_list[ elem ] = cell.atoms.select{ |atom| atom.element == elem }
  end
  io.puts(elems.map { |elem| elem_list[elem].size }.join(" "))

  # Selective dynamics
  # どれか1つでも getMovableFlag が真であれば Selective dynamics をオンにする
  selective_dynamics = false
  cell.atoms.each do |atom|
    if atom.movable_flags
      selective_dynamics = true
      io.puts "Selective dynamics"
      break
    end
  end

  elems.each do |elem|
    elem_list[ elem ].each do |atom|
      if atom.movable_flags
        selective_dynamics = true
        break
      end
    end
    break if selective_dynamics
  end

  io.puts "Direct"

  # positions of atoms
  elems.each do |elem|
    elem_list[ elem ].each do |atom|
      tmp =  sprintf(
        "  % 18.15f  % 18.15f  % 18.15f",
        * atom.position)
      if selective_dynamics
        if atom.movable_flags == nil
          tmp += " T T T"
        else
          atom.movable_flags.each do |mov|
            (mov == true) ?  tmp += " T" : tmp += " F"
          end
        end
      end
      io.puts tmp
    end
  end
end

.load_file(file) ⇒ Object

file で与えられた名前のファイルを読み込んで Cell クラスインスタンスを返す。 構文解析できなければ例外 Poscar::ParseError を投げる。



103
104
105
106
# File 'lib/vasputils/poscar.rb', line 103

def self.load_file(file)
  io = File.open(file, "r")
  self.parse(io)
end

.parse(io) ⇒ Object

io を読み込んで Cell クラスインスタンスを返す。 構文解析できなければ例外 Poscar::ParseError を投げる。



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/vasputils/poscar.rb', line 28

def self.parse(io)
  # analyze POSCAR.

  begin
    #line 1: comment (string)
    comment = io.readline.chomp

    #line 2: universal scaling factor (float)
    scale = io.readline.to_f
    raise "Poscar.load_file cannot use negative scaling factor.\n" if scale < 0

    #line 3-5: axes (3x3 Array of float)
    axes = []
    3.times do |i| #each axis of a, b, c.
      vec = io.readline.strip.split(/\s+/) #in x,y,z directions
      axes << vec.collect! { |i| i.to_f * scale } #multiply scaling factor
    end

    # Element symbol (vasp 5). Nothing in vasp 4.
    #elements = io.readline.strip.split(/\s+/).map{|i| i.to_i}
    vals = io.readline.strip.split(/\s+/)
    if vals[0].to_i == 0
      elements = vals
      nums_elements = io.readline.strip.split(/\s+/).map{|i| i.to_i}
    else
      elements = []
      vals.size.times { |i| elements << i }
      nums_elements = vals.map{|i| i.to_i}
    end

    # 'Selective dynamics' or not (bool)
    line = io.readline
    if line =~ /^\s*s/i
      selective_dynamics = true
      line = io.readline    # when this situation, reading one more line is nessesarry
    end

    if (line =~ /^\s*d/i) # allow only 'Direct' now
      direct = true
    else
      raise "Not 'direct' indication."
    end

    # atom positions
    # e.g., positions_of_elements
    # e.g., movable_flags_of_elements

    atoms = []
    nums_elements.size.times do |elem_index|
      nums_elements[elem_index].times do |index|
      items = io.readline.strip.split(/\s+/)
      pos = items[0..2].map {|coord| coord.to_f}

      mov_flags = []
      if items.size >= 6 then
        items[3..5].each do |i|
          (i =~ /^t/i) ? mov_flags << true : mov_flags << false
        end
        atoms << Atom.new(elements[elem_index], pos, mov_flags)
      else
        atoms << Atom.new(elements[elem_index], pos)
      end
      end
    end
  rescue EOFError
    raise ParseError, "end of file reached"
  end

  cell = Cell.new(axes, atoms)
  cell.comment = comment
  cell
end