class Crysna::TransitionFinder
Public Class Methods
# File lib/crysna/transitionfinder.rb, line 20 def initialize(cells, site_migrations, site_operations) @site_migrations = site_migrations @site_operations = site_operations @cell_candidates = cells.map do |cell| new_cell = cell.minimize(@site_operations) new_cell.name = cell.name new_cell end @edges = [] @cell_manager = Crysna::TransitionFinder::CellManager.new #@graph = GraphViz.new( "Transition", # :type => "graph", # :use => "dot" # ) #@nodes = {} #@edges = [] end
Public Instance Methods
Start finding and retrun path. ‘start’ is the name of the starting cell. ‘truncate_energy’ is end condition of cell enegy. If truncate_energy == false, ‘finding one MEP mode’ is selected. The returned array of cell transitions. When ‘finding one MEP mode’, return an array of one item. Return nil if the transition path cannot be found. NOTE: specification may be changed in future.
# File lib/crysna/transitionfinder.rb, line 47 def find_path(start, truncate_energy, result_io, dot_io) pair = meet_transit_cells(start, truncate_energy) if pair ## Show result_io.puts "-"*60 result_io.puts "One direction" write_path(@cell_manager.ascend(pair[0]), result_io) result_io.puts "-"*60 result_io.puts "The other direction" write_path(@cell_manager.ascend(pair[1]), result_io) #@graph.output(dot_io) #dot_io.puts @graph.output( :none => String ) write_dot(pair, dot_io) else result_io.puts "Cannot find path." cell_transition = nil end #return cell_transition end
Private Instance Methods
# File lib/crysna/transitionfinder.rb, line 443 def each_sort(arys) arys.map {|ary| ary.sort} end
Argument ‘cell_id’ indicates an index of a base cell in @cell_manager. New nodes from the base cell are added into @cell_manager. New edges between the base cell and new cells are added into @edges. New nodes:
They are found from the base cell via a single migration. Energy of new cell is set to be the same vale of symmetric identical cell in @cell_candidates. If it is not found in @cell_candidates, set to be Float::INFINITY
Each edge has ‘note’ of the infomation of migration and operation; migration element, source site, destination site, symmetry operation to minimum_expression. ‘reach_flag’s of new cells are set to be false, ‘from_edges’ to be empty, energy to be set nil. NOTE: edge weight should be as in edge list.
# File lib/crysna/transitionfinder.rb, line 174 def expand_edges(cell_id) $log_io.puts "-" * 30 $log_io.puts "#{self.class}.expand_edges with cell_id of #{cell_id}" cell = @cell_manager[cell_id] $log_io.puts "[cell_id = #{cell_id}]" $log_io.puts " #{cell.configuration_string_full}, energy=#{cell.energy}, reach_flag=#{cell.reach_flag.to_s}, name=\"#{cell.name}\"" $log_io.puts '-' * 60 $log_io.puts 'Loop to find lowest edge: start' cell.atoms.each_with_index do |atom, atom_id| $log_io.puts '-' * 30 $log_io.puts "Migrating atom: index=#{atom_id}, element=#{atom.element}, site=#{atom.sitename}" @site_migrations[atom.site.name].each do |site, global_vector| $log_io.puts "-" * 10 $log_io.puts "To site=#{site}, global_vector=#{global_vector}" new_cell = cell.migrate(atom_id, site, global_vector) $log_io.puts " Generated cell: #{new_cell.configuration_string}(#{new_cell.configuration_string_full})" min_ope = minimum_operation(new_cell) $log_io.puts " minimum_operation id: #{min_ope}" identical_cell = new_cell.operate(@site_operations[min_ope]["operation"]) $log_io.puts " Symmetrically identical cell: #{identical_cell.configuration_string}(#{identical_cell.configuration_string_full})" ##Check including @cell_manager id = @cell_manager.index_same_atoms(new_cell) if id $log_io.puts "This cell is contained in @cell_manager." $log_io.puts "Truncated." next else $log_io.puts "This cell is not included in @cell_manager." end ##Check including @cell_candidates id = find_periodically_candidate(identical_cell) if id $log_io.puts "This cell is contained in @cell_candidates." energy = @cell_candidates[id].energy else $log_io.puts "This cell is not included in @cell_candidates." energy = Float::INFINITY end ## new_cell.name = new_cell.occupied_sitename new_cell.energy = energy new_cell_id = @cell_manager.add(new_cell) $log_io.puts "The cell is added into @cell_manager as index of #{new_cell_id}" energy = [@cell_manager[cell_id].energy, new_cell.energy].max note = "|[migration] elem=#{atom.element}" + ", #{atom.sitename}"+ " -> #{site}#{global_vector.to_a}\n" # + new_edge = Crysna::TransitionFinder::Edge.new( [cell_id, new_cell_id], energy, note ) $log_io.puts "New edge: cell_ids of (#{cell_id} -- #{new_cell_id}), energy=#{energy}" @edges.push(new_edge) conf_full = new_cell.configuration_string_full #@nodes[conf_full] = @graph.add_nodes(conf_full, :label => conf_full) #pp @nodes #pp cell.configuration_string_full #pp new_cell.configuration_string_full #puts #puts #pp @nodes[cell.configuration_string_full] #pp @nodes[new_cell.configuration_string_full] #@graph.add_edges(@nodes[cell.configuration_string_full], # @nodes[new_cell.configuration_string_full] # ) end end $log_io.puts 'Finding lowest edge loop end' $log_io.puts '-'*60 end
Return index of the cell which is periodically equal to the cell in @cell_candidates Not minimize(symid)-expressed cell is minimized to be found. Rerurn nil if no periodically equal cell is included.
# File lib/crysna/transitionfinder.rb, line 132 def find_periodically_candidate(cell) cell = cell.minimize(@site_operations) result = nil @cell_candidates.each_with_index do |ref_cell, index| if cell.periodically_equal? ref_cell result = index break end end return result end
Retrun [from, to] nodes. If the nodes of ‘edge’ is both reached or both not_reached, raise RuntimeError.
# File lib/crysna/transitionfinder.rb, line 146 def from_to_node_indices(edge) flag0 = @cell_manager[edge.nodes[0]].reach? flag1 = @cell_manager[edge.nodes[1]].reach? if flag0 == true && flag1 == false results = [edge.nodes[0], edge.nodes[1]] elsif flag0 == false && flag1 == true results = [edge.nodes[1], edge.nodes[0]] else raise RuntimeError, "Must not happen." end return results end
# File lib/crysna/transitionfinder.rb, line 305 def meet_transit_cells(start, truncate_energy) $log_io.puts "#{self.class}.find_path" cur_cell = @cell_candidates.find{|tmp| tmp.name == start} start_index = cur_index = @cell_manager.add(cur_cell) @cell_manager.reach(cur_index, nil) conf = cur_cell.configuration_string conf_full = cur_cell.configuration_string_full $log_io.puts "Starting point: #{start}" $log_io.puts "#{conf}(#{conf_full}) is added as cur_index of #{cur_index}" #@nodes[conf_full] = @graph.add_nodes( conf_full, # #:shape => "circle", # #:penwidth => 2, # #:fontsize => 12, # #:style => :filled, # #:fillcolor => "orange", # :label => conf_full #) write_log_state expand_edges(cur_index) iteration = 0 while true iteration += 1 $log_io.puts "-" * 10 + "Iteration #{iteration}" + "-" * 40 write_log_state $log_io.puts "-" * 60 $log_io.puts "reach_unreach_edges: " reach_unreach_edges.each do |edge| #$log_io.puts edge.inspect $log_io.puts " #{edge.nodes.to_s}" end $log_io.puts "cur_index:" + cur_index.to_s cur_cell = @cell_manager[cur_index] $log_io.puts "[#{cur_index}] #{cur_cell.configuration_string_full}, energy=#{cur_cell.energy}, reach_flag=#{cur_cell.reach_flag.to_s}, name=\"#{cur_cell.name}\"" if truncate_energy if @cell_manager.max{|cell| cell.energy} > truncate_energy break end else if @cell_manager.contain_periodically_shift_cells? $log_io.puts "truncate condition reached by contain_periodically_shift_cells." $log_io.puts @cell_manager.periodically_shift_indices.to_s break end end ##Choose the lowest edge $log_io.puts "-" * 30 $log_io.puts "reach_unreach_edges" reach_unreach_edges.each do |edge| $log_io.puts "-" * 10 $log_io.puts edge.note $log_io.puts "energy=#{edge.weight}" end new_edge = reach_unreach_edges.min_by {|edge| edge.weight} $log_io.puts "-" * 20 if new_edge $log_io.puts "Chosen edge: #{new_edge.note}" $log_io.puts "energy=#{new_edge.weight}" else $log_io.puts "Chosen edge: nil" $log_io.puts "break finding loop." break end ##Show cells state $log_io.puts "-" * 30 $log_io.puts "Cells state" from_index, to_index = from_to_node_indices(new_edge) @cell_manager.reach(to_index, new_edge) cur_index = to_index @cell_manager.cells.each_with_index do |cell, index| $log_io.puts "-" * 20 $log_io.puts "[#{index}] #{cell.configuration_string_full}, energy=#{cell.energy}, reach_flag=#{cell.reach_flag.to_s}, name=\"#{cell.name}\"" #$log_io.puts "-" * 10 $log_io.puts "Number of from_edges: #{cell.from_edges.size}" cell.from_edges.each do |edge| $log_io.puts "-" * 10 if edge $log_io.puts "Nodes: #{edge.nodes}" $log_io.puts edge.note else $log_io.puts "Nil edge(Starting point)" next end end end $log_io.puts "-" * 30 $log_io.puts "cur_index is set to be #{cur_index}" expand_edges(to_index) $log_io.puts "-" * 60 $log_io.puts "find_path loop: next" $log_io.puts "-" * 60 $log_io.puts end result = @cell_manager.periodically_shift_indices.find {|i| i.size > 1} result end
Return an index in @site_operations to minimuze expresion.
# File lib/crysna/transitionfinder.rb, line 259 def minimum_operation(cell) min_ope = @site_operations[0]["operation"] min_id = @site_operations[0]["operation_id"] $log_io.puts " in minimum_operation:" @site_operations.each do |site_ope| id = site_ope["operation_id"] ope = site_ope["operation"] $log_io.puts '-'*30 $log_io.puts " site_ope id: #{id}" min = cell.operate(min_ope) cur = cell.operate(ope) write_log_cell(cur) if cur < min min_id = id min_ope = ope $log_io.puts " min_exp is updated." end end $log_io.puts '-'*30 return min_id end
Generate and return an array of edges which connect between reached and not reached cells.
# File lib/crysna/transitionfinder.rb, line 118 def reach_unreach_edges results = [] @edges.each do |edge| if @cell_manager[edge.nodes[0]].reach? ^ @cell_manager[edge.nodes[1]].reach? results << edge end end return results end
# File lib/crysna/transitionfinder.rb, line 435 def two_indices(ary) results = [] ((ary.size) -1 ).times do |i| results << [ary[i], ary[i+1]] end results end
# File lib/crysna/transitionfinder.rb, line 72 def write_dot(goal_pair, dot_io) min_path_edges0 = two_indices(@cell_manager.ascend(goal_pair[0]).reverse) min_path_edges1 = two_indices(@cell_manager.ascend(goal_pair[1]).reverse) min_path_edges0 = each_sort(min_path_edges0) min_path_edges1 = each_sort(min_path_edges1) dot_io.puts "graph Transition {" ##nodes @cell_manager.cells.each_with_index do |cell, index| conf = cell.configuration_string_full node_id_str = sprintf("%3d", index) str = " #{node_id_str} [label = \"#{node_id_str}\\n#{conf}\\n#{cell.energy}\"];" dot_io.puts str end dot_io.puts ##normal edges @edges.each do |edge| index0 = edge.nodes[0] index1 = edge.nodes[1] indices_for_test = [index0, index1].sort #str = " #{index0} -- #{index1} " str = sprintf(" %3d -- %3d ", index0, index1) if min_path_edges0.include?(indices_for_test) str += "[label = \"#{edge.note.chomp}\", penwidth = 3, color = red];" elsif min_path_edges1.include?(indices_for_test) str += "[label = \"#{edge.note.chomp}\", penwidth = 3, color = green];" else str += "[label = \"#{edge.note.chomp}\"];" end dot_io.puts str end ##connect goal_pair str = " #{goal_pair[0]} -- #{goal_pair[1]} [label = \"Semi-identical\", penwidth = 3, color = blue, style = dotted];" dot_io.puts str ##close dot_io.puts "}" end
# File lib/crysna/transitionfinder.rb, line 298 def write_log_cell(cell) $log_io.puts " atoms of cell:" cell.atoms.each do |atom| $log_io.puts " - #{atom.site.name} #{atom.site.global_vector}" end end
# File lib/crysna/transitionfinder.rb, line 284 def write_log_state $log_io.puts "state" + "-" * 30 $log_io.puts "cells in cellmanager:" @cell_manager.cells.size.times do |i| $log_io.puts " [#{i}]" cell = @cell_manager[i] $log_io.puts " name: #{cell.name}" write_log_cell(cell) $log_io.puts " energy: #{cell.energy}" $log_io.puts end end
# File lib/crysna/transitionfinder.rb, line 413 def write_path(cell_ids, result_io) cell_ids.reverse.each do |index| cell = @cell_manager[index] edge = cell.from_edges[0] if edge result_io.puts "|" result_io.puts "#{edge.note}" result_io.puts "|" else end result_io.puts "[cell]" result_io.puts " atoms: #{cell.configuration_string_full}" min_ope = minimum_operation(cell) symid_cell = cell.operate(@site_operations[min_ope]["operation"]) result_io.puts " symmetrically identical cell: #{symid_cell.configuration_string}" + "(operation_id=#{min_ope})" result_io.puts " energy: #{cell.energy}" end end