Class: OpenTox::Compound

Inherits:
Substance show all
Defined in:
lib/compound.rb

Overview

Small molecules with defined chemical structures

Constant Summary collapse

DEFAULT_FINGERPRINT =
"MP2D"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.find_or_create_by(params) ⇒ Object

Overwrites standard Mongoid method to create fingerprints before database insertion



22
23
24
25
26
27
# File 'lib/compound.rb', line 22

def self.find_or_create_by params
  compound = self.find_or_initialize_by params
  compound.default_fingerprint_size = compound.fingerprint(DEFAULT_FINGERPRINT).size
  compound.save
  compound
end

.from_inchi(inchi) ⇒ OpenTox::Compound

Create a compound from InChI string

Parameters:

Returns:



136
137
138
139
# File 'lib/compound.rb', line 136

def self.from_inchi inchi
  smiles = obconversion(inchi,"inchi","can")
  smiles.empty? ? nil : Compound.find_or_create_by(:smiles => smiles)
end

.from_name(name) ⇒ OpenTox::Compound

Create a compound from name. Relies on an external service for name lookups.

Examples:

compound = OpenTox::Compound.from_name("Benzene")

Parameters:

  • name, (String)

    can be also an InChI/InChiKey, CAS number, etc

Returns:



154
155
156
# File 'lib/compound.rb', line 154

def self.from_name name
  Compound.from_smiles RestClientWrapper.get(File.join(PUBCHEM_URI,"compound","name",URI.escape(name),"property","CanonicalSMILES","TXT")).chomp
end

.from_sdf(sdf) ⇒ OpenTox::Compound

Create a compound from SDF

Parameters:

Returns:



144
145
146
147
# File 'lib/compound.rb', line 144

def self.from_sdf sdf
  # do not store sdf because it might be 2D
  Compound.from_smiles obconversion(sdf,"sdf","can")
end

.from_smiles(smiles) ⇒ OpenTox::Compound

Create a compound from smiles string

Examples:

compound = OpenTox::Compound.from_smiles("c1ccccc1")

Parameters:

Returns:



127
128
129
130
131
# File 'lib/compound.rb', line 127

def self.from_smiles smiles
  return nil if smiles.match(/\s/) # spaces seem to confuse obconversion and may lead to invalid smiles
  smiles = obconversion(smiles,"smi","can") # test if SMILES is correct and return canonical smiles (for compound comparisons)
  smiles.empty? ? nil : Compound.find_or_create_by(:smiles => smiles)
end

Instance Method Details

#calculate_properties(descriptors = PhysChem::OPENBABEL) ⇒ Array<Float>

Calculate physchem properties

Parameters:

  • list (Array<Hash>)

    of descriptors

Returns:



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/compound.rb', line 80

def calculate_properties descriptors=PhysChem::OPENBABEL
  calculated_ids = properties.keys
  # BSON::ObjectId instances are not allowed as keys in a BSON document.
  new_ids = descriptors.collect{|d| d.id.to_s} - calculated_ids
  descs = {}
  algos = {}
  new_ids.each do |id|
    descriptor = PhysChem.find id
    descs[[descriptor.library, descriptor.descriptor]]  = descriptor
    algos[descriptor.name] = descriptor
  end
  # avoid recalculating Cdk features with multiple values
  descs.keys.uniq.each do |k|
    descs[k].send(k[0].downcase,k[1],self).each do |n,v|
      properties[algos[n].id.to_s] = v # BSON::ObjectId instances are not allowed as keys in a BSON document.
    end
  end
  save
  descriptors.collect{|d| properties[d.id.to_s]}
end

#cidString

Get PubChem Compound Identifier (CID), obtained via REST call to PubChem

Returns:



229
230
231
232
# File 'lib/compound.rb', line 229

def cid
  update(:cid => RestClientWrapper.post(File.join(PUBCHEM_URI, "compound", "inchi", "cids", "TXT"),{:inchi => inchi}).strip) unless self["cid"] 
  self["cid"]
end

#fingerprint(type = DEFAULT_FINGERPRINT) ⇒ Array<String>

Create chemical fingerprint

Parameters:

  • fingerprint (String)

    type

Returns:



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/compound.rb', line 32

def fingerprint type=DEFAULT_FINGERPRINT
  unless fingerprints[type]
    return [] unless self.smiles
    if type == "MP2D" # http://openbabel.org/docs/dev/FileFormats/MolPrint2D_format.html#molprint2d-format
      fp = obconversion(smiles,"smi","mpd").strip.split("\t")
      name = fp.shift # remove Title
      fingerprints[type] = fp.uniq # no fingerprint counts
    elsif type== "MNA" # http://openbabel.org/docs/dev/FileFormats/Multilevel_Neighborhoods_of_Atoms_(MNA).html
      level = 2 # TODO: level as parameter, evaluate level 1, see paper
      fp = obconversion(smiles,"smi","mna","xL\"#{level}\"").split("\n")
      fp.shift # remove Title
      fingerprints[type] = fp
    else # standard fingerprints
      fp = OpenBabel::OBFingerprint.find_fingerprint(type)
      obmol = OpenBabel::OBMol.new
      obconversion = OpenBabel::OBConversion.new
      obconversion.set_in_format "smi"
      obconversion.read_string obmol, self.smiles
      result = OpenBabel::VectorUnsignedInt.new
      fp.get_fingerprint(obmol,result)
      # TODO: %ignore *::DescribeBits @ line 163 openbabel/scripts/openbabel-ruby.i
      #p OpenBabel::OBFingerprint.describe_bits(result)
      # convert result to a list of the bits that are set
      # from openbabel/scripts/python/pybel.py line 830
      # see also http://openbabel.org/docs/dev/UseTheLibrary/Python_Pybel.html#fingerprints
      result = result.to_a
      bitsperint = OpenBabel::OBFingerprint.getbitsperint()
      bits_set = []
      start = 1
      result.each do |x|
        i = start
        while x > 0 do
          bits_set << i if (x % 2) == 1
          x >>= 1
          i += 1
        end
        start += bitsperint
      end
      fingerprints[type] = bits_set
    end
    save
  end
  fingerprints[type]
