Class: VaspUtils::Poscar

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

Overview

Class to manage POSCAR format of VASP.

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

Defined Under Namespace

Classes: ElementMismatchError, ParseError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash) ⇒ Poscar

Returns a new instance of Poscar.



19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/vasputils/poscar.rb', line 19

def initialize(hash)
  hash.each do |key,val|
    @comment            = val if :comment            ==key
    @scale              = val if :scale              ==key
    @axes               = val if :axes               ==key
    @elements           = val if :elements           ==key
    @nums_elements      = val if :nums_elements      ==key
    @selective_dynamics = val if :selective_dynamics ==key
    @direct             = val if :direct             ==key
    @positions          = val if :positions          ==key
  end
end

Instance Attribute Details

#axesObject

Returns the value of attribute axes.



17
18
19
# File 'lib/vasputils/poscar.rb', line 17

def axes
  @axes
end

#commentObject (readonly)

Returns the value of attribute comment.



15
16
17
# File 'lib/vasputils/poscar.rb', line 15

def comment
  @comment
end

#directObject (readonly)

Returns the value of attribute direct.



15
16
17
# File 'lib/vasputils/poscar.rb', line 15

def direct
  @direct
end

#elementsObject (readonly)

Returns the value of attribute elements.



15
16
17
# File 'lib/vasputils/poscar.rb', line 15

def elements
  @elements
end

#nums_elementsObject (readonly)

Returns the value of attribute nums_elements.



15
16
17
# File 'lib/vasputils/poscar.rb', line 15

def nums_elements
  @nums_elements
end

#positionsObject (readonly)

Returns the value of attribute positions.



15
16
17
# File 'lib/vasputils/poscar.rb', line 15

def positions
  @positions
end

#scaleObject (readonly)

Returns the value of attribute scale.



15
16
17
# File 'lib/vasputils/poscar.rb', line 15

def scale
  @scale
end

#selective_dynamicsObject (readonly)

Returns the value of attribute selective_dynamics.



15
16
17
# File 'lib/vasputils/poscar.rb', line 15

def selective_dynamics
  @selective_dynamics
end

Class Method Details

.interpolate(poscar0, poscar1, ratio, periodic = false) ⇒ Object

selective_dynamics は常に on にする。各要素の真偽値は 2つの POSCAR の論理積。指定がなければ true と見做す。ratio は poscar1 の比率。0だと poscar0 に、1だとposcar1 となる。Return Poscar class instance



232
233
234
# File 'lib/vasputils/poscar.rb', line 232

def self.interpolate(poscar0, poscar1, ratio, periodic = false)
  poscar0.interpolate(poscar1, ratio, periodic)
end

.load_cell(cell) ⇒ Object

CrystalCell::Cell クラスインスタンスからPoscar クラスインスタンスを生成



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
# File 'lib/vasputils/poscar.rb', line 120

def self.load_cell(cell)
  elements = cell.elements.sort.uniq

  atoms = cell.atoms.sort_by{|atom| atom.element}

  nums_elements = {}
  atoms.each do |atom|
    nums_elements[atom.element] ||= 0
    nums_elements[atom.element] += 1
  end
  nums_elements = elements.map{|elem| nums_elements[elem]}

  positions = []
  movable_flags = []
  selective_dynamics = false
  atoms.each do |atom|
    positions << atom.position
    movable_flags << atom.movable_flags
    selective_dynamics = true if movable_flags
  end

  selective_dynamics = movable_flags if movable_flags

  options = {
    :comment            => cell.comment           ,
    :scale              => 1.0               ,
    :axes               => cell.axes.to_a,
    :elements           => elements          ,
    :nums_elements      => nums_elements     ,
    :selective_dynamics => selective_dynamics,
    :direct             => true,
    :positions          => positions             ,
  }
  self.new(options)
end

.load_file(file) ⇒ Object

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



113
114
115
116
# File 'lib/vasputils/poscar.rb', line 113

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

.parse(io) ⇒ Object

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



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
100
101
102
103
104
105
106
107
108
109
# File 'lib/vasputils/poscar.rb', line 34

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! { |j| j.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+/)
    elements = nil
    if vals[0].to_i == 0
      elements = vals
      vals = io.readline.strip.split(/\s+/)
    end
    nums_elements = vals.map{|i| i.to_i}

    # 'Selective dynamics' or not (bool)
    line = io.readline
    selective_dynamics = false
    if line =~ /^\s*s/i
      selective_dynamics = []
      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

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

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

  options = {
    :comment            => comment           ,
    :scale              => scale             ,
    :axes               => axes              ,
    :elements           => elements          ,
    :nums_elements      => nums_elements     ,
    :selective_dynamics => selective_dynamics,
    :direct             => direct            ,
    :positions          => positions         ,
  }
  self.new(options)
