OBRuby: A Ruby Interface to Open Babel

And the LORD said, Behold, the people is one, and they have all one language; and this they begin to do: and now nothing will be restrained from them, which they have imagined to do.

Genesis 11:6

Open Babel is a widely-used Open Source chemical informatics toolkit written in C++. Although originally designed as a molecular language translator, Open Babel also supports SMARTS pattern recognition, molecular fingerprints, molecular superposition, and other features as well.

Open Babel currently offers interfaces for two scripting languages: Python and Perl. Recently, Geoff Hutchison and I have been working to add Ruby to that list. This article reports our success in doing so and provides a glimpse of what might now be possible.

OBRuby

The upcoming release of Open Babel (version 2.1.0) will come complete with a Ruby interface. For those interested in trying it out sooner, a package called OBRuby can be downloaded now. OBRuby compiles against revision 1577 of the Open Babel SVN trunk. It has been tested with Linux and Mac OS X, and will probably work on Windows with minor modifications. The approach outlined here is known to fail with Open Babel 2.0.2.

OBRuby is a technology demonstration. The Ruby scripting support included with Open Babel 2.1.0 may differ in some details from OBRuby. My purpose in this article is simply to demonstrate what is now possible. Please read through the install scripts (they're short) to be sure you're comfortable with what they do.

Here was my OBRuby installation process:

  1. Download the Open Babel SVN trunk revision 1577 or later.
  2. cd trunk
  3. configure, make, (as root) make install
  4. (as root) ldconfig (necessary on my system - perhaps not on yours)
  5. cd OBRUBY_DIR
  6. ruby build.rb
  7. (as root) make install

One last wrinkle: the build.rb script included with OBRuby is something of a hack. It hardcodes the location of the Open Babel library on line 6:

@@ob_dir='/usr/local'

Change this line to match your Open Babel installation and you should be ready to go. make install places a single file, openbabel.so into your Ruby site_ruby directory.

To verify that the installation worked with IRB:

irb
irb(main):001:0> require 'openbabel'
=> true

A return value of true shows that the installation was successful. An error message about libopenbabel.so not being found indicates that your system can't find your Open Babel libraries. Be sure you've installed Open Babel and either run ldconfig or set LD_LIBRARY_PATH.

The majority of OBRuby was autogenerated by SWIG. A future article will detail how this was done - with an eye toward developing a Java interface to Open Babel.

Building an OBMol From SMILES

With installation out of the way, let's fire up OBRuby and take her for a test drive. The following code can either be entered with IRB or saved to a file and executed with the ruby interpreter:

require 'openbabel'
include OpenBabel

smi2mol = OBConversion.new
smi2mol.set_in_format("smi")

mol = OBMol.new
smi2mol.read_string(mol, 'CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C') # cholesterol, no chirality
mol.add_hydrogens

puts "Cholesterol has #{mol.num_atoms} atoms, including hydrogens."
puts "Its molecular weight is #{mol.get_mol_wt} and its molecular formula is #{mol.get_formula}."

This simple code illustrates some important points. All OBRuby classes reside in the OpenBabel module. These classes can be directly referenced by including the OpenBabel module. Also notice how Ruby underscore_delimited method names are used, rather than C++ UpperCamelCase names.

SMARTS Matching

One of the most useful features of Open Babel is its SMARTS pattern matching capability. This can conveniently be accessed from OBRuby by first instantiating an OBSmartsPattern, passing the SMARTS pattern of interest to the instance's init method, and retrieving the hit set:

require 'openbabel'
include OpenBabel

smi2mol = OBConversion.new
smi2mol.set_in_format("smi")

mol = OBMol.new
smiles = 'CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C' # cholesterol, no chirality
smi2mol.read_string(mol, smiles) 
mol.add_hydrogens

pattern=OBSmartsPattern.new
smarts = 'C1CCCCC1'

pattern.init(smarts)
pattern.match(mol)
hits = pattern.get_umap_list # => indicies of two cyclohexane rings

puts "Found #{hits.size} instances of the SMARTS pattern '#{smarts}' in the SMILES string #{smiles}. Here are the atom indices:"
hits.each_with_index do |hit, index|
  print "Hit #{index}: [ "

  hit.each do |atom_index|
    print "#{atom_index} "
  end

  puts "]"
end

Notice the Rubyesque each_with_index block that iterates over the elements in the hit set.

Running the above code produces the following output:

Found 2 instances of the SMARTS pattern 'C1CCCCC1' in the SMILES string CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C. Here are the atom indices:
Hit 0: [ 12 17 16 15 14 13 ]
Hit 1: [ 20 25 24 23 22 21 ]

Finding Your Way

Using a new library like OBRuby can take some getting used to. An excellent source of information is OpenBabel's online API documentation. Another source is Ruby itself.

For example, let's say you've instantiated an OBMol, but can't remember the exact name of the method that counts the number of atoms. Just use Object.methods.sort:

require 'openbabel'

mol = OpenBabel::OBMol.new

mol.methods.sort # => see output below

When run from Interactive Ruby (irb), this code produces the following alphabetized list of methods, which I've truncated:

... "is_corrected_for_ph", "kekulize", "kind_of?", "method", "methods", "new_atom", "new_perceive_kekule_bonds", "new_residue", "next_atom", "next_bond", "next_conformer", "next_internal_coord", "next_residue", "nil?", <strong>"num_atoms"</strong>, "num_bonds", "num_conformers", "num_edges", "num_hvy_atoms", "num_nodes", "num_residues", "num_rotors", "object_id", "perceive_bond_orders", "perceive_kekule_bonds", "private_methods", "protected_methods", "public_methods", "renumber_atoms", "reserve_atoms", "reset_visit_flags" ...

Conclusions

OBRuby combines the dynamic programming language Ruby with the highly-functional toolkit Open Babel. Further augmenting OBRuby's capabilities with the web application framework Rails and/or Ruby Chemistry Development Kit offers even more possibilities. Future articles will bring some of them to life.