Skip to content

Instantly share code, notes, and snippets.

@bjmorgan
Last active December 23, 2015 06:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bjmorgan/6592074 to your computer and use it in GitHub Desktop.
Save bjmorgan/6592074 to your computer and use it in GitHub Desktop.
Calculates activation energies from a set of `T y` data (see example `data.dat`) (in eV), according to the Arrhenius equation. Uses a linear regression around `r` points away from each point considered to calculate the local slope.
#!/usr/local/bin/ruby
# September 16, 2013
require 'optparse'
options = {}
executable_name = File.basename($PROGRAM_NAME)
optparse = OptionParser.new do |opts|
opts.banner = "Usage: #{executable_name} [options] data_filename"
options[:regression_length] = 1
opts.on( '-r' '--regression LENGTH', Integer, 'Set the length of local regression around central point (Default = 1)' ) do |l|
options[:regression_length] = l
end
options[:ignore_negatives] = false
opts.on( '-n', '--ignore-negatives', 'Ignore data entries with negative values' ) do
options[:ignore_negatives] = true
end
opts.on( '-h', '--help', 'Display this screen' ) do
puts opts
exit
end
end
optparse.parse!
$regression_length = options[:regression_length]
$ignore_negatives = options[:ignore_negatives]
$boltzmann_constant_in_eV = 8.6173324e-5 # eV K^-1
class Data_Point
@@number_of_points = 0
attr_reader :temp, :number
def initialize( temp, value )
@number = @@number_of_points
@@number_of_points += 1
@temp = temp
@value = value
end
def ln_value
Math::log( @value )
end
def inv_temp
1.0 / @temp
end
end
def sum( array )
return array.inject{ |sum, x| sum + x }
end
class Data_Set
def initialize( data )
@elements = data
end
def point( index )
return @elements[ index ]
end
def local_slope( this_point )
return nil if ( this_point.number - $regression_length < 0 ) or ( this_point.number + $regression_length >= @elements.length )
local_data_set = Data_Set.new( @elements[ (this_point.number - $regression_length )..(this_point.number + $regression_length ) ] )
return local_data_set.regression[0]
end
def local_activation_energy( this_point )
return [ this_point.temp, nil ] if local_slope( this_point ).nil?
return [ this_point.temp, - $boltzmann_constant_in_eV * local_slope( this_point ) ]
end
def activation_energies
@elements.collect { |point| local_activation_energy( point ) }
end
def regression
number_of_points = @elements.length
x = []
y = []
xx = []
xy = []
@elements.each do |element|
x << element.inv_temp
y << element.ln_value
xx << x[-1] * x[-1]
xy << x[-1] * y[-1]
end
sx = sum( x )
sy = sum( y )
sxx = sum( xx )
sxy = sum( xy )
slope = ( number_of_points * sxy - sx * sy ) / ( number_of_points * sxx - sx * sx )
intercept = ( sy - slope * sx ) / number_of_points
return [ slope, intercept ]
end
end
def slope( point1, point2 )
delta_inv_temp = point2.inv_temp - point1.inv_temp
delta_ln_value = point2.ln_value - point1.ln_value
slope = delta_ln_value / delta_inv_temp
return slope
end
def read_yx_data_from( data_file )
comment_re = /\s*\#/
negative_value_re = /\d+\s+-\d/
input_data = File.new( data_file, 'r' ).readlines.reject{ |line| comment_re.match( line ) }
input_data.reject!{ |line| negative_value_re.match( line ) } if $ignore_negatives
data = input_data.map{ |line| line.split.map{ |s| s.to_f } }.reject{ |line| line.empty? }.reverse
return Data_Set.new( data.collect{ |point| Data_Point.new( *point ) } )
end
if File.exist? ARGV[0]
data_file = ARGV[0]
else
abort( "#{ARGV[0]} not found")
end
data = read_yx_data_from( data_file )
data.activation_energies.each{ |temp, e_act| puts "#{temp} #{e_act}"}
# T D
462 8.31E-10
475 1.63E-09
488 1.41E-09
500 3.61E-09
512 1.03E-08
525 1.27E-08
538 2.46E-08
550 4.56E-08
562 8.18E-08
575 1.61E-07
588 4.31E-07
600 6.11E-07
612 1.23E-06
625 2.75E-06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment