asciichem 0.3.0 → 0.3.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 +4 -4
- data/asciichem.gemspec +1 -1
- data/lib/asciichem/cml/extensions.rb +3 -3
- data/lib/asciichem/cml/translator.rb +37 -25
- data/lib/asciichem/model_adapter/from_canonical.rb +47 -25
- data/lib/asciichem/model_adapter/to_canonical.rb +35 -32
- data/lib/asciichem/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f025579767518b2551359a0817e2019c19bec299f0a7c77c16264dd98342df25
|
|
4
|
+
data.tar.gz: c092bfffa44620c0bb2ef20abdba849732eaaaa01ae2f0039bf8c22a4d57baad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63957c098bb2df1785ce770d6e375c1f54c771e847f01f7de5b03cfd88a4321cf9c6abd6d93c5a5b6f43b1239887f1da80ec48d46d62351071ab25632bfd766f
|
|
7
|
+
data.tar.gz: 82f5b7dda4a1f9461e5fe68dfc8566328ab2a4541101b33605582620be59651b61f358a7053e3361b5022587d3dc83be4ad1039615673076a492167025321fa0
|
data/asciichem.gemspec
CHANGED
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
31
31
|
spec.require_paths = ["lib"]
|
|
32
32
|
|
|
33
|
-
spec.add_dependency "chemicalml", "0.2.
|
|
33
|
+
spec.add_dependency "chemicalml", "~> 0.2.1"
|
|
34
34
|
spec.add_dependency "elkrb", "~> 1.0"
|
|
35
35
|
spec.add_dependency "nokogiri", "~> 1.16"
|
|
36
36
|
spec.add_dependency "parslet", "~> 2.0"
|
|
@@ -158,7 +158,7 @@ module AsciiChem
|
|
|
158
158
|
# then reaction reactants+products, then cascade reactions.
|
|
159
159
|
def self.flatten_canonical_atoms(canonical_doc)
|
|
160
160
|
atoms = []
|
|
161
|
-
canonical_doc.molecules.each { |m| atoms.concat(m.atoms) }
|
|
161
|
+
canonical_doc.molecules.each { |m| atoms.concat((m.atom_array&.atoms || [])) }
|
|
162
162
|
canonical_doc.reactions.each do |reaction|
|
|
163
163
|
atoms.concat(flatten_reaction_atoms(reaction))
|
|
164
164
|
end
|
|
@@ -172,8 +172,8 @@ module AsciiChem
|
|
|
172
172
|
atoms = []
|
|
173
173
|
reactants = reaction.reactant_list
|
|
174
174
|
products = reaction.product_list
|
|
175
|
-
reactants&.reactants&.each { |r| atoms.concat(r.substance.molecule.atoms) }
|
|
176
|
-
products&.products&.each { |p| atoms.concat(p.substance.molecule.atoms) }
|
|
175
|
+
reactants&.reactants&.each { |r| atoms.concat(r.substance.molecule.atom_array&.atoms || []) }
|
|
176
|
+
products&.products&.each { |p| atoms.concat(p.substance.molecule.atom_array&.atoms || []) }
|
|
177
177
|
atoms
|
|
178
178
|
end
|
|
179
179
|
private_class_method :flatten_reaction_atoms
|
|
@@ -4,62 +4,74 @@ require "chemicalml"
|
|
|
4
4
|
|
|
5
5
|
module AsciiChem
|
|
6
6
|
module Cml
|
|
7
|
-
# Thin
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
7
|
+
# Thin adapter between AsciiChem::Model and Chemicalml::Cml wire
|
|
8
|
+
# classes. chemicalml 0.2.1+ merged the canonical Model into the
|
|
9
|
+
# wire classes — Schema3::Atom, Schema3::Molecule, etc. handle both
|
|
10
|
+
# serialization and semantics. This class:
|
|
11
|
+
#
|
|
12
|
+
# 1. Builds a Chemicalml::Cml::Document from AsciiChem::Model::Formula
|
|
13
|
+
# 2. Serializes to XML (optionally enriching with aci: extensions)
|
|
14
|
+
# 3. Parses CML XML back into AsciiChem::Model::Formula
|
|
15
|
+
#
|
|
16
|
+
# The aci: extension namespace (oxidation state, Lewis markers, ring
|
|
17
|
+
# closures, groups, electron config, math, text, metadata) is handled
|
|
18
|
+
# by post-processing the XML via Extensions and GroupExtensions.
|
|
13
19
|
class Translator
|
|
14
20
|
class << self
|
|
15
|
-
# AsciiChem::Model::Formula -> CML XML string.
|
|
16
21
|
def from_asciichem(formula)
|
|
17
22
|
ensure_schema_registered!
|
|
18
23
|
translation = AsciiChem::ModelAdapter.to_canonical_with_mapping(formula)
|
|
19
|
-
|
|
20
|
-
xml = wire_doc.to_xml
|
|
24
|
+
xml = translation.document.to_xml
|
|
21
25
|
xml = inject_atom_extensions(xml, translation.atom_mapping)
|
|
22
|
-
|
|
23
|
-
inject_top_level_extensions(xml, formula)
|
|
26
|
+
inject_molecule_extensions(xml, formula, translation)
|
|
24
27
|
end
|
|
25
28
|
|
|
26
|
-
# CML XML string -> AsciiChem::Model::Formula.
|
|
27
29
|
def to_asciichem(xml)
|
|
28
30
|
ensure_schema_registered!
|
|
29
31
|
top_level = Extensions.extract_top_level(xml)
|
|
30
32
|
atom_extensions = Extensions.extract(xml)
|
|
31
33
|
group_extensions = GroupExtensions.extract(xml)
|
|
32
34
|
wire_doc = Chemicalml::Cml::Document.from_xml(xml)
|
|
33
|
-
|
|
34
|
-
formula
|
|
35
|
-
|
|
36
|
-
GroupExtensions.restore(formula, canonical, group_extensions)
|
|
35
|
+
formula = AsciiChem::ModelAdapter.from_canonical(wire_doc)
|
|
36
|
+
Extensions.restore(formula, wire_doc, atom_extensions)
|
|
37
|
+
GroupExtensions.restore(formula, wire_doc, group_extensions)
|
|
37
38
|
Extensions.restore_top_level(formula, top_level)
|
|
38
39
|
formula
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
private
|
|
42
43
|
|
|
44
|
+
def build_wire_document(document)
|
|
45
|
+
molecules = document.molecules.map { |m| m }
|
|
46
|
+
reactions = document.reactions.map { |r| r }
|
|
47
|
+
reaction_lists = document.reaction_lists.map { |l| l }
|
|
48
|
+
Chemicalml::Cml::Document.new(
|
|
49
|
+
molecules: molecules,
|
|
50
|
+
reactions: reactions,
|
|
51
|
+
reaction_lists: reaction_lists
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
43
55
|
def inject_atom_extensions(xml, atom_mapping)
|
|
44
56
|
extensions = Extensions.collect(atom_mapping)
|
|
45
57
|
Extensions.inject(xml, extensions)
|
|
46
58
|
end
|
|
47
59
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
def inject_molecule_extensions(xml, formula, translation)
|
|
61
|
+
xml = inject_groups(xml, formula, translation)
|
|
62
|
+
inject_top_level(xml, formula)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def inject_groups(xml, formula, translation)
|
|
66
|
+
groups = translation.respond_to?(:groups) ? translation.groups : {}
|
|
67
|
+
GroupExtensions.inject(xml, GroupExtensions.collect(groups))
|
|
51
68
|
end
|
|
52
69
|
|
|
53
|
-
def
|
|
70
|
+
def inject_top_level(xml, formula)
|
|
54
71
|
top_level = Extensions.collect_top_level(formula)
|
|
55
72
|
Extensions.inject_top_level(xml, top_level)
|
|
56
73
|
end
|
|
57
74
|
|
|
58
|
-
# chemicalml registers wire classes into a lutaml-model
|
|
59
|
-
# TypeRegistry on first use. The registry call is idempotent
|
|
60
|
-
# (guarded by `@models_registered`) but must run before any
|
|
61
|
-
# `from_xml` so element types like `molecule` and `atom` are
|
|
62
|
-
# resolvable.
|
|
63
75
|
def ensure_schema_registered!
|
|
64
76
|
Chemicalml::Cml::Schema3.ensure_registered!
|
|
65
77
|
end
|
|
@@ -10,18 +10,18 @@ module AsciiChem
|
|
|
10
10
|
#
|
|
11
11
|
# Mapping rules:
|
|
12
12
|
#
|
|
13
|
-
# - `Chemicalml::
|
|
14
|
-
# - `Chemicalml::
|
|
13
|
+
# - `Chemicalml::Cml::Document` -> `Formula`.
|
|
14
|
+
# - `Chemicalml::Cml::Molecule` -> `Molecule`. Atoms become
|
|
15
15
|
# AsciiChem atoms with
|
|
16
16
|
# subscript=count. Bonds
|
|
17
17
|
# re-insert between their
|
|
18
18
|
# endpoint positions.
|
|
19
|
-
# - `Chemicalml::
|
|
19
|
+
# - `Chemicalml::Cml::Atom` -> `Atom` (element, isotope,
|
|
20
20
|
# charge, lone pairs,
|
|
21
21
|
# radical electrons).
|
|
22
|
-
# - `Chemicalml::
|
|
23
|
-
# - `Chemicalml::
|
|
24
|
-
# - `Chemicalml::
|
|
22
|
+
# - `Chemicalml::Cml::Bond` -> `Bond` (kind enum).
|
|
23
|
+
# - `Chemicalml::Cml::Reaction` -> `Reaction`.
|
|
24
|
+
# - `Chemicalml::Cml::ReactionList` -> `ReactionCascade`.
|
|
25
25
|
#
|
|
26
26
|
# Round-trip note: the canonical model is richer than AsciiChem's
|
|
27
27
|
# (3D coordinates, metadata, etc.). Those fields are dropped on the
|
|
@@ -113,7 +113,7 @@ module AsciiChem
|
|
|
113
113
|
|
|
114
114
|
def extract_scalar_value(value)
|
|
115
115
|
return nil if value.nil?
|
|
116
|
-
return value.value if value.is_a?(Chemicalml::
|
|
116
|
+
return value.value if value.is_a?(Chemicalml::Cml::Scalar)
|
|
117
117
|
|
|
118
118
|
value.to_s
|
|
119
119
|
end
|
|
@@ -136,11 +136,21 @@ module AsciiChem
|
|
|
136
136
|
AsciiChem::Model::Reaction.new(
|
|
137
137
|
reactants: reactants_from_canonical(reaction.reactant_list),
|
|
138
138
|
products: products_from_canonical(reaction.product_list),
|
|
139
|
-
arrow: reaction
|
|
139
|
+
arrow: arrow_from_wire(reaction),
|
|
140
140
|
conditions: conditions_from_canonical(reaction)
|
|
141
141
|
)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
def arrow_from_wire(reaction)
|
|
145
|
+
type = reaction.type || reaction.title
|
|
146
|
+
case type.to_s
|
|
147
|
+
when 'reverse' then :reverse
|
|
148
|
+
when 'equilibrium' then :equilibrium
|
|
149
|
+
when 'resonance' then :resonance
|
|
150
|
+
else :forward
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
144
154
|
def reaction_list_from_canonical(list)
|
|
145
155
|
AsciiChem::Model::ReactionCascade.new(
|
|
146
156
|
steps: list.reactions.map { |r| reaction_from_canonical(r) }
|
|
@@ -159,12 +169,8 @@ module AsciiChem
|
|
|
159
169
|
list.products.map { |p| molecule_from_canonical(p.substance.molecule) }
|
|
160
170
|
end
|
|
161
171
|
|
|
162
|
-
def conditions_from_canonical(
|
|
163
|
-
|
|
164
|
-
below = reaction.conditions_below
|
|
165
|
-
return nil unless above || below
|
|
166
|
-
|
|
167
|
-
AsciiChem::Model::Reaction::Conditions.new(above: above, below: below)
|
|
172
|
+
def conditions_from_canonical(_reaction)
|
|
173
|
+
nil
|
|
168
174
|
end
|
|
169
175
|
|
|
170
176
|
# Rebuilds an AsciiChem::Model::Molecule's node list from a
|
|
@@ -180,7 +186,7 @@ module AsciiChem
|
|
|
180
186
|
def initialize(molecule)
|
|
181
187
|
@molecule = molecule
|
|
182
188
|
@position_by_atom_id = {}
|
|
183
|
-
molecule.
|
|
189
|
+
wire_atoms(molecule).each_with_index do |atom, idx|
|
|
184
190
|
@position_by_atom_id[atom.id] = idx
|
|
185
191
|
end
|
|
186
192
|
@nodes = build_nodes
|
|
@@ -189,20 +195,26 @@ module AsciiChem
|
|
|
189
195
|
private
|
|
190
196
|
|
|
191
197
|
def build_nodes
|
|
192
|
-
atoms = @molecule.
|
|
193
|
-
return atoms if @molecule.
|
|
198
|
+
atoms = wire_atoms(@molecule).map { |a| atom_from_canonical(a) }
|
|
199
|
+
return atoms if wire_bonds(@molecule).empty?
|
|
194
200
|
|
|
195
201
|
insert_bonds(atoms)
|
|
196
202
|
end
|
|
197
203
|
|
|
204
|
+
def wire_atoms(molecule)
|
|
205
|
+
molecule.atom_array&.atoms || []
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def wire_bonds(molecule)
|
|
209
|
+
molecule.bond_array&.bonds || []
|
|
210
|
+
end
|
|
211
|
+
|
|
198
212
|
def atom_from_canonical(atom)
|
|
199
213
|
AsciiChem::Model::Atom.new(
|
|
200
|
-
element: atom.
|
|
214
|
+
element: atom.element_type,
|
|
201
215
|
isotope: atom.isotope,
|
|
202
216
|
subscript: subscript_from_count(atom.count),
|
|
203
217
|
charge: atom.formal_charge,
|
|
204
|
-
lone_pairs: atom.lone_pairs,
|
|
205
|
-
radical_electrons: atom.radical_electrons,
|
|
206
218
|
**extract_coordinates(atom)
|
|
207
219
|
)
|
|
208
220
|
end
|
|
@@ -230,14 +242,14 @@ module AsciiChem
|
|
|
230
242
|
result = atoms.dup
|
|
231
243
|
# Insert in descending position order so earlier insertions
|
|
232
244
|
# don't shift the indices of pending ones.
|
|
233
|
-
bonds_with_pos = @molecule.
|
|
245
|
+
bonds_with_pos = wire_bonds(@molecule).map { |b| [b, later_position(b)] }
|
|
234
246
|
bonds_with_pos.sort_by { |(_, pos)| -pos }.each do |bond, pos|
|
|
235
247
|
# Ring bonds connect non-adjacent atoms. They're represented
|
|
236
248
|
# by the ring_closures digit carried via aci: extension on
|
|
237
249
|
# the atoms, not as a positional bond in the node list.
|
|
238
250
|
next if ring_bond?(bond)
|
|
239
251
|
|
|
240
|
-
result.insert(pos, AsciiChem::Model::Bond.new(kind: bond.
|
|
252
|
+
result.insert(pos, AsciiChem::Model::Bond.new(kind: bond_kind_from_order(bond.order)))
|
|
241
253
|
end
|
|
242
254
|
result
|
|
243
255
|
end
|
|
@@ -245,7 +257,7 @@ module AsciiChem
|
|
|
245
257
|
# Position of the later endpoint of the bond — i.e. where the
|
|
246
258
|
# bond marker should sit in the rebuilt linear sequence.
|
|
247
259
|
def later_position(bond)
|
|
248
|
-
positions = bond.
|
|
260
|
+
positions = bond.atom_refs2.to_s.split.map { |id| @position_by_atom_id[id] }.compact
|
|
249
261
|
return 0 if positions.empty?
|
|
250
262
|
|
|
251
263
|
positions.max
|
|
@@ -255,16 +267,26 @@ module AsciiChem
|
|
|
255
267
|
# adjacent — its endpoints span a gap. Such bonds are
|
|
256
268
|
# represented by ring_closures digits, not positional markers.
|
|
257
269
|
def ring_bond?(bond)
|
|
258
|
-
positions = bond.
|
|
270
|
+
positions = bond.atom_refs2.to_s.split.map { |id| @position_by_atom_id[id] }.compact
|
|
259
271
|
return false if positions.length < 2
|
|
260
272
|
|
|
261
273
|
positions.max - positions.min > 1
|
|
262
274
|
end
|
|
263
275
|
|
|
264
276
|
def first_position(bond)
|
|
265
|
-
positions = bond.
|
|
277
|
+
positions = bond.atom_refs2.to_s.split.map { |id| @position_by_atom_id[id] }.compact
|
|
266
278
|
positions.min || 0
|
|
267
279
|
end
|
|
280
|
+
|
|
281
|
+
ORDER_TO_KIND = {
|
|
282
|
+
'S' => :single, 'D' => :double, 'T' => :triple,
|
|
283
|
+
'Q' => :quadruple, 'W' => :wedge, 'H' => :hash,
|
|
284
|
+
'A' => :dative, 'V' => :wavy
|
|
285
|
+
}.freeze
|
|
286
|
+
|
|
287
|
+
def bond_kind_from_order(order)
|
|
288
|
+
ORDER_TO_KIND.fetch(order.to_s, :single)
|
|
289
|
+
end
|
|
268
290
|
end
|
|
269
291
|
private_constant :MoleculeRebuilder
|
|
270
292
|
end
|
|
@@ -9,17 +9,17 @@ module AsciiChem
|
|
|
9
9
|
#
|
|
10
10
|
# Mapping rules:
|
|
11
11
|
#
|
|
12
|
-
# - `Formula` -> `Chemicalml::
|
|
13
|
-
# - `Molecule` -> `Chemicalml::
|
|
12
|
+
# - `Formula` -> `Chemicalml::Cml::Document`.
|
|
13
|
+
# - `Molecule` -> `Chemicalml::Cml::Molecule`. Inner atoms
|
|
14
14
|
# collect IDs; bonds reference consecutive
|
|
15
15
|
# IDs. Groups flatten with their multiplicity
|
|
16
16
|
# applied to each contained atom's count.
|
|
17
|
-
# - `Atom` -> `Chemicalml::
|
|
17
|
+
# - `Atom` -> `Chemicalml::Cml::Atom` (element,
|
|
18
18
|
# isotope, charge, count, lone pairs,
|
|
19
19
|
# radical electrons).
|
|
20
|
-
# - `Bond` -> `Chemicalml::
|
|
21
|
-
# - `Reaction` -> `Chemicalml::
|
|
22
|
-
# - `ReactionCascade` -> `Chemicalml::
|
|
20
|
+
# - `Bond` -> `Chemicalml::Cml::Bond` (kind, refs).
|
|
21
|
+
# - `Reaction` -> `Chemicalml::Cml::Reaction`.
|
|
22
|
+
# - `ReactionCascade` -> `Chemicalml::Cml::ReactionList`.
|
|
23
23
|
# - `ElectronConfiguration`, `EmbeddedMath`, `Text` -> skipped
|
|
24
24
|
# (no canonical representation yet; a future extension can carry
|
|
25
25
|
# them as namespaced metadata).
|
|
@@ -52,7 +52,7 @@ module AsciiChem
|
|
|
52
52
|
reaction_lists << reaction_cascade_to_canonical(node)
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
|
-
Chemicalml::
|
|
55
|
+
Chemicalml::Cml::Document.new(
|
|
56
56
|
molecules: molecules,
|
|
57
57
|
reactions: reactions,
|
|
58
58
|
reaction_lists: reaction_lists
|
|
@@ -67,10 +67,10 @@ module AsciiChem
|
|
|
67
67
|
walker = MoleculeWalker.new(@ids)
|
|
68
68
|
atoms, bonds = walker.walk(molecule)
|
|
69
69
|
@atom_mapping.merge!(walker.atom_mapping)
|
|
70
|
-
canonical_molecule = Chemicalml::
|
|
70
|
+
canonical_molecule = Chemicalml::Cml::Molecule.new(
|
|
71
71
|
id: @ids.next(:molecule),
|
|
72
|
-
atoms: atoms,
|
|
73
|
-
bonds: bonds,
|
|
72
|
+
atom_array: atoms.empty? ? nil : Chemicalml::Cml::AtomArray.new(atoms: atoms),
|
|
73
|
+
bond_array: bonds.empty? ? nil : Chemicalml::Cml::BondArray.new(bonds: bonds),
|
|
74
74
|
count: molecule.coefficient,
|
|
75
75
|
formal_charge: total_formal_charge(atoms),
|
|
76
76
|
names: map_names(molecule.names),
|
|
@@ -88,7 +88,7 @@ module AsciiChem
|
|
|
88
88
|
return [] if names.nil? || names.empty?
|
|
89
89
|
|
|
90
90
|
names.map do |n|
|
|
91
|
-
Chemicalml::
|
|
91
|
+
Chemicalml::Cml::Name.new(
|
|
92
92
|
content: n.content,
|
|
93
93
|
convention: n.convention,
|
|
94
94
|
dict_ref: n.dict_ref
|
|
@@ -100,7 +100,7 @@ module AsciiChem
|
|
|
100
100
|
return [] if identifiers.nil? || identifiers.empty?
|
|
101
101
|
|
|
102
102
|
identifiers.map do |i|
|
|
103
|
-
Chemicalml::
|
|
103
|
+
Chemicalml::Cml::Identifier.new(
|
|
104
104
|
value: i.value,
|
|
105
105
|
convention: i.convention,
|
|
106
106
|
dict_ref: i.dict_ref
|
|
@@ -112,7 +112,7 @@ module AsciiChem
|
|
|
112
112
|
return [] if formulas.nil? || formulas.empty?
|
|
113
113
|
|
|
114
114
|
formulas.map do |f|
|
|
115
|
-
Chemicalml::
|
|
115
|
+
Chemicalml::Cml::Formula.new(
|
|
116
116
|
concise: f[:concise],
|
|
117
117
|
inline: f[:inline],
|
|
118
118
|
formal_charge: f[:formal_charge],
|
|
@@ -128,7 +128,7 @@ module AsciiChem
|
|
|
128
128
|
return [] if properties.nil? || properties.empty?
|
|
129
129
|
|
|
130
130
|
properties.map do |p|
|
|
131
|
-
Chemicalml::
|
|
131
|
+
Chemicalml::Cml::Property.new(
|
|
132
132
|
title: p[:title],
|
|
133
133
|
value: build_scalar(p[:value]),
|
|
134
134
|
dict_ref: p[:dict_ref],
|
|
@@ -140,7 +140,7 @@ module AsciiChem
|
|
|
140
140
|
def build_scalar(value)
|
|
141
141
|
return nil if value.nil?
|
|
142
142
|
|
|
143
|
-
Chemicalml::
|
|
143
|
+
Chemicalml::Cml::Scalar.new(
|
|
144
144
|
value: value.to_s,
|
|
145
145
|
dict_ref: nil
|
|
146
146
|
)
|
|
@@ -150,7 +150,7 @@ module AsciiChem
|
|
|
150
150
|
return [] if labels.nil? || labels.empty?
|
|
151
151
|
|
|
152
152
|
labels.map do |l|
|
|
153
|
-
Chemicalml::
|
|
153
|
+
Chemicalml::Cml::Label.new(
|
|
154
154
|
value: l[:value],
|
|
155
155
|
dict_ref: l[:dict_ref],
|
|
156
156
|
convention: l[:convention]
|
|
@@ -180,7 +180,7 @@ module AsciiChem
|
|
|
180
180
|
# -- Reactions --------------------------------------------------
|
|
181
181
|
|
|
182
182
|
def reaction_to_canonical(reaction)
|
|
183
|
-
Chemicalml::
|
|
183
|
+
Chemicalml::Cml::Reaction.new(
|
|
184
184
|
id: @ids.next(:reaction),
|
|
185
185
|
reactant_list: reactant_list_to_canonical(reaction.reactants),
|
|
186
186
|
product_list: product_list_to_canonical(reaction.products),
|
|
@@ -193,26 +193,26 @@ module AsciiChem
|
|
|
193
193
|
end
|
|
194
194
|
|
|
195
195
|
def reaction_cascade_to_canonical(cascade)
|
|
196
|
-
Chemicalml::
|
|
196
|
+
Chemicalml::Cml::ReactionList.new(
|
|
197
197
|
reactions: cascade.steps.map { |s| reaction_to_canonical(s) }
|
|
198
198
|
)
|
|
199
199
|
end
|
|
200
200
|
|
|
201
201
|
def reactant_list_to_canonical(reactants)
|
|
202
|
-
Chemicalml::
|
|
202
|
+
Chemicalml::Cml::ReactantList.new(
|
|
203
203
|
reactants: reactants.map { |m| participant_to_canonical(m, :reactant) }
|
|
204
204
|
)
|
|
205
205
|
end
|
|
206
206
|
|
|
207
207
|
def product_list_to_canonical(products)
|
|
208
|
-
Chemicalml::
|
|
208
|
+
Chemicalml::Cml::ProductList.new(
|
|
209
209
|
products: products.map { |m| participant_to_canonical(m, :product) }
|
|
210
210
|
)
|
|
211
211
|
end
|
|
212
212
|
|
|
213
213
|
def participant_to_canonical(molecule, role)
|
|
214
|
-
Chemicalml::
|
|
215
|
-
substance: Chemicalml::
|
|
214
|
+
Chemicalml::Cml::Reactant.new(
|
|
215
|
+
substance: Chemicalml::Cml::Substance.new(
|
|
216
216
|
molecule: molecule_to_canonical(molecule),
|
|
217
217
|
role: role
|
|
218
218
|
)
|
|
@@ -264,7 +264,7 @@ module AsciiChem
|
|
|
264
264
|
# into canonical atom IDs. Skips pairs that already have a
|
|
265
265
|
# positional bond between them (degenerate case like `C1-C1`).
|
|
266
266
|
def emit_ring_bonds(molecule)
|
|
267
|
-
existing_pair_keys = @bonds.map { |b| pair_key(b.
|
|
267
|
+
existing_pair_keys = @bonds.map { |b| pair_key(b.atom_refs2.to_s.split) }.to_set
|
|
268
268
|
AsciiChem::RingBonds.each_in(molecule) do |ring_bond|
|
|
269
269
|
from_id = @atom_id_by_object_id[ring_bond.from_atom.object_id]
|
|
270
270
|
to_id = @atom_id_by_object_id[ring_bond.to_atom.object_id]
|
|
@@ -273,10 +273,10 @@ module AsciiChem
|
|
|
273
273
|
key = pair_key([from_id, to_id])
|
|
274
274
|
next if existing_pair_keys.include?(key)
|
|
275
275
|
|
|
276
|
-
@bonds << Chemicalml::
|
|
276
|
+
@bonds << Chemicalml::Cml::Bond.new(
|
|
277
277
|
id: @ids.next(:bond),
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
atom_refs2: "#{from_id} #{to_id}",
|
|
279
|
+
order: "S"
|
|
280
280
|
)
|
|
281
281
|
existing_pair_keys << key
|
|
282
282
|
end
|
|
@@ -325,7 +325,7 @@ module AsciiChem
|
|
|
325
325
|
@group_stack.each { |record| record.atom_ids << id }
|
|
326
326
|
attrs = {
|
|
327
327
|
id: id,
|
|
328
|
-
|
|
328
|
+
element_type: atom.element,
|
|
329
329
|
isotope: atom.isotope,
|
|
330
330
|
formal_charge: atom.charge,
|
|
331
331
|
count: effective_count(atom, multiplier),
|
|
@@ -333,17 +333,17 @@ module AsciiChem
|
|
|
333
333
|
radical_electrons: atom.radical_electrons
|
|
334
334
|
}
|
|
335
335
|
merge_coordinates(attrs, atom)
|
|
336
|
-
@atoms << Chemicalml::
|
|
336
|
+
@atoms << Chemicalml::Cml::Atom.new(**attrs)
|
|
337
337
|
emit_pending_bond(id) if @pending_bond_kind && @last_atom_id
|
|
338
338
|
@last_atom_id = id
|
|
339
339
|
@pending_bond_kind = nil
|
|
340
340
|
end
|
|
341
341
|
|
|
342
342
|
def emit_pending_bond(next_atom_id)
|
|
343
|
-
@bonds << Chemicalml::
|
|
343
|
+
@bonds << Chemicalml::Cml::Bond.new(
|
|
344
344
|
id: @ids.next(:bond),
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
atom_refs2: "#{@last_atom_id} #{next_atom_id}",
|
|
346
|
+
order: BOND_ORDER_CODES.fetch(@pending_bond_kind, "S"),
|
|
347
347
|
bond_stereo: bond_stereo_for(@pending_bond_kind)
|
|
348
348
|
)
|
|
349
349
|
end
|
|
@@ -351,13 +351,16 @@ module AsciiChem
|
|
|
351
351
|
# CML bondStereo codes for stereo bonds. Wedge (coming out of
|
|
352
352
|
# page) → "W"; hash (going into page) → "H". Other bond kinds
|
|
353
353
|
# have no stereo meaning.
|
|
354
|
+
BOND_ORDER_CODES = { single: "S", double: "D", triple: "T",
|
|
355
|
+
quadruple: "Q", wedge: "W", hash: "H",
|
|
356
|
+
dative: "A", wavy: "V" }.freeze
|
|
354
357
|
BOND_STEREO_CODES = { wedge: "W", hash: "H" }.freeze
|
|
355
358
|
|
|
356
359
|
def bond_stereo_for(kind)
|
|
357
360
|
code = BOND_STEREO_CODES[kind]
|
|
358
361
|
return nil unless code
|
|
359
362
|
|
|
360
|
-
Chemicalml::
|
|
363
|
+
Chemicalml::Cml::BondStereo.new(value: code)
|
|
361
364
|
end
|
|
362
365
|
|
|
363
366
|
def effective_count(atom, multiplier)
|
data/lib/asciichem/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciichem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -13,16 +13,16 @@ dependencies:
|
|
|
13
13
|
name: chemicalml
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- -
|
|
16
|
+
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.2.
|
|
18
|
+
version: 0.2.1
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- -
|
|
23
|
+
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.2.
|
|
25
|
+
version: 0.2.1
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: elkrb
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|