YAML for serialization

June 20, 2007

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.

Enter YAML

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

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.

class Atom
  attr_accessor :x, :y, :z

  def initialize
    @x = @y = @z = 0.0

  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)


class Molecule
  attr_accessor :atoms
  def initialize
    @atoms = []

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 
- !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>]>

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