chemicalml 0.1.0

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.
Files changed (248) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +20 -0
  4. data/CHANGELOG.md +21 -0
  5. data/CLAUDE.md +198 -0
  6. data/Gemfile +14 -0
  7. data/LICENSE +24 -0
  8. data/NOTICES.adoc +115 -0
  9. data/README.adoc +87 -0
  10. data/Rakefile +8 -0
  11. data/TODO.align/01-cml-base-mixins.md +122 -0
  12. data/TODO.align/02-real-schema-hierarchies.md +88 -0
  13. data/TODO.align/03-configuration.md +50 -0
  14. data/TODO.align/04-parse-entry-points.md +59 -0
  15. data/TODO.align/05-schema-aware-translator.md +33 -0
  16. data/TODO.align/06-no-respond-to.md +44 -0
  17. data/TODO.align/07-canon-xml-comparison.md +43 -0
  18. data/TODO.align/08-expand-fixtures.md +37 -0
  19. data/TODO.align/09-final-spec-lint.md +29 -0
  20. data/TODO.align/10-role-marker-modules.md +54 -0
  21. data/TODO.align/11-dry-configuration.md +63 -0
  22. data/TODO.align/12-wire-class-macro.md +51 -0
  23. data/TODO.align/13-wire-namespace.md +44 -0
  24. data/TODO.align/14-schema24-correctness.md +37 -0
  25. data/TODO.align/15-canon-fixture-roundtrip.md +38 -0
  26. data/TODO.align/16-schema24-convention-coverage.md +33 -0
  27. data/TODO.align/17-final-spec-lint.md +28 -0
  28. data/TODO.align/README.md +27 -0
  29. data/TODO.align/README.round2.md +24 -0
  30. data/TODO.cml-full/01-reference-docs.md +43 -0
  31. data/TODO.cml-full/02-schema-versioned-model.md +70 -0
  32. data/TODO.cml-full/03-fixtures-scrape.md +52 -0
  33. data/TODO.cml-full/04-convention-framework.md +69 -0
  34. data/TODO.cml-full/05-dictionary-layer.md +77 -0
  35. data/TODO.cml-full/06-extend-canonical-translator.md +60 -0
  36. data/TODO.cml-full/07-attribution-notices.md +42 -0
  37. data/TODO.cml-full/08-autoload-only.md +48 -0
  38. data/TODO.cml-full/09-spec-coverage.md +38 -0
  39. data/TODO.cml-full/README.md +23 -0
  40. data/chemicalml.gemspec +35 -0
  41. data/data/dictionaries/_index.yaml +14 -0
  42. data/data/dictionaries/compchem.yaml +481 -0
  43. data/data/dictionaries/unit_non_si.yaml +53 -0
  44. data/data/dictionaries/unit_si.yaml +87 -0
  45. data/data/dictionaries/unit_type.yaml +113 -0
  46. data/lib/chemicalml/cml/array.rb +10 -0
  47. data/lib/chemicalml/cml/atom.rb +10 -0
  48. data/lib/chemicalml/cml/atom_array.rb +10 -0
  49. data/lib/chemicalml/cml/atom_parity.rb +10 -0
  50. data/lib/chemicalml/cml/base/array.rb +36 -0
  51. data/lib/chemicalml/cml/base/atom.rb +38 -0
  52. data/lib/chemicalml/cml/base/atom_array.rb +22 -0
  53. data/lib/chemicalml/cml/base/atom_parity.rb +24 -0
  54. data/lib/chemicalml/cml/base/bond.rb +28 -0
  55. data/lib/chemicalml/cml/base/bond_array.rb +22 -0
  56. data/lib/chemicalml/cml/base/bond_stereo.rb +28 -0
  57. data/lib/chemicalml/cml/base/cml_module.rb +40 -0
  58. data/lib/chemicalml/cml/base/dictionary.rb +32 -0
  59. data/lib/chemicalml/cml/base/dictionary_entry.rb +34 -0
  60. data/lib/chemicalml/cml/base/document.rb +26 -0
  61. data/lib/chemicalml/cml/base/formula.rb +38 -0
  62. data/lib/chemicalml/cml/base/identifier.rb +26 -0
  63. data/lib/chemicalml/cml/base/label.rb +28 -0
  64. data/lib/chemicalml/cml/base/list.rb +36 -0
  65. data/lib/chemicalml/cml/base/matrix.rb +38 -0
  66. data/lib/chemicalml/cml/base/metadata.rb +30 -0
  67. data/lib/chemicalml/cml/base/metadata_list.rb +28 -0
  68. data/lib/chemicalml/cml/base/molecule.rb +40 -0
  69. data/lib/chemicalml/cml/base/name.rb +26 -0
  70. data/lib/chemicalml/cml/base/parameter.rb +34 -0
  71. data/lib/chemicalml/cml/base/parameter_list.rb +28 -0
  72. data/lib/chemicalml/cml/base/product.rb +22 -0
  73. data/lib/chemicalml/cml/base/product_list.rb +22 -0
  74. data/lib/chemicalml/cml/base/property.rb +34 -0
  75. data/lib/chemicalml/cml/base/property_list.rb +28 -0
  76. data/lib/chemicalml/cml/base/reactant.rb +22 -0
  77. data/lib/chemicalml/cml/base/reactant_list.rb +22 -0
  78. data/lib/chemicalml/cml/base/reaction.rb +32 -0
  79. data/lib/chemicalml/cml/base/reaction_list.rb +22 -0
  80. data/lib/chemicalml/cml/base/scalar.rb +32 -0
  81. data/lib/chemicalml/cml/base/substance.rb +30 -0
  82. data/lib/chemicalml/cml/base/unit.rb +38 -0
  83. data/lib/chemicalml/cml/base/unit_list.rb +30 -0
  84. data/lib/chemicalml/cml/base/unit_type.rb +28 -0
  85. data/lib/chemicalml/cml/base/unit_type_list.rb +30 -0
  86. data/lib/chemicalml/cml/base.rb +51 -0
  87. data/lib/chemicalml/cml/bond.rb +10 -0
  88. data/lib/chemicalml/cml/bond_array.rb +10 -0
  89. data/lib/chemicalml/cml/bond_stereo.rb +10 -0
  90. data/lib/chemicalml/cml/cml_module.rb +10 -0
  91. data/lib/chemicalml/cml/dictionary.rb +10 -0
  92. data/lib/chemicalml/cml/dictionary_entry.rb +10 -0
  93. data/lib/chemicalml/cml/document.rb +10 -0
  94. data/lib/chemicalml/cml/elements.rb +58 -0
  95. data/lib/chemicalml/cml/formula.rb +10 -0
  96. data/lib/chemicalml/cml/identifier.rb +10 -0
  97. data/lib/chemicalml/cml/label.rb +10 -0
  98. data/lib/chemicalml/cml/list.rb +10 -0
  99. data/lib/chemicalml/cml/matrix.rb +10 -0
  100. data/lib/chemicalml/cml/metadata.rb +10 -0
  101. data/lib/chemicalml/cml/metadata_list.rb +10 -0
  102. data/lib/chemicalml/cml/molecule.rb +10 -0
  103. data/lib/chemicalml/cml/name.rb +10 -0
  104. data/lib/chemicalml/cml/namespace.rb +17 -0
  105. data/lib/chemicalml/cml/parameter.rb +10 -0
  106. data/lib/chemicalml/cml/parameter_list.rb +10 -0
  107. data/lib/chemicalml/cml/product.rb +10 -0
  108. data/lib/chemicalml/cml/product_list.rb +10 -0
  109. data/lib/chemicalml/cml/property.rb +10 -0
  110. data/lib/chemicalml/cml/property_list.rb +10 -0
  111. data/lib/chemicalml/cml/reactant.rb +10 -0
  112. data/lib/chemicalml/cml/reactant_list.rb +10 -0
  113. data/lib/chemicalml/cml/reaction.rb +10 -0
  114. data/lib/chemicalml/cml/reaction_list.rb +10 -0
  115. data/lib/chemicalml/cml/role/array.rb +13 -0
  116. data/lib/chemicalml/cml/role/atom.rb +13 -0
  117. data/lib/chemicalml/cml/role/atom_array.rb +13 -0
  118. data/lib/chemicalml/cml/role/atom_parity.rb +13 -0
  119. data/lib/chemicalml/cml/role/bond.rb +13 -0
  120. data/lib/chemicalml/cml/role/bond_array.rb +13 -0
  121. data/lib/chemicalml/cml/role/bond_stereo.rb +13 -0
  122. data/lib/chemicalml/cml/role/cml_module.rb +14 -0
  123. data/lib/chemicalml/cml/role/dictionary.rb +13 -0
  124. data/lib/chemicalml/cml/role/dictionary_entry.rb +13 -0
  125. data/lib/chemicalml/cml/role/document.rb +13 -0
  126. data/lib/chemicalml/cml/role/formula.rb +13 -0
  127. data/lib/chemicalml/cml/role/identifier.rb +13 -0
  128. data/lib/chemicalml/cml/role/label.rb +13 -0
  129. data/lib/chemicalml/cml/role/list.rb +13 -0
  130. data/lib/chemicalml/cml/role/matrix.rb +13 -0
  131. data/lib/chemicalml/cml/role/metadata.rb +13 -0
  132. data/lib/chemicalml/cml/role/metadata_list.rb +13 -0
  133. data/lib/chemicalml/cml/role/molecule.rb +13 -0
  134. data/lib/chemicalml/cml/role/name.rb +13 -0
  135. data/lib/chemicalml/cml/role/parameter.rb +13 -0
  136. data/lib/chemicalml/cml/role/parameter_list.rb +13 -0
  137. data/lib/chemicalml/cml/role/product.rb +13 -0
  138. data/lib/chemicalml/cml/role/product_list.rb +13 -0
  139. data/lib/chemicalml/cml/role/property.rb +13 -0
  140. data/lib/chemicalml/cml/role/property_list.rb +13 -0
  141. data/lib/chemicalml/cml/role/reactant.rb +13 -0
  142. data/lib/chemicalml/cml/role/reactant_list.rb +13 -0
  143. data/lib/chemicalml/cml/role/reaction.rb +13 -0
  144. data/lib/chemicalml/cml/role/reaction_list.rb +13 -0
  145. data/lib/chemicalml/cml/role/scalar.rb +13 -0
  146. data/lib/chemicalml/cml/role/substance.rb +13 -0
  147. data/lib/chemicalml/cml/role/unit.rb +13 -0
  148. data/lib/chemicalml/cml/role/unit_list.rb +13 -0
  149. data/lib/chemicalml/cml/role/unit_type.rb +13 -0
  150. data/lib/chemicalml/cml/role/unit_type_list.rb +13 -0
  151. data/lib/chemicalml/cml/role.rb +54 -0
  152. data/lib/chemicalml/cml/scalar.rb +10 -0
  153. data/lib/chemicalml/cml/schema24/configuration.rb +19 -0
  154. data/lib/chemicalml/cml/schema24.rb +40 -0
  155. data/lib/chemicalml/cml/schema3/configuration.rb +17 -0
  156. data/lib/chemicalml/cml/schema3.rb +40 -0
  157. data/lib/chemicalml/cml/substance.rb +10 -0
  158. data/lib/chemicalml/cml/translator/value_translations.rb +265 -0
  159. data/lib/chemicalml/cml/translator.rb +233 -0
  160. data/lib/chemicalml/cml/unit.rb +10 -0
  161. data/lib/chemicalml/cml/unit_list.rb +10 -0
  162. data/lib/chemicalml/cml/unit_type.rb +10 -0
  163. data/lib/chemicalml/cml/unit_type_list.rb +10 -0
  164. data/lib/chemicalml/cml/visitable.rb +65 -0
  165. data/lib/chemicalml/cml/wire_class_macro.rb +45 -0
  166. data/lib/chemicalml/cml.rb +55 -0
  167. data/lib/chemicalml/context_configuration.rb +127 -0
  168. data/lib/chemicalml/convention/base.rb +48 -0
  169. data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +33 -0
  170. data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +38 -0
  171. data/lib/chemicalml/convention/compchem/constraints.rb +14 -0
  172. data/lib/chemicalml/convention/compchem.rb +28 -0
  173. data/lib/chemicalml/convention/constraint.rb +79 -0
  174. data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +33 -0
  175. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +34 -0
  176. data/lib/chemicalml/convention/dictionary/constraints.rb +14 -0
  177. data/lib/chemicalml/convention/dictionary.rb +28 -0
  178. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +30 -0
  179. data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +42 -0
  180. data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +54 -0
  181. data/lib/chemicalml/convention/molecular/constraints.rb +19 -0
  182. data/lib/chemicalml/convention/molecular.rb +29 -0
  183. data/lib/chemicalml/convention/registry.rb +50 -0
  184. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +46 -0
  185. data/lib/chemicalml/convention/unit_dictionary/constraints.rb +12 -0
  186. data/lib/chemicalml/convention/unit_dictionary.rb +27 -0
  187. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +34 -0
  188. data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +12 -0
  189. data/lib/chemicalml/convention/unit_type_dictionary.rb +27 -0
  190. data/lib/chemicalml/convention/violation.rb +36 -0
  191. data/lib/chemicalml/convention.rb +39 -0
  192. data/lib/chemicalml/dictionary/entry.rb +59 -0
  193. data/lib/chemicalml/dictionary/enum.rb +51 -0
  194. data/lib/chemicalml/dictionary/link.rb +32 -0
  195. data/lib/chemicalml/dictionary/loader.rb +60 -0
  196. data/lib/chemicalml/dictionary/model.rb +64 -0
  197. data/lib/chemicalml/dictionary/registry.rb +75 -0
  198. data/lib/chemicalml/dictionary.rb +26 -0
  199. data/lib/chemicalml/errors.rb +7 -0
  200. data/lib/chemicalml/model/atom.rb +40 -0
  201. data/lib/chemicalml/model/bond.rb +33 -0
  202. data/lib/chemicalml/model/cml_array.rb +27 -0
  203. data/lib/chemicalml/model/cml_module.rb +45 -0
  204. data/lib/chemicalml/model/document.rb +29 -0
  205. data/lib/chemicalml/model/formula.rb +34 -0
  206. data/lib/chemicalml/model/identifier.rb +21 -0
  207. data/lib/chemicalml/model/label.rb +21 -0
  208. data/lib/chemicalml/model/matrix.rb +29 -0
  209. data/lib/chemicalml/model/metadata.rb +23 -0
  210. data/lib/chemicalml/model/metadata_list.rb +25 -0
  211. data/lib/chemicalml/model/molecule.rb +38 -0
  212. data/lib/chemicalml/model/name.rb +21 -0
  213. data/lib/chemicalml/model/node.rb +45 -0
  214. data/lib/chemicalml/model/parameter.rb +27 -0
  215. data/lib/chemicalml/model/parameter_list.rb +25 -0
  216. data/lib/chemicalml/model/product.rb +21 -0
  217. data/lib/chemicalml/model/product_list.rb +21 -0
  218. data/lib/chemicalml/model/property.rb +28 -0
  219. data/lib/chemicalml/model/property_list.rb +25 -0
  220. data/lib/chemicalml/model/reactant.rb +21 -0
  221. data/lib/chemicalml/model/reactant_list.rb +21 -0
  222. data/lib/chemicalml/model/reaction.rb +41 -0
  223. data/lib/chemicalml/model/reaction_list.rb +23 -0
  224. data/lib/chemicalml/model/scalar.rb +25 -0
  225. data/lib/chemicalml/model/substance.rb +25 -0
  226. data/lib/chemicalml/model.rb +38 -0
  227. data/lib/chemicalml/schema/definition.rb +41 -0
  228. data/lib/chemicalml/schema/registry.rb +30 -0
  229. data/lib/chemicalml/schema.rb +35 -0
  230. data/lib/chemicalml/version.rb +5 -0
  231. data/lib/chemicalml/versioned_parser.rb +43 -0
  232. data/lib/chemicalml.rb +47 -0
  233. data/lib/tasks/reference_fetch.rake +105 -0
  234. data/lib/tasks/scrape_fixtures.rake +133 -0
  235. data/reference-docs/conventions/compchem.md +141 -0
  236. data/reference-docs/conventions/dictionary.md +119 -0
  237. data/reference-docs/conventions/index.md +56 -0
  238. data/reference-docs/conventions/molecular.md +115 -0
  239. data/reference-docs/conventions/unit-dictionary.md +92 -0
  240. data/reference-docs/conventions/unitType-dictionary.md +51 -0
  241. data/reference-docs/dictionaries/compchem.md +335 -0
  242. data/reference-docs/dictionaries/index.md +52 -0
  243. data/reference-docs/examples/schema24-index.md +50 -0
  244. data/reference-docs/examples/schema3-compchem-index.md +14 -0
  245. data/reference-docs/examples/schema3-molecular-index.md +28 -0
  246. data/reference-docs/schemas/schema24/schema.xsd +10624 -0
  247. data/reference-docs/schemas/schema3/schema.xsd +11459 -0
  248. metadata +306 -0
