One of the projects I’m working on for research has to deal with atomic potentials, or a way to try to describe the forces between atoms when they are in a certain configuration. The more realistic ones are fairly complicated functions.
One of the tricks in dealing with potentials is to try to plot some specific configurations, and see how the energy varies. ‘Wells’ represent stable points where atoms will tend to sit, while ‘humps’ are barriers between stable points that an atom must overcome somehow. The height of the hump gives you an idea of how stable the low points are.
Plotting one of these potentials is not super difficult, just looping over some positions and (in my case) outputting the positions and energies to a file, which is then plotted with gnuplot. One of the things I wanted to do, however, is to plot a bunch of variations with one of the parameters swept over a range of values. One way would be to create the potential as an object and modify the parameters that way. What I wanted to do, is have parameter file that I read in to setup the potential.
It turns out that this is essentially built into ruby, through the YAML methods that are designed for marshaling objects over various communication methods between computers/processes. All you have to do is
require 'yaml' # you may not need this line thing.to_yaml
and you get a string representation of the object. You can then save it to a file. After this, restoring the object is as simple as
Even complicated things
One of the neatest things about this is that nested objects translate seamlessly with this. For example, I have to following files that I work with.
#atom.rb class Atom attr_accessor :x, :y, :z def initialize @x = @y = @z = 0.0 end def r_ij(atom_two) r_squared = (@x-atom_two.x)**2 + (@y-atom_two.y)**2 + (@z-atom_two.z)**2 return Math.sqrt(r_squared) end end
#molecule.rb class Molecule attr_accessor :atoms def initialize @atoms =  end end
which lets you do this
imac:~/code/ruby-yaml brian$ irb irb(main):001:0> require 'atom' => true irb(main):002:0> require 'molecule' => true irb(main):003:0> require 'yaml' => true irb(main):004:0> a = Atom.new => #<Atom:0x59e304 @x=0.0, @z=0.0, @y=0.0> irb(main):005:0> a.x,a.y,a.z = [1.0,2.0,3.0] => [1.0, 2.0, 3.0] irb(main):006:0> b = Atom.new => #<Atom:0x598774 @x=0.0, @z=0.0, @y=0.0> irb(main):007:0> b.x,b.y,b.z = [4.0,5.0,6.0] => [4.0, 5.0, 6.0] irb(main):008:0> m = Molecule.new => #<Molecule:0x592b08 @atoms=> irb(main):009:0> m.atoms.push a => [#<Atom:0x59e304 @x=1.0, @z=3.0, @y=2.0>] irb(main):010:0> m.atoms.push b => [#<Atom:0x59e304 @x=1.0, @z=3.0, @y=2.0>, #<Atom:0x598774 @x=4.0, @z=6.0, @y=5.0>] irb(main):011:0> print save = m.to_yaml --- !ruby/object:Molecule atoms: - !ruby/object:Atom x: 1.0 y: 2.0 z: 3.0 - !ruby/object:Atom x: 4.0 y: 5.0 z: 6.0 => nil irb(main):013:0> new = YAML.load(save) => #<Molecule:0x586cf4 @atoms=[#<Atom:0x58726c @x=1.0, @z=3.0, @y=2.0>, #<Atom:0x586f38 @x=4.0, @z=6.0, @y=5.0>]> irb(main):014:0>
YAML is plain text
From my perspective, the real advantage of doing this is having the output in a plain text file. I can then edit the text in an editor and tweak things this way. I can also markup the text and run it through a template engine and generate a whole bunch of files. This was part of the magic behind my rake example, where I generated 800 or so configurations in an hour or so, which was very cool.
Edit: tweaked the formatting in the irb section to see the code better