end

Instance Method Details

#==(other) ⇒ Object



278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/vasputils/poscar.rb', line 278

def ==(other)
  result = true
  result = false unless @comment             == other.comment            
  result = false unless @scale               == other.scale              
  result = false unless @axes                == other.axes               
  result = false unless @elements            == other.elements           
  result = false unless @nums_elements       == other.nums_elements      
  result = false unless @selective_dynamics  == other.selective_dynamics 
  result = false unless @direct              == other.direct             
  result = false unless @positions           == other.positions          
  result
end

#dump(io, version = 5) ⇒ Object

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

elems が cell の持つ元素リストとマッチしなければ
例外 Poscar::ElementMismatchError を投げる。
nil ならば、原子の element でソートした順に出力する。

io は書き出すファイルハンドル。‘version’ indicates a poscar style for vasp 4 or 5. def dump(io, elements = nil, version = 5)



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/vasputils/poscar.rb', line 166

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

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

  ## Element symbols for vasp 5.
  if version >= 5
    io.puts elem_indices.map{|i| @elements[i]}.join(' ')
  end

  ## Atom numbers.
  io.puts elem_indices.map{|i| @nums_elements[i]}.join(' ')

  ## Selective dynamics
  io.puts "Selective dynamics" if @selective_dynamics
  io.puts "Direct"

  @positions.size.times do |i|
    io.puts sprint_position(i)
  end
end

#interpolate(other, ratio, periodic = false) ⇒ Object

selective_dynamics は常に on にする。other は Poscar クラスインスタンス

Raises:

  • (PoscarMismatchError)


238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/vasputils/poscar.rb', line 238

def interpolate(other, ratio, periodic = false)
  #self.class.interpolate(self, poscar, ratio, periodic = false)
  axes0 = self.axes
  axes1 = other.axes
  new_axes = []
  3.times do |i|
    new_axes << interpolate_coords(axes0[i], axes1[i], ratio)
  end

  raise PoscarMismatchError unless self.elements == other.elements
  raise PoscarMismatchError unless self.nums_elements == other.nums_elements

  new_positions = []
  self.positions.size.times do |i|
    ##positions
    coord0 = self.positions[i]
    coord1 = other.positions[i]
    coord1 = periodic_nearest(coord0, coord1) if periodic
    new_positions << interpolate_coords(
      coord0, coord1, ratio)
  end

  hash = {
    :comment => "Generated by interpolation of #{ratio}",
    :scale   => 1.0,
    :axes    => new_axes,
    :elements           => self.elements,
    :nums_elements      => self.nums_elements,
    :selective_dynamics => merge_selective_dynamics(
      self.selective_dynamics,
      other.selective_dynamics
    ),
    :direct             => true,
    :positions          => new_positions
  }

  VaspUtils::Poscar.new(hash)
end

#substitute(elem1, elem2) ⇒ Object



291
292
293
294
295
# File 'lib/vasputils/poscar.rb', line 291

def substitute(elem1, elem2)
  result = Marshal.load(Marshal.dump(self))
  result.substitute!(elem1, elem2)
  result
end

#substitute!(elem1, elem2) ⇒ Object



297
298
299
300
301
302
303
304
305
306
# File 'lib/vasputils/poscar.rb', line 297

def substitute!(elem1, elem2)
  @elements.map! do |elem|
    if elem == elem1 
      elem2
    else
      elem
    end
  end
  unite_elements
end

#to_cell(klass = CrystalCell::Cell) ⇒ Object

Generate CrystalCell::Cell class. If klass is argued, try to generate the class.



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/vasputils/poscar.rb', line 200

def to_cell(klass = CrystalCell::Cell)
  axes = CrystalCell::LatticeAxes.new( @axes)

  atoms = []
  total_id = 0
  @nums_elements.each_with_index do |num, elem_id|
    num.times do |atom_id|
      element = elem_id
      element = @elements[elem_id] if @elements

      movable_flags = nil
      if @selective_dynamics
        movable_flags = @selective_dynamics[total_id]
      end
      atoms << CrystalCell::Atom.new(
        element, 
        @positions[total_id],
        movable_flags
      )
      total_id += 1
    end
  end

  klass.new(axes, atoms)
end