Templator – Ruby Template System

June 18, 2007

Most simulation software is based on a text input file. Mostly I’m doing molecular dynamics simulation using LAMMPS. One input file from the samples is the following

# 3d Lennard-Jones melt

units       lj
atom_style  atomic

lattice     fcc 0.8442
region      box block 0 20 0 20 0 20
create_box  1 box
create_atoms    1
mass        1 1.0

velocity    all create 3.0 87287

pair_style  lj/cut 2.5
pair_coeff  1 1 1.0 1.0 2.5

neighbor    0.3 bin
neigh_modify    every 20 delay 0 check no

fix     1 all nve

dump        id all atom 10 dump.melt

thermo      50
run     250

So the input file has the setup of where the atoms go, what the temperature is, what type of simulation you are doing, where the output files record and what the filenames are. If you are doing a sweep of some parameter over a large range it is a bit annoying to have to make a bunch of copies of the file, tweak them, and keep everything in sync. I usually like to have the output files have the major parameter in the filename and it is very easy to get these out of sync with the actual parameter.

I started looking for a simple template system with Ruby, where I could specify a template file, and then somehow loop over a range of numbers and replace specific parts.

So, what I wanted was something to take a template file like this…

regular text with <% title %> embedded
inside that I want to try to replace
and variable p equals <% velocity %>
along with another <%title %> tag, since
thats the point. And another <% title%> for
good measure.

and pass it to a function, along with an option hash, and have it return this…

regular text with test.0.5 embedded
inside that I want to try to replace
and variable p equals 0.5
along with another test.0.5 tag, since
thats the point. And another test.0.5 for
good measure.

I wrote up some code to take care of this. The base code I came up with is this…

class Templator
  def Templator.generate(template, options)
    #template is text string of the template file
    #options is a hash of things to replace

    #currently not checking they match up

    tag_regex = /<%s*w+_*w*s*%>/
    hits = template.scan(tag_regex)
    tags = hits.map {|item| item.chomp('%>').reverse.chomp('%<').reverse.strip}
    tags.map! {|a| a.intern}
    tags.uniq!

    tags.inject(template) {|ntext,tag|
        ntext.gsub(Templator.symbol_to_tag_regex(tag),
        options[tag].to_s)}
  end

  def Templator.symbol_to_tag_regex(tag_name)
    Regexp.new('<%s*' + tag_name.to_s + 's*%>')
  end
end

This design is one that is going to be called from Ruby code, and not from the command line. I designed this to be extremely flexible, and easy to automate for, say, a hundred files or so. All you have to do is read in the template file, build the options hash, and pass the two to the Templator#generate method. Here is the driver code I used.

require 'templator'

template = File.open('template.txt') {|f| f.read}

0.5.step(1.5, 1.0) do |x|
  opt = {:title => "test." + x.to_s, :velocity => x}
  newtext = Templator.generate(template, opt)
  filename = "test-file-" + x.to_s
  File.open(filename,'w') {|f| f.print newtext }
end

There are certainly more options for different purposes, but this one did what I wanted. Feel free to use this code if you’re in a similar situation, or if there is something you think it should do that it doesn’t, let me know and I’ll take a look at it.

Advertisements