msplinter 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +23 -0
- data/README.md +41 -0
- data/Rakefile +19 -0
- data/bin/msplinter +69 -0
- data/lib/msplinter/commandline.rb +1 -0
- data/lib/msplinter/version.rb +3 -0
- data/lib/msplinter.rb +4 -0
- data/lib/rubabel/molecule/fragmentable.rb +160 -0
- data/msplinter.gemspec +36 -0
- data/spec/rubabel/molecule/fragmentable_spec.rb +200 -0
- data/spec/spec_helper.rb +28 -0
- metadata +146 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 549e4f5c83a9065e97cc542ee98a6c69c54d0443
|
4
|
+
data.tar.gz: a93e6a4567344e57b8a6d3bdfc86bb766200118d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4b705b21e3db91c1f5b9862b4b02a01d09140b1b827244c98cf86e89bd1ce6558a169c6a8b3f702b553aa2f7d4094ffa81952cef83c138cb519183bc4e431dc9
|
7
|
+
data.tar.gz: 38695117916f6328b555d78338d114a8994ae859b0bbb604fba059ed08eb9498f8eadddc530a220ea6e6a56d3cca905f60ff9389737c518dc1fe09f16e2b1754
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2013 Brigham Young University
|
2
|
+
Authored by: John T. Prince
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# msplinter
|
2
|
+
|
3
|
+
[![Gem Version][GV img]][Gem Version]
|
4
|
+
[![Dependency Status][DS img]][Dependency Status]
|
5
|
+
[![Code Climate][CC img]][Code Climate]
|
6
|
+
[![Coverage Status][CS img]][Coverage Status]
|
7
|
+
|
8
|
+
Predicts how molecules will fragment in a mass spectrometer. Currently
|
9
|
+
focused on lipid fragmentation under CID, HCD or PQD.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
msplinter is built on rubabel, which is built on openbabel. To get the
|
14
|
+
necessary openbabel dependencies on ubuntu/debian:
|
15
|
+
|
16
|
+
sudo apt-get install openbabel libopenbabel-dev cmake make curl
|
17
|
+
|
18
|
+
Then
|
19
|
+
|
20
|
+
gem install msplinter
|
21
|
+
|
22
|
+
See [the "Installing" section](https://github.com/princelab/rubabel) for
|
23
|
+
complete instructions for installing rubabel and openbabel.
|
24
|
+
|
25
|
+
## Commandline Usage
|
26
|
+
|
27
|
+
Type 'msplinter' with no arguments to see a help message.
|
28
|
+
|
29
|
+
## Copyright
|
30
|
+
|
31
|
+
MIT license. See LICENSE.txt for further details.
|
32
|
+
|
33
|
+
[Gem Version]: https://rubygems.org/gems/msplinter
|
34
|
+
[Dependency Status]: https://gemnasium.com/princelab/msplinter
|
35
|
+
[Code Climate]: https://codeclimate.com/github/princelab/msplinter
|
36
|
+
[Coverage Status]: https://coveralls.io/r/princelab/msplinter
|
37
|
+
|
38
|
+
[GV img]: https://badge.fury.io/rb/msplinter.png
|
39
|
+
[DS img]: https://gemnasium.com/princelab/msplinter.png
|
40
|
+
[CC img]: https://codeclimate.com/github/princelab/msplinter.png
|
41
|
+
[CS img]: https://coveralls.io/repos/princelab/msplinter/badge.png?branch=master
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rspec/core'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
6
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
7
|
+
end
|
8
|
+
|
9
|
+
task :default => :spec
|
10
|
+
|
11
|
+
require 'rdoc/task'
|
12
|
+
Rake::RDocTask.new do |rdoc|
|
13
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
14
|
+
|
15
|
+
rdoc.rdoc_dir = 'rdoc'
|
16
|
+
rdoc.title = "rubabel #{version}"
|
17
|
+
rdoc.rdoc_files.include('README*')
|
18
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
19
|
+
end
|
data/bin/msplinter
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'trollop'
|
4
|
+
require 'rubabel'
|
5
|
+
require 'rubabel/molecule/fragmentable'
|
6
|
+
|
7
|
+
default_ph = 2.5
|
8
|
+
|
9
|
+
Fragment = Struct.new(:frag, :id, :title, :mz, :mass, :charge, :smiles, :pairing)
|
10
|
+
|
11
|
+
progname = File.basename(__FILE__)
|
12
|
+
|
13
|
+
parser = Trollop::Parser.new do
|
14
|
+
banner "usage: #{progname} [OPTIONS|RULES] <SMARTS> ..."
|
15
|
+
text "\noptions:"
|
16
|
+
opt :ph, "the pH to use (experimental option)", :default => default_ph
|
17
|
+
opt :images, "print out svg images of fragments"
|
18
|
+
opt :format, "format of the molecules", :default => 'smiles'
|
19
|
+
#opt :uniq, "no repeated fragments", :default => false
|
20
|
+
text "\nrules:"
|
21
|
+
Rubabel::Molecule::Fragmentable::RULES.each do |rule|
|
22
|
+
opt rule, rule.to_s.gsub("_",' ')
|
23
|
+
end
|
24
|
+
text "\nexample:"
|
25
|
+
text " #{progname} -xeh 'CCC(=O)OCCC' 'CCC(=O)OCCC(=O)O'"
|
26
|
+
end
|
27
|
+
|
28
|
+
options = parser.parse(ARGV)
|
29
|
+
opts = {rules: []}
|
30
|
+
opts[:uniq] = options.delete(:uniq)
|
31
|
+
ph = options.delete(:ph)
|
32
|
+
opts[:rules] = Rubabel::Molecule::Fragmentable::RULES.map do |rule|
|
33
|
+
rule if options["#{rule}_given".to_sym]
|
34
|
+
end.compact
|
35
|
+
|
36
|
+
if ARGV.size == 0
|
37
|
+
parser.educate && exit
|
38
|
+
end
|
39
|
+
|
40
|
+
ARGV.each do |smiles|
|
41
|
+
mol = Rubabel[smiles, options[:format].to_sym]
|
42
|
+
puts "\nmolecule: #{mol.csmiles}"
|
43
|
+
mol.correct_for_ph!(ph)
|
44
|
+
puts "at ph #{ph}: #{mol.csmiles}"
|
45
|
+
fragment_sets = mol.fragment(opts)
|
46
|
+
puts %w(mz mass charge title smiles pairing).join("\t")
|
47
|
+
frags = []
|
48
|
+
fragment_sets.each_with_index do |frag_set,i|
|
49
|
+
frag_set.each_with_index do |frag,j|
|
50
|
+
unless frag.charge == 0
|
51
|
+
mz = (frag.mass / frag.charge).round(5)
|
52
|
+
end
|
53
|
+
|
54
|
+
frag.title = "#{i}-#{j}pair_" + (mz ? "#{mz}_mz" : "#{frag.mass.round(3)}_Mass")
|
55
|
+
frag_obj = Fragment.new(frag, frag.title, frag.title, mz, frag.exact_mass, frag.charge, frag.csmiles, i)
|
56
|
+
frags << frag_obj
|
57
|
+
end
|
58
|
+
end
|
59
|
+
frags = frags.sort_by {|frag| [-frag.charge, frag.mz] }
|
60
|
+
if options[:images]
|
61
|
+
frags.each do |frag|
|
62
|
+
fn = "#{frag.title}.svg"
|
63
|
+
frag.frag.write(fn)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
frags.each do |frag|
|
67
|
+
puts [:mz, :mass, :charge, :title, :smiles, :pairing].map {|cat| frag.send(cat) }.join("\t")
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
|
data/lib/msplinter.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Rubabel
|
4
|
+
class Molecule
|
5
|
+
module Fragmentable
|
6
|
+
|
7
|
+
#RULES = Set[:cod, :codoo, :oxe, :oxepd, :oxh]
|
8
|
+
RULES = Set[:cod, :codoo, :oxe, :oxepd, :oxh, :oxhpd]
|
9
|
+
|
10
|
+
DEFAULT_OPTIONS = {
|
11
|
+
rules: RULES,
|
12
|
+
errors: :remove,
|
13
|
+
# return only the set of unique fragments
|
14
|
+
uniq: false,
|
15
|
+
}
|
16
|
+
|
17
|
+
# molecules and fragments should all have hydrogens added (add_h!)
|
18
|
+
# before calling this method
|
19
|
+
#
|
20
|
+
# For instance, water loss with double bond formation is not allowable
|
21
|
+
# for NCC(O)CC => CCC=C[NH2+], presumably because of the lone pair and
|
22
|
+
# double bond resonance.
|
23
|
+
def allowable_fragmentation?(frags)
|
24
|
+
self.num_atoms(true) == frags.reduce(0) {|cnt,fr| cnt + fr.num_atoms(true) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# splits the molecule between the carbon and carbon_nbr, adds a double
|
28
|
+
# bond between the carbon and oxygen, and moves whatever was on the
|
29
|
+
# oxygen (e.g., an OH or a charge) to the carbon_nbr. Returns two new
|
30
|
+
# molecules.
|
31
|
+
def carbonyl_oxygen_dump(carbon, oxygen, carbon_nbr)
|
32
|
+
appendage = oxygen.atoms.find {|a| a.el != :C }
|
33
|
+
if oxygen.charge != 0
|
34
|
+
ocharge = oxygen.charge
|
35
|
+
end
|
36
|
+
nmol = self.dup
|
37
|
+
new_oxygen = nmol.atom(oxygen.id)
|
38
|
+
new_carbon = nmol.atom(carbon.id)
|
39
|
+
new_carbon_nbr = nmol.atom(carbon_nbr.id)
|
40
|
+
new_appendage = nmol.atom(appendage.id) if appendage
|
41
|
+
nmol.delete_bond(new_carbon.get_bond(new_carbon_nbr))
|
42
|
+
if new_appendage
|
43
|
+
nmol.delete_bond(new_oxygen.get_bond(new_appendage))
|
44
|
+
nmol.add_bond!(new_carbon_nbr, new_appendage)
|
45
|
+
end
|
46
|
+
if ocharge
|
47
|
+
new_carbon_nbr.charge += ocharge
|
48
|
+
new_oxygen.charge -= ocharge
|
49
|
+
end
|
50
|
+
new_carbon.get_bond(new_oxygen).bond_order = 2
|
51
|
+
nmol.split
|
52
|
+
end
|
53
|
+
|
54
|
+
# breaks the bond and gives the electrons to the oxygen
|
55
|
+
def carbon_oxygen_esteal(carbon, oxygen)
|
56
|
+
nmol = self.dup
|
57
|
+
ncarbon = nmol.atom(carbon.id)
|
58
|
+
noxygen = nmol.atom(oxygen.id)
|
59
|
+
|
60
|
+
is_carboxyl = noxygen.carboxyl_oxygen?
|
61
|
+
|
62
|
+
nmol.delete_bond(ncarbon, noxygen)
|
63
|
+
ncarbon.remove_a_hydride!
|
64
|
+
noxygen.remove_a_proton!
|
65
|
+
nmol.split
|
66
|
+
end
|
67
|
+
|
68
|
+
# returns the duplicated molecule and the equivalent atoms
|
69
|
+
def dup_molecule(atoms=[])
|
70
|
+
nmol = self.dup
|
71
|
+
[nmol, atoms.map {|old_atom| nmol.atom(old_atom.id) }]
|
72
|
+
end
|
73
|
+
|
74
|
+
# returns molecules created from splitting between the electrophile and
|
75
|
+
# the center and where the bond order is increased between the center
|
76
|
+
# and center_nbr
|
77
|
+
def break_with_double_bond(electrophile, center, center_nbr)
|
78
|
+
(nmol, (nele, ncarb, ncarb_nbr)) = self.dup_molecule([electrophile, center, center_nbr])
|
79
|
+
nmol.delete_bond(nele, ncarb)
|
80
|
+
ncarb_nbr.get_bond(ncarb) + 1
|
81
|
+
nmol.split
|
82
|
+
end
|
83
|
+
|
84
|
+
# an empty array is returned if there are no fragments generated.
|
85
|
+
# Hydrogens are added at a pH of 7.4, unless they have already been
|
86
|
+
# added.
|
87
|
+
#
|
88
|
+
# :rules => queryable by :include? set of rules
|
89
|
+
# :uniq => false
|
90
|
+
# :errors => :remove | :fix | :ignore (default is :remove)
|
91
|
+
def fragment(opts={})
|
92
|
+
only_uniqs = true
|
93
|
+
opts = DEFAULT_OPTIONS.merge(opts)
|
94
|
+
opts[:rules].each do |rule|
|
95
|
+
raise ArgumentError, "bad rule: #{rule}" unless RULES.include?(rule)
|
96
|
+
end
|
97
|
+
|
98
|
+
had_hydrogens = self.h_added?
|
99
|
+
self.correct_for_ph!(7.4) unless had_hydrogens
|
100
|
+
self.remove_h!
|
101
|
+
|
102
|
+
fragment_sets = []
|
103
|
+
|
104
|
+
if opts[:rules].any? {|r| [:cod, :codoo].include?(r) }
|
105
|
+
self.each_match("C[O;h1,O]", only_uniqs) do |carbon, oxygen|
|
106
|
+
carbon.atoms.select {|a| a.el == :C }.each do |carbon_nbr|
|
107
|
+
fragment_sets << carbonyl_oxygen_dump(carbon, oxygen, carbon_nbr)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
if opts[:rules].any? {|r| [:oxe].include?(r) }
|
112
|
+
self.each_match("C-O", only_uniqs) do |carbon, oxygen|
|
113
|
+
fragment_sets << carbon_oxygen_esteal(carbon, oxygen)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# right now implemented so that a beta hydrogen has to be availabe for
|
117
|
+
# extraction
|
118
|
+
if opts[:rules].any? {|r| [:oxh].include?(r) }
|
119
|
+
self.each_match("C[C,O]-O", only_uniqs) do |beta_c, center, oxygen|
|
120
|
+
next unless beta_c.hydrogen_count > 0
|
121
|
+
fragment_sets << break_with_double_bond(oxygen, center, beta_c)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
if opts[:rules].any? {|r| [:oxhpd].include?(r) }
|
125
|
+
self.each_match("C-O-P-O", only_uniqs) do |carbon, alc_oxy, phosphate, beta_carb_oxy|
|
126
|
+
next unless beta_carb_oxy.hydrogen_count > 0
|
127
|
+
frag_set = break_with_double_bond(alc_oxy, phosphate, beta_carb_oxy)
|
128
|
+
frag_set.map! &:convert_dative_bonds!
|
129
|
+
fragment_sets << frag_set
|
130
|
+
end
|
131
|
+
end
|
132
|
+
if opts[:rules].any? {|r| [:oxepd].include?(r) }
|
133
|
+
self.each_match("P-O-C", only_uniqs) do |phosphate, oxygen, carbon|
|
134
|
+
frag_set = carbon_oxygen_esteal(phosphate, oxygen)
|
135
|
+
frag_set.map! &:convert_dative_bonds!
|
136
|
+
fragment_sets << frag_set
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
case opts[:errors]
|
141
|
+
when :remove
|
142
|
+
fragment_sets.select! {|set| allowable_fragmentation?(set) }
|
143
|
+
when :fix
|
144
|
+
raise NotImplementedError
|
145
|
+
when :ignore # do nothing
|
146
|
+
end
|
147
|
+
|
148
|
+
self.remove_h!
|
149
|
+
if opts[:uniq]
|
150
|
+
# TODO: impelent properly
|
151
|
+
raise NotImplementedError
|
152
|
+
#fragment_sets = fragment_sets.uniq_by(&:csmiles)
|
153
|
+
end
|
154
|
+
|
155
|
+
fragment_sets
|
156
|
+
end
|
157
|
+
end
|
158
|
+
include Fragmentable
|
159
|
+
end
|
160
|
+
end # Rubabel
|
data/msplinter.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'msplinter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "msplinter"
|
8
|
+
spec.version = Msplinter::VERSION
|
9
|
+
spec.authors = ["John T. Prince"]
|
10
|
+
spec.email = ["jtprince@gmail.com"]
|
11
|
+
spec.description = %q{Predicts how molecules will fragment in a mass spectrometer. Currently focused on lipid fragmentation under CID, HCD or PQD.}
|
12
|
+
spec.summary = %q{Predicts how molecules will fragment in a mass spectrometer}
|
13
|
+
spec.homepage = "https://github.com/princelab/msplinter"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
[
|
22
|
+
["rubabel", "~> 0.4.1"],
|
23
|
+
].each do |args|
|
24
|
+
spec.add_dependency(*args)
|
25
|
+
end
|
26
|
+
[
|
27
|
+
["bundler", "~> 1.3"],
|
28
|
+
["rake"],
|
29
|
+
["rspec", "~> 2.13.0"],
|
30
|
+
["rdoc", "~> 3.12"],
|
31
|
+
["simplecov"],
|
32
|
+
].each do |args|
|
33
|
+
spec.add_development_dependency(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'rubabel'
|
4
|
+
require 'rubabel/molecule/fragmentable'
|
5
|
+
|
6
|
+
$VERBOSE = nil
|
7
|
+
|
8
|
+
describe Rubabel::Molecule::Fragmentable do
|
9
|
+
|
10
|
+
# :peroxy_to_carboxy
|
11
|
+
# :oxygen_asymmetric_sp3, :nitrogen_asymmetric_sp3,
|
12
|
+
# :internal_phosphoester
|
13
|
+
|
14
|
+
describe 'fragmentation rules' do
|
15
|
+
# coenzyme: CC1=CC(=O)C=CC1=O
|
16
|
+
# 2-methylcyclohexa-2,5-diene-1,4-dione
|
17
|
+
|
18
|
+
#let(:test_mol) { "COP(=O)(O)OCNCOCC(OO)C(=O)O" }
|
19
|
+
|
20
|
+
it 'raises an error for a bad rule' do
|
21
|
+
mol = Rubabel["CCNC"]
|
22
|
+
expect { mol.fragment(rules: [:wackiness]) }.to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'cod: carbonyl appendage dump' do
|
26
|
+
# a primary oxygen or peroxide => C=O appendage dump
|
27
|
+
|
28
|
+
specify 'cod: primary alcohol' do
|
29
|
+
mol = Rubabel["NCC(O)CC"]
|
30
|
+
frags = mol.fragment(rules: [:cod])
|
31
|
+
frags.flatten(1).map(&:csmiles).should == ["C[NH3+]", "CCC=O", "C([NH3+])C=O", "CC"]
|
32
|
+
end
|
33
|
+
|
34
|
+
specify 'peroxide' do
|
35
|
+
mol = Rubabel["NCC(OO)CC"]
|
36
|
+
frags = mol.fragment(rules: [:codoo])
|
37
|
+
frags.flatten(1).map(&:csmiles).should == ["OC[NH3+]", "CCC=O", "C([NH3+])C=O", "CCO"]
|
38
|
+
end
|
39
|
+
|
40
|
+
specify 'cod: carboxylate' do
|
41
|
+
mol = Rubabel["CCC(=O)O"]
|
42
|
+
pieces = mol.fragment(rules: [:cod])
|
43
|
+
pieces.flatten(1).map(&:csmiles).should == ["[CH2-]C", "O=C=O"]
|
44
|
+
end
|
45
|
+
|
46
|
+
specify 'cod: carboxylic acid' do
|
47
|
+
mol = Rubabel["CCC(=O)O"]
|
48
|
+
mol.add_h!(1.5)
|
49
|
+
pieces = mol.fragment(rules: [:cod])
|
50
|
+
pieces.flatten(1).map(&:csmiles).should == ["CC", "O=C=O"]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'oxe: oxygen electron stealing' do
|
55
|
+
# oxygen just steals the electron pair it is attached to. This
|
56
|
+
# typically results in a negatively charged oxygen and a positively
|
57
|
+
# charged carbo-cation.
|
58
|
+
specify 'ether to ions' do
|
59
|
+
mol = Rubabel["CCOCCN"]
|
60
|
+
frag_set = mol.fragment(rules: [:oxe])
|
61
|
+
frags = frag_set.first
|
62
|
+
frags.first.csmiles.should == "C[CH2+]"
|
63
|
+
frags.last.csmiles.should == '[O-]CC[NH3+]'
|
64
|
+
frags.first.formula.should == 'C2H5+'
|
65
|
+
frags.last.formula.should == 'C2H7NO'
|
66
|
+
frags.first.exact_mass.should be_within(1e-6).of(29.03912516)
|
67
|
+
frags.last.exact_mass.should be_within(1e-6).of(61.052763849)
|
68
|
+
end
|
69
|
+
|
70
|
+
specify 'ester to ions' do
|
71
|
+
mol = Rubabel["CCOC(=O)CCN"]
|
72
|
+
frag_set = mol.fragment(rules: [:oxe])
|
73
|
+
ff = frag_set.first
|
74
|
+
ff.first.csmiles.should == 'C[CH2+]'
|
75
|
+
ff.last.csmiles.should == '[O-]C(=O)CC[NH3+]'
|
76
|
+
ff.first.formula.should == "C2H5+"
|
77
|
+
|
78
|
+
ff.last.formula.should == "C3H7NO2"
|
79
|
+
ff.first.exact_mass.should be_within(1e-6).of(29.03912516035)
|
80
|
+
ff.last.exact_mass.should be_within(1e-6).of(89.04767846841)
|
81
|
+
end
|
82
|
+
|
83
|
+
specify 'carboxyl group' do
|
84
|
+
mol = Rubabel["CCC(=O)O"]
|
85
|
+
frag_set = mol.fragment(rules: [:oxe])
|
86
|
+
ff = frag_set.first
|
87
|
+
ff.first.csmiles.should == 'CC[C+]=O'
|
88
|
+
# this is a carboxylate oxygen that falls off
|
89
|
+
ff.last.csmiles.should == '[O-2]'
|
90
|
+
#ff.first.formula.should == "C3H5O"
|
91
|
+
ff.first.formula.should == "C3H5O+"
|
92
|
+
ff.first.exact_mass.should be_within(1e-6).of(57.034039779909996)
|
93
|
+
#ff.last.formula.should == "O"
|
94
|
+
ff.last.formula.should == "O--"
|
95
|
+
end
|
96
|
+
|
97
|
+
specify 'phosphodiester' do
|
98
|
+
mol = Rubabel["CC(COP(=O)([O-])OCCN"]
|
99
|
+
frag_set = mol.fragment(rules: [:oxepd])
|
100
|
+
ff = frag_set.first
|
101
|
+
ff.first.csmiles.should == '[O-]CCC'
|
102
|
+
#ff.last.csmiles.should == '[NH3+]CCO[P](=O)=O'
|
103
|
+
ff.last.csmiles.should == "[NH3+]CCOP(=O)=O"
|
104
|
+
#ff.first.formula.should == 'C3H7O'
|
105
|
+
ff.first.formula.should == 'C3H7O-'
|
106
|
+
ff.first.exact_mass.should be_within(1e-6).of(59.049689844)
|
107
|
+
#ff.last.formula.should == 'C2H7NO3P'
|
108
|
+
ff.last.formula.should == 'C2H7NO3P+'
|
109
|
+
ff.last.exact_mass.should be_within(1e-6).of(124.016354719)
|
110
|
+
|
111
|
+
mol = Rubabel["CCCOP(=O)(OCC[N+](C)(C)C)[O-]"]
|
112
|
+
frag_set = mol.fragment(rules: [:oxepd, :oxe])
|
113
|
+
# some of these don't like right on first inspection, but that is
|
114
|
+
# because we 'converted dative bonds' meaning + and - next to each
|
115
|
+
# other are allowed to cancel one another out!
|
116
|
+
frag_set.size.should == 4
|
117
|
+
mols = frag_set.flatten
|
118
|
+
mols.map(&:csmiles).should == ["CC[CH2+]", "[O-]P(=O)(OCC[N+](C)(C)C)[O-]", "CCCOP(=O)([O-])[O-]", "[CH2+]C[N+](C)(C)C", "[O-]CCC", "O=P(=O)OCC[N+](C)(C)C", "CCCOP(=O)=O", "[O-]CC[N+](C)(C)C"]
|
119
|
+
mols.map(&:formula).should == ["C3H7+", "C5H13NO4P-", "C3H7O4P--", "C5H13N++", "C3H7O-", "C5H13NO3P+", "C3H7O3P", "C5H13NO"]
|
120
|
+
mols.map(&:exact_mass).zip([43.05477522449, 182.05821952995, 138.00819533273, 87.10479942171, 59.04968984405, 166.06330491039, 122.01328071317, 103.09971404127]) do |act, exp|
|
121
|
+
act.should be_within(1e-6).of(exp)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# this is really a subset of oxygen bond stealing: if the negatively
|
128
|
+
# charged oxygen can rip off a nearby proton, it will.
|
129
|
+
describe 'oxh: oxygen alpha/beta/gamma hydrogen stealing' do
|
130
|
+
specify 'primary alcohol giving water loss' do
|
131
|
+
mol = Rubabel["CC(O)CCN"]
|
132
|
+
frags = mol.fragment(rules: [:oxh])
|
133
|
+
ff = frags.first
|
134
|
+
ff.first.csmiles.should == 'C=CCC[NH3+]'
|
135
|
+
ff.last.csmiles.should == 'O'
|
136
|
+
ll = frags.last
|
137
|
+
ll.first.csmiles.should == 'CC=CC[NH3+]'
|
138
|
+
ll.last.csmiles.should == 'O'
|
139
|
+
#ff.first.formula.should == 'C4H10N'
|
140
|
+
ff.first.formula.should == 'C4H10N+'
|
141
|
+
ff.first.exact_mass.should be_within(1e-6).of(72.0813243255)
|
142
|
+
end
|
143
|
+
|
144
|
+
specify 'peroxide carbonyl formation (or peroxide formation [that what we want??])' do
|
145
|
+
# do we really see peroxide formation? Tamil didn't include this in
|
146
|
+
# the rules but it follows from the broad way for creating these
|
147
|
+
# rules. Can prohibit peroxide formation in future if necessary...
|
148
|
+
mol = Rubabel["CC(OO)CCN"]
|
149
|
+
frags = mol.fragment(rules: [:oxh])
|
150
|
+
mols = frags.flatten
|
151
|
+
mols.map(&:csmiles).should == ["C=CCC[NH3+]", "OO", "CC(=O)CC[NH3+]", "O", "CC=CC[NH3+]", "OO"]
|
152
|
+
mols.map(&:formula).should == ["C4H10N+", "H2O2", "C4H10NO+", "H2O", "C4H10N+", "H2O2"]
|
153
|
+
#mols.map(&:formula).should == ["C4H10N", "H2O2", "C4H10NO", "H2O", "C4H10N", "H2O2"]
|
154
|
+
mols.map(&:exact_mass).zip([72.081324325, 34.005479304, 88.076238945, 18.010564684, 72.081324325, 34.005479304]) do |act, exp|
|
155
|
+
act.should be_within(1e-6).of(exp)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
specify 'ether to alcohol, ignoring errors' do
|
160
|
+
# this is a good example of a 'disallowed structure' where the
|
161
|
+
# formula's do not match up to the original formulas
|
162
|
+
mol = Rubabel["CCOCCN"]
|
163
|
+
frags = mol.fragment(rules: [:oxh], errors: :ignore)
|
164
|
+
mols = frags.flatten
|
165
|
+
mols.map(&:csmiles).should == ["C=C", "OCC[NH3+]", "CCO", "C=C[NH2+]"]
|
166
|
+
end
|
167
|
+
|
168
|
+
specify 'ether to alcohol, removing errors' do
|
169
|
+
mol = Rubabel["CCOCCN"]
|
170
|
+
frags = mol.fragment(rules: [:oxh])
|
171
|
+
mols = frags.flatten
|
172
|
+
mols.map(&:csmiles).should == ["C=C", "OCC[NH3+]"]
|
173
|
+
end
|
174
|
+
|
175
|
+
specify 'ester to alcohol' do
|
176
|
+
mol = Rubabel["CC(=O)OCCCN"]
|
177
|
+
frags = mol.fragment(rules: [:oxh])
|
178
|
+
mols = frags.flatten
|
179
|
+
mols.map(&:csmiles).should == ["C=C=O", "OCCC[NH3+]", "CC(=O)O", "C=CC[NH3+]"]
|
180
|
+
#mols.map(&:formula).should == ["C2H2O", "C3H10NO", "C2H4O2", "C3H8N"]
|
181
|
+
mols.map(&:formula).should == ["C2H2O", "C3H10NO+", "C2H4O2", "C3H8N+"]
|
182
|
+
mols.map(&:exact_mass).zip([42.010564684, 76.076238945, 60.021129368000004, 58.065674261]) do |act,exp|
|
183
|
+
act.should be_within(1e-6).of(exp)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
specify 'phosphodiester (right now needs very low pH and NOT SURE WORKING PROPERLY)' do
|
188
|
+
mol = Rubabel["CC(COP(=O)(O)OCCCN"]
|
189
|
+
mol.add_h!(1.0)
|
190
|
+
frags = mol.fragment(rules: [:oxhpd])
|
191
|
+
frags = frags.flatten
|
192
|
+
frags.map(&:csmiles).should == ["CCCO", "[NH3+]CCCOP(=O)=O", "CCCOP(=O)=O", "OCCC[NH3+]"]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
require 'rspec/core/formatters/progress_formatter'
|
8
|
+
# doesn't say so much about pending guys
|
9
|
+
class QuietPendingFormatter < RSpec::Core::Formatters::ProgressFormatter
|
10
|
+
def example_pending(example)
|
11
|
+
output.print pending_color('*')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'rspec/core/formatters/documentation_formatter'
|
16
|
+
class QuietPendingDocFormatter < RSpec::Core::Formatters::DocumentationFormatter
|
17
|
+
def example_pending(example)
|
18
|
+
output.puts pending_color( "<pending>: #{example.execution_result[:pending_message]}" )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
RSpec.configure do |config|
|
23
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
24
|
+
config.formatter = QuietPendingDocFormatter
|
25
|
+
config.color = true
|
26
|
+
end
|
27
|
+
|
28
|
+
TESTFILES = File.dirname(__FILE__) + "/testfiles"
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: msplinter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John T. Prince
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-06-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubabel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.13.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.13.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rdoc
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.12'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.12'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Predicts how molecules will fragment in a mass spectrometer. Currently
|
98
|
+
focused on lipid fragmentation under CID, HCD or PQD.
|
99
|
+
email:
|
100
|
+
- jtprince@gmail.com
|
101
|
+
executables:
|
102
|
+
- msplinter
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- .gitignore
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/msplinter
|
112
|
+
- lib/msplinter.rb
|
113
|
+
- lib/msplinter/commandline.rb
|
114
|
+
- lib/msplinter/version.rb
|
115
|
+
- lib/rubabel/molecule/fragmentable.rb
|
116
|
+
- msplinter.gemspec
|
117
|
+
- spec/rubabel/molecule/fragmentable_spec.rb
|
118
|
+
- spec/spec_helper.rb
|
119
|
+
homepage: https://github.com/princelab/msplinter
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.0.2
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Predicts how molecules will fragment in a mass spectrometer
|
143
|
+
test_files:
|
144
|
+
- spec/rubabel/molecule/fragmentable_spec.rb
|
145
|
+
- spec/spec_helper.rb
|
146
|
+
has_rdoc:
|