@@ -0,0 +1,265 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cml
5
+ class Translator
6
+ # Canonical-form translations for the value-container and module
7
+ # elements introduced when the compchem / molecular conventions
8
+ # were added. Each method is symmetric with the existing
9
+ # molecule/atom/bond translators.
10
+ module ValueTranslations
11
+ module ClassMethods
12
+ def scalar_to_canonical(cml_scalar)
13
+ return nil unless cml_scalar
14
+
15
+ Model::Scalar.new(
16
+ value: cml_scalar.content,
17
+ data_type: cml_scalar.data_type,
18
+ units: cml_scalar.units,
19
+ id: cml_scalar.id,
20
+ title: cml_scalar.title,
21
+ dict_ref: cml_scalar.dict_ref
22
+ )
23
+ end
24
+
25
+ def scalar_from_canonical(scalar)
26
+ return nil unless scalar
27
+
28
+ Cml::Scalar.new(
29
+ content: scalar.value,
30
+ data_type: scalar.data_type,
31
+ units: scalar.units,
32
+ id: scalar.id,
33
+ title: scalar.title,
34
+ dict_ref: scalar.dict_ref
35
+ )
36
+ end
37
+
38
+ def array_to_canonical(cml_array)
39
+ return nil unless cml_array
40
+
41
+ Model::Array.new(
42
+ values: cml_array.content.to_s.split,
43
+ data_type: cml_array.data_type,
44
+ units: cml_array.units,
45
+ size: cml_array.size,
46
+ id: cml_array.id,
47
+ title: cml_array.title,
48
+ dict_ref: cml_array.dict_ref
49
+ )
50
+ end
51
+
52
+ def array_from_canonical(array)
53
+ return nil unless array
54
+
55
+ Cml::Array.new(
56
+ content: Array(array.values).join(" "),
57
+ data_type: array.data_type,
58
+ units: array.units,
59
+ size: array.size,
60
+ id: array.id,
61
+ title: array.title,
62
+ dict_ref: array.dict_ref
63
+ )
64
+ end
65
+
66
+ def matrix_to_canonical(cml_matrix)
67
+ return nil unless cml_matrix
68
+
69
+ Model::Matrix.new(
70
+ values: cml_matrix.content.to_s.split,
71
+ rows: cml_matrix.rows,
72
+ columns: cml_matrix.columns,
73
+ data_type: cml_matrix.data_type,
74
+ units: cml_matrix.units,
75
+ id: cml_matrix.id,
76
+ title: cml_matrix.title,
77
+ dict_ref: cml_matrix.dict_ref
78
+ )
79
+ end
80
+
81
+ def matrix_from_canonical(matrix)
82
+ return nil unless matrix
83
+
84
+ Cml::Matrix.new(
85
+ content: Array(matrix.values).join(" "),
86
+ rows: matrix.rows,
87
+ columns: matrix.columns,
88
+ data_type: matrix.data_type,
89
+ units: matrix.units,
90
+ id: matrix.id,
91
+ title: matrix.title,
92
+ dict_ref: matrix.dict_ref
93
+ )
94
+ end
95
+
96
+ def value_container_to_canonical(cml_value)
97
+ return nil unless cml_value
98
+
99
+ if cml_value.is_a?(Cml::Matrix)
100
+ matrix_to_canonical(cml_value)
101
+ elsif cml_value.is_a?(Cml::Array)
102
+ array_to_canonical(cml_value)
103
+ elsif cml_value.is_a?(Cml::Scalar)
104
+ scalar_to_canonical(cml_value)
105
+ end
106
+ end
107
+
108
+ def value_container_from_canonical(value)
109
+ case value
110
+ when Model::Matrix then matrix_from_canonical(value)
111
+ when Model::Array then array_from_canonical(value)
112
+ when Model::Scalar then scalar_from_canonical(value)
113
+ end
114
+ end
115
+
116
+ def property_to_canonical(cml_prop)
117
+ return nil unless cml_prop
118
+
119
+ Model::Property.new(
120
+ id: cml_prop.id,
121
+ title: cml_prop.title,
122
+ dict_ref: cml_prop.dict_ref,
123
+ convention: cml_prop.convention,
124
+ value: property_value_to_canonical(cml_prop)
125
+ )
126
+ end
127
+
128
+ def property_from_canonical(prop)
129
+ return nil unless prop
130
+
131
+ value = value_container_from_canonical(prop.value)
132
+ Cml::Property.new(
133
+ id: prop.id,
134
+ title: prop.title,
135
+ dict_ref: prop.dict_ref,
136
+ convention: prop.convention,
137
+ scalar: value.is_a?(Cml::Scalar) ? value : nil,
138
+ array: value.is_a?(Cml::Array) ? value : nil,
139
+ matrix: value.is_a?(Cml::Matrix) ? value : nil
140
+ )
141
+ end
142
+
143
+ def parameter_to_canonical(cml_param)
144
+ return nil unless cml_param
145
+
146
+ Model::Parameter.new(
147
+ id: cml_param.id,
148
+ title: cml_param.title,
149
+ dict_ref: cml_param.dict_ref,
150
+ convention: cml_param.convention,
151
+ value: parameter_value_to_canonical(cml_param)
152
+ )
153
+ end
154
+
155
+ def parameter_from_canonical(param)
156
+ return nil unless param
157
+
158
+ value = value_container_from_canonical(param.value)
159
+ Cml::Parameter.new(
160
+ id: param.id,
161
+ title: param.title,
162
+ dict_ref: param.dict_ref,
163
+ convention: param.convention,
164
+ scalar: value.is_a?(Cml::Scalar) ? value : nil,
165
+ array: value.is_a?(Cml::Array) ? value : nil,
166
+ matrix: value.is_a?(Cml::Matrix) ? value : nil
167
+ )
168
+ end
169
+
170
+ def label_to_canonical(cml_label)
171
+ return nil unless cml_label
172
+
173
+ Model::Label.new(
174
+ id: cml_label.id,
175
+ value: cml_label.value,
176
+ dict_ref: cml_label.dict_ref,
177
+ convention: cml_label.convention
178
+ )
179
+ end
180
+
181
+ def label_from_canonical(label)
182
+ return nil unless label
183
+
184
+ Cml::Label.new(
185
+ id: label.id,
186
+ value: label.value,
187
+ dict_ref: label.dict_ref,
188
+ convention: label.convention
189
+ )
190
+ end
191
+
192
+ def metadata_to_canonical(cml_md)
193
+ return nil unless cml_md
194
+
195
+ Model::Metadata.new(
196
+ id: cml_md.id,
197
+ name: cml_md.name,
198
+ content: cml_md.content,
199
+ convention: cml_md.convention,
200
+ title: cml_md.title
201
+ )
202
+ end
203
+
204
+ def metadata_from_canonical(md)
205
+ return nil unless md
206
+
207
+ Cml::Metadata.new(
208
+ id: md.id,
209
+ name: md.name,
210
+ content: md.content,
211
+ convention: md.convention,
212
+ title: md.title
213
+ )
214
+ end
215
+
216
+ def formula_to_canonical(cml_formula)
217
+ return nil unless cml_formula
218
+
219
+ Model::Formula.new(
220
+ id: cml_formula.id,
221
+ concise: cml_formula.concise,
222
+ inline: cml_formula.inline,
223
+ formal_charge: cml_formula.formal_charge,
224
+ count: cml_formula.count,
225
+ title: cml_formula.title,
226
+ convention: cml_formula.convention,
227
+ dict_ref: cml_formula.dict_ref
228
+ )
229
+ end
230
+
231
+ def formula_from_canonical(formula)
232
+ return nil unless formula
233
+
234
+ Cml::Formula.new(
235
+ id: formula.id,
236
+ concise: formula.concise,
237
+ inline: formula.inline,
238
+ formal_charge: formula.formal_charge,
239
+ count: formula.count,
240
+ title: formula.title,
241
+ convention: formula.convention,
242
+ dict_ref: formula.dict_ref
243
+ )
244
+ end
245
+
246
+ private
247
+
248
+ def property_value_to_canonical(cml_prop)
249
+ value_container_to_canonical(cml_prop.scalar) ||
250
+ value_container_to_canonical(cml_prop.array) ||
251
+ value_container_to_canonical(cml_prop.matrix)
252
+ end
253
+
254
+ def parameter_value_to_canonical(cml_param)
255
+ value_container_to_canonical(cml_param.scalar) ||
256
+ value_container_to_canonical(cml_param.array) ||
257
+ value_container_to_canonical(cml_param.matrix)
258
+ end
259
+ end
260
+ end
261
+
262
+ extend ValueTranslations::ClassMethods
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cml
5
+ # Adapter between the canonical `Chemicalml::Model` and the CML XML
6
+ # wire-format layer (`Chemicalml::Cml::*` lutaml-model classes).
7
+ #
8
+ # Pure transformation. No I/O. Two class methods, one per
9
+ # direction. Adding a new CML element means updating the
10
+ # translator's mapping rules; the canonical classes and the CML
11
+ # wire classes stay independent.
12
+ class Translator
13
+ autoload :ValueTranslations, "chemicalml/cml/translator/value_translations"
14
+
15
+ # CML wire format -> canonical. Accepts either Schema3 or
16
+ # Schema24 wire documents; dispatches based on class.
17
+ def self.to_canonical(document)
18
+ Model::Document.new(
19
+ molecules: document.molecules.map { |m| molecule_to_canonical(m) },
20
+ reactions: document.reactions.map { |r| reaction_to_canonical(r) }
21
+ )
22
+ end
23
+
24
+ # Canonical -> CML wire format. `schema:` selects the target
25
+ # version (`:schema3` default, or `:schema24`).
26
+ def self.from_canonical(document, schema: :schema3)
27
+ wire_class = document_class_for(schema)
28
+ wire_class.new(
29
+ molecules: document.molecules.map { |m| molecule_from_canonical(m) },
30
+ reactions: document.reactions.map { |r| reaction_from_canonical(r) }
31
+ )
32
+ end
33
+
34
+ def self.document_class_for(schema)
35
+ case schema.to_sym
36
+ when :schema3 then Chemicalml::Cml::Schema3::Document
37
+ when :schema24 then Chemicalml::Cml::Schema24::Document
38
+ else
39
+ raise ArgumentError, "unsupported schema: #{schema.inspect}"
40
+ end
41
+ end
42
+ private_class_method :document_class_for
43
+
44
+ # -- CML -> canonical --------------------------------------------
45
+
46
+ def self.molecule_to_canonical(cml_mol)
47
+ Model::Molecule.new(
48
+ id: cml_mol.id,
49
+ atoms: cml_mol.atom_array&.atoms&.map { |a| atom_to_canonical(a) } || [],
50
+ bonds: cml_mol.bond_array&.bonds&.map { |b| bond_to_canonical(b) } || [],
51
+ names: cml_mol.names.map { |n| Model::Name.new(content: n.content, convention: n.convention, dict_ref: n.dict_ref) },
52
+ identifiers: cml_mol.identifiers.map { |i| Model::Identifier.new(value: i.value, convention: i.convention, dict_ref: i.dict_ref) },
53
+ count: cml_mol.count,
54
+ formal_charge: cml_mol.formal_charge,
55
+ title: cml_mol.title
56
+ )
57
+ end
58
+
59
+ def self.atom_to_canonical(cml_atom)
60
+ Model::Atom.new(
61
+ id: cml_atom.id,
62
+ element: cml_atom.element_type,
63
+ formal_charge: cml_atom.formal_charge,
64
+ isotope: cml_atom.isotope,
65
+ count: cml_atom.count,
66
+ hydrogen_count: cml_atom.hydrogen_count,
67
+ spin_multiplicity: cml_atom.spin_multiplicity,
68
+ title: cml_atom.title
69
+ )
70
+ end
71
+
72
+ def self.bond_to_canonical(cml_bond)
73
+ Model::Bond.new(
74
+ id: cml_bond.id,
75
+ atom_refs: cml_bond.atom_refs2&.split(" ") || [],
76
+ kind: cml_order_to_kind(cml_bond.order)
77
+ )
78
+ end
79
+
80
+ def self.reaction_to_canonical(cml_rxn)
81
+ Model::Reaction.new(
82
+ id: cml_rxn.id,
83
+ reactant_list: reactant_list_to_canonical(cml_rxn.reactant_list),
84
+ product_list: product_list_to_canonical(cml_rxn.product_list),
85
+ arrow: type_to_arrow(cml_rxn.type || cml_rxn.title),
86
+ title: cml_rxn.title,
87
+ type: cml_rxn.type
88
+ )
89
+ end
90
+
91
+ def self.reactant_list_to_canonical(cml_list)
92
+ return Model::ReactantList.new unless cml_list
93
+
94
+ Model::ReactantList.new(
95
+ reactants: cml_list.reactants.map do |r|
96
+ Model::Reactant.new(
97
+ substance: Model::Substance.new(
98
+ molecule: molecule_to_canonical(r.substance.molecule),
99
+ title: r.substance.title,
100
+ role: r.substance.role
101
+ )
102
+ )
103
+ end
104
+ )
105
+ end
106
+
107
+ def self.product_list_to_canonical(cml_list)
108
+ return Model::ProductList.new unless cml_list
109
+
110
+ Model::ProductList.new(
111
+ products: cml_list.products.map do |p|
112
+ Model::Product.new(
113
+ substance: Model::Substance.new(
114
+ molecule: molecule_to_canonical(p.substance.molecule),
115
+ title: p.substance.title,
116
+ role: p.substance.role
117
+ )
118
+ )
119
+ end
120
+ )
121
+ end
122
+
123
+ # -- Canonical -> CML --------------------------------------------
124
+
125
+ def self.molecule_from_canonical(mol)
126
+ Cml::Molecule.new(
127
+ id: mol.id,
128
+ atom_array: mol.atoms.empty? ? nil : Cml::AtomArray.new(
129
+ atoms: mol.atoms.map { |a| atom_from_canonical(a) }
130
+ ),
131
+ bond_array: mol.bonds.empty? ? nil : Cml::BondArray.new(
132
+ bonds: mol.bonds.map { |b| bond_from_canonical(b) }
133
+ ),
134
+ names: mol.names.map { |n| Cml::Name.new(content: n.content, convention: n.convention, dict_ref: n.dict_ref) },
135
+ identifiers: mol.identifiers.map { |i| Cml::Identifier.new(value: i.value, convention: i.convention, dict_ref: i.dict_ref) },
136
+ count: mol.count,
137
+ formal_charge: mol.formal_charge,
138
+ title: mol.title
139
+ )
140
+ end
141
+
142
+ def self.atom_from_canonical(atom)
143
+ Cml::Atom.new(
144
+ id: atom.id,
145
+ element_type: atom.element,
146
+ formal_charge: atom.formal_charge,
147
+ isotope: atom.isotope,
148
+ count: atom.count,
149
+ hydrogen_count: atom.hydrogen_count,
150
+ spin_multiplicity: atom.spin_multiplicity,
151
+ title: atom.title
152
+ )
153
+ end
154
+
155
+ def self.bond_from_canonical(bond)
156
+ Cml::Bond.new(
157
+ id: bond.id,
158
+ atom_refs2: bond.atom_refs.join(" "),
159
+ order: bond.cml_order
160
+ )
161
+ end
162
+
163
+ def self.reaction_from_canonical(rxn)
164
+ Cml::Reaction.new(
165
+ id: rxn.id,
166
+ title: rxn.title || rxn.arrow.to_s,
167
+ type: rxn.type || rxn.arrow.to_s,
168
+ reactant_list: reactant_list_from_canonical(rxn.reactant_list),
169
+ product_list: product_list_from_canonical(rxn.product_list)
170
+ )
171
+ end
172
+
173
+ def self.reactant_list_from_canonical(list)
174
+ Cml::ReactantList.new(
175
+ reactants: list.reactants.map do |r|
176
+ Cml::Reactant.new(
177
+ substance: Cml::Substance.new(
178
+ molecule: molecule_from_canonical(r.substance.molecule),
179
+ title: r.substance.title,
180
+ role: r.substance.role
181
+ )
182
+ )
183
+ end
184
+ )
185
+ end
186
+
187
+ def self.product_list_from_canonical(list)
188
+ Cml::ProductList.new(
189
+ products: list.products.map do |p|
190
+ Cml::Product.new(
191
+ substance: Cml::Substance.new(
192
+ molecule: molecule_from_canonical(p.substance.molecule),
193
+ title: p.substance.title,
194
+ role: p.substance.role
195
+ )
196
+ )
197
+ end
198
+ )
199
+ end
200
+
201
+ # -- Lookup tables -----------------------------------------------
202
+
203
+ ORDER_TO_KIND = {
204
+ "S" => :single, "D" => :double, "T" => :triple,
205
+ "Q" => :quadruple, "A" => :aromatic, "W" => :wedge,
206
+ "H" => :hash, "DG" => :dative, "V" => :wavy
207
+ }.freeze
208
+
209
+ TYPE_TO_ARROW = {
210
+ "forward" => :forward,
211
+ "reverse" => :reverse,
212
+ "equilibrium" => :equilibrium,
213
+ "resonance" => :resonance
214
+ }.freeze
215
+
216
+ def self.cml_order_to_kind(order)
217
+ return :single if order.nil?
218
+
219
+ ORDER_TO_KIND.fetch(order.upcase, :single)
220
+ end
221
+
222
+ def self.type_to_arrow(value)
223
+ return :forward if value.nil?
224
+
225
+ TYPE_TO_ARROW.fetch(value.to_s.downcase, :forward)
226
+ end
227
+
228
+ private_class_method :cml_order_to_kind, :type_to_arrow
229
+
230
+ extend ValueTranslations::ClassMethods
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Backward-compatible alias. New code should reference the versioned
4
+ # class directly: Chemicalml::Cml::Schema3::Unit.
5
+ module Chemicalml
6
+ module Cml
7
+ remove_const(:Unit) if const_defined?(:Unit, false)
8
+ const_set(:Unit, Chemicalml::Cml::Schema3::Unit)
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Backward-compatible alias. New code should reference the versioned
4
+ # class directly: Chemicalml::Cml::Schema3::UnitList.
5
+ module Chemicalml
6
+ module Cml
7
+ remove_const(:UnitList) if const_defined?(:UnitList, false)
8
+ const_set(:UnitList, Chemicalml::Cml::Schema3::UnitList)
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Backward-compatible alias. New code should reference the versioned
4
+ # class directly: Chemicalml::Cml::Schema3::UnitType.
5
+ module Chemicalml
6
+ module Cml
7
+ remove_const(:UnitType) if const_defined?(:UnitType, false)
8
+ const_set(:UnitType, Chemicalml::Cml::Schema3::UnitType)
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Backward-compatible alias. New code should reference the versioned
4
+ # class directly: Chemicalml::Cml::Schema3::UnitTypeList.
5
+ module Chemicalml
6
+ module Cml
7
+ remove_const(:UnitTypeList) if const_defined?(:UnitTypeList, false)
8
+ const_set(:UnitTypeList, Chemicalml::Cml::Schema3::UnitTypeList)
9
+ end
10
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cml
5
+ # Marker mixin included by every wire class. Provides a uniform
6
+ # interface the convention-constraint walker relies on:
7
+ #
8
+ # - `wire_children` returns the child wire nodes
9
+ # - `node_id` returns the id attribute or `nil`
10
+ # - `element_name` returns the XML tag name
11
+ #
12
+ # All three use lutaml-model's attribute registry — no duck typing.
13
+ # A wire class that doesn't declare an `:id` attribute simply has
14
+ # `node_id` return `nil`.
15
+ module Visitable
16
+ def wire_children
17
+ declared_attribute_names.flat_map do |name|
18
+ collect_wire_nodes(public_send(name))
19
+ end
20
+ end
21
+
22
+ def node_id
23
+ return id if declared_attribute?(:id)
24
+
25
+ nil
26
+ end
27
+
28
+ def element_name
29
+ xml_mapping = self.class.mappings[:xml]
30
+ root = xml_mapping && xml_mapping.root
31
+ root && root.name || self.class.name.split("::").last
32
+ rescue StandardError
33
+ self.class.name.split("::").last
34
+ end
35
+
36
+ def collect_wire_nodes(value)
37
+ return [] if value.nil?
38
+
39
+ if value.is_a?(::Array)
40
+ value.select { |v| wire_node?(v) }
41
+ elsif wire_node?(value)
42
+ [value]
43
+ else
44
+ []
45
+ end
46
+ end
47
+
48
+ def wire_node?(value)
49
+ value.is_a?(Lutaml::Model::Serializable)
50
+ end
51
+
52
+ def declared_attribute_names
53
+ self.class.model.attributes.keys
54
+ rescue StandardError
55
+ []
56
+ end
57
+
58
+ def declared_attribute?(name)
59
+ self.class.model.attributes.key?(name)
60
+ rescue StandardError
61
+ false
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cml
5
+ # Macro for defining a schema version's wire classes without
6
+ # writing 35+ boilerplate files. Each generated class:
7
+ #
8
+ # - subclasses `Lutaml::Model::Serializable`
9
+ # - includes the corresponding `Base::*` mixin (shared attribute
10
+ # declarations and xml mapping)
11
+ # - includes `Cml::Visitable` (uniform interface for the
12
+ # constraint walker)
13
+ # - defines `self.lutaml_default_register` returning the
14
+ # provided context_id
15
+ #
16
+ # Usage:
17
+ #
18
+ # Chemicalml::Cml.define_wire_classes(
19
+ # namespace: Chemicalml::Cml::Schema3,
20
+ # context_id: :chemicalml_schema3
21
+ # )
22
+ #
23
+ # Pass `except:` to skip elements (e.g. Schema 2.4 lacks <module>):
24
+ #
25
+ # Chemicalml::Cml.define_wire_classes(
26
+ # namespace: Chemicalml::Cml::Schema24,
27
+ # context_id: :chemicalml_schema24,
28
+ # except: Chemicalml::Cml::Elements::SCHEMA3_ONLY
29
+ # )
30
+ def self.define_wire_classes(namespace:, context_id:, except: [])
31
+ skip = except.to_set
32
+ Elements::ALL.each_key do |class_name|
33
+ next if skip.include?(class_name)
34
+
35
+ base_module = Base.const_get(class_name)
36
+ klass = Class.new(Lutaml::Model::Serializable) do
37
+ include base_module
38
+ include Chemicalml::Cml::Visitable
39
+ end
40
+ klass.define_singleton_method(:lutaml_default_register) { context_id }
41
+ namespace.const_set(class_name, klass)
42
+ end
43
+ end
44
+ end
45
+ end