end

#inchiString

Get InChI

Returns:



160
161
162
163
164
165
166
# File 'lib/compound.rb', line 160

def inchi
  unless self["inchi"]
    result = obconversion(smiles,"smi","inchi")
    update(:inchi => result.chomp) if result and !result.empty?
  end
  self["inchi"]
end

#inchikeyString

Get InChIKey

Returns:



170
171
172
173
# File 'lib/compound.rb', line 170

def inchikey
  update(:inchikey => obconversion(smiles,"smi","inchikey")) unless self["inchikey"]
  self["inchikey"]
end

#mg_to_mmol(mg) ⇒ Float

Convert mg to mmol

Returns:

  • (Float)

    value in mmol



242
243
244
# File 'lib/compound.rb', line 242

def mg_to_mmol mg
  mg.to_f/molecular_weight
end

#mmol_to_mg(mmol) ⇒ Float

Convert mmol to mg

Returns:

  • (Float)

    value in mg



236
237
238
# File 'lib/compound.rb', line 236

def mmol_to_mg mmol
  mmol.to_f*molecular_weight
end

#molecular_weightFloat

Calculate molecular weight of Compound with OB and store it in compound object

Returns:

  • (Float)

    molecular weight



248
249
250
251
# File 'lib/compound.rb', line 248

def molecular_weight
  mw_feature = PhysChem.find_or_create_by(:name => "Openbabel.MW")
  calculate_properties([mw_feature]).first
end

#namesArray<String>

Get all known compound names. Relies on an external service for name lookups.

Examples:

names = compound.names

Returns:



222
223
224
225
# File 'lib/compound.rb', line 222

def names
  update(:names => RestClientWrapper.get(File.join(PUBCHEM_URI,"compound","smiles",URI.escape(smiles),"synonyms","TXT")).split("\n")) #unless self["names"] 
  self["names"]
end

#pngimage/png

Get png image

Examples:

image = compound.png

Returns:

  • (image/png)

    Image data



209
210
211
212
213
214
215
216
# File 'lib/compound.rb', line 209

def png
  if self.png_id.nil?
   png = obconversion(smiles,"smi","_png2")
   file = Mongo::Grid::File.new(Base64.encode64(png), :filename => "#{id}.png", :content_type => "image/png")
   update(:png_id => $gridfs.insert_one(file))
  end
  Base64.decode64($gridfs.find_one(_id: self.png_id).data)
end

#sdfString

Get SDF

Returns:



184
185
186
187
188
189
190
191
192
# File 'lib/compound.rb', line 184

def sdf
  if self.sdf_id.nil? 
    sdf = obconversion(smiles,"smi","sdf")
    file = Mongo::Grid::File.new(sdf, :filename => "#{id}.sdf",:content_type => "chemical/x-mdl-sdfile")
    sdf_id = $gridfs.insert_one file
    update :sdf_id => sdf_id
  end
  $gridfs.find_one(_id: self.sdf_id).data
end

#smarts_match(smarts, count = false) ⇒ TrueClass, ...

Match a SMARTS substructure

Parameters:

  • smarts (String)
  • count (TrueClass, FalseClass) (defaults to: false)

    matches or return true/false

Returns:

  • (TrueClass, FalseClass, Fixnum)


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/compound.rb', line 105

def smarts_match smarts, count=false
  obconversion = OpenBabel::OBConversion.new
  obmol = OpenBabel::OBMol.new
  obconversion.set_in_format('smi')
  obconversion.read_string(obmol,self.smiles)
  smarts_pattern = OpenBabel::OBSmartsPattern.new
  smarts.collect do |sma|
    smarts_pattern.init(sma.smarts)
    if smarts_pattern.match(obmol)
      count ? value = smarts_pattern.get_map_list.to_a.size : value = 1
    else
      value = 0 
    end
    value
  end
end

#smilesString

Get (canonical) smiles

Returns:



177
178
179
180
# File 'lib/compound.rb', line 177

def smiles
  update(:smiles => obconversion(self["smiles"],"smi","can")) unless self["smiles"] 
  self["smiles"]
end

#svgimage/svg

Get SVG image

Returns:

  • (image/svg)

    Image data



196
197
198
199
200
201
202
203
# File 'lib/compound.rb', line 196

def svg
  if self.svg_id.nil?
   svg = obconversion(smiles,"smi","svg")
   file = Mongo::Grid::File.new(svg, :filename => "#{id}.svg", :content_type => "image/svg")
   update(:svg_id => $gridfs.insert_one(file))
  end
  $gridfs.find_one(_id: self.svg_id).data
end