chemicalml 0.2.0 → 0.3.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 (450) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +7 -1
  3. data/.rubocop.yml +23 -0
  4. data/CLAUDE.md +95 -40
  5. data/README.adoc +154 -26
  6. data/TODO.align/36-molecular-convention-coverage.md +46 -0
  7. data/TODO.align/37-compchem-convention-coverage.md +55 -0
  8. data/TODO.align/38-dictionary-convention-coverage.md +34 -0
  9. data/TODO.align/39-unit-dictionary-convention-coverage.md +33 -0
  10. data/TODO.align/40-unit-type-dictionary-convention-coverage.md +28 -0
  11. data/TODO.align/41-expand-canonical-model-layer.md +43 -0
  12. data/TODO.align/42-translator-wire-new-models.md +31 -0
  13. data/TODO.align/43-dictionary-model-no-to-h.md +39 -0
  14. data/TODO.align/44-schema3-only-and-schema24-module.md +44 -0
  15. data/TODO.align/45-documentation-and-final-verification.md +23 -0
  16. data/TODO.align/46-lattice-vector-content.md +24 -0
  17. data/TODO.align/47-molecule-crystal-spectra-property-lists.md +23 -0
  18. data/TODO.align/48-model-list-and-module-lists.md +21 -0
  19. data/TODO.align/49-more-chemistry-models.md +34 -0
  20. data/TODO.align/50-validation-report.md +29 -0
  21. data/TODO.align/51-convention-auto-detection.md +30 -0
  22. data/TODO.align/52-schema24-legacy-elements.md +28 -0
  23. data/TODO.align/53-round-trip-integration-specs.md +30 -0
  24. data/TODO.align/54-document-versioned-parser-shim.md +38 -0
  25. data/TODO.align/55-molecule-missing-attributes.md +20 -0
  26. data/TODO.align/56-geometry-models.md +24 -0
  27. data/TODO.align/57-compchem-models.md +26 -0
  28. data/TODO.align/58-remaining-models.md +24 -0
  29. data/TODO.align/59-wire-new-models-translator.md +18 -0
  30. data/TODO.align/60-docs-and-final-verification.md +19 -0
  31. data/TODO.align/61-parallel-array-attrs.md +54 -0
  32. data/TODO.align/62-per-element-attribute-gaps.md +49 -0
  33. data/TODO.align/63-universal-children-mixin.md +42 -0
  34. data/TODO.align/64-per-element-child-gaps.md +81 -0
  35. data/TODO.align/65-simple-unit-convention.md +40 -0
  36. data/TODO.align/66-schema24-legacy-children.md +32 -0
  37. data/TODO.align/67-final-verification.md +32 -0
  38. data/TODO.align/68-detection-coverage.md +33 -0
  39. data/TODO.align/69-json-yaml-roundtrip.md +27 -0
  40. data/TODO.align/70-iterative-walker.md +40 -0
  41. data/TODO.align/71-violation-value.md +25 -0
  42. data/TODO.align/72-close-child-gaps.md +66 -0
  43. data/TODO.align/73-schema24-parse-limitation.md +64 -0
  44. data/TODO.align/74-final-verification-round18.md +15 -0
  45. data/TODO.align/75-key-value-mappings.md +43 -0
  46. data/TODO.align/76-schema24-workaround.md +57 -0
  47. data/TODO.align/77-convention-coverage-matrix.md +31 -0
  48. data/TODO.align/78-fixture-audit.md +23 -0
  49. data/TODO.align/79-final-verification-round19.md +15 -0
  50. data/TODO.align/80-xsd-enum-constants.md +35 -0
  51. data/TODO.align/81-enum-validation-constraints.md +29 -0
  52. data/TODO.align/82-reference-resolver.md +33 -0
  53. data/TODO.align/83-toml-roundtrip.md +20 -0
  54. data/TODO.align/84-final-verification-round21.md +16 -0
  55. data/TODO.align/85-bond-distinct-atoms.md +25 -0
  56. data/TODO.align/86-references-should-resolve.md +20 -0
  57. data/TODO.align/87-cli-utility.md +26 -0
  58. data/TODO.align/88-diverse-fixtures.md +26 -0
  59. data/TODO.align/89-final-verification-round22.md +15 -0
  60. data/TODO.align/90-bond-stereo-completeness.md +31 -0
  61. data/TODO.align/91-atomparity-distinct.md +21 -0
  62. data/TODO.align/92-unit-dictionary-completeness.md +28 -0
  63. data/TODO.align/93-yard-docs.md +23 -0
  64. data/TODO.align/94-final-verification-round23.md +15 -0
  65. data/TODO.align/README.round17.md +28 -0
  66. data/TODO.align/README.round18.md +37 -0
  67. data/TODO.align/README.round19.md +40 -0
  68. data/TODO.align/README.round20.md +49 -0
  69. data/TODO.align/README.round21.md +35 -0
  70. data/TODO.align/README.round22.md +48 -0
  71. data/TODO.align/README.round23.md +46 -0
  72. data/TODO.align/README.round24.md +53 -0
  73. data/TODO.align/README.round25.md +66 -0
  74. data/TODO.align/README.round26.md +55 -0
  75. data/TODO.align/README.round27.md +51 -0
  76. data/TODO.align/README.round28.md +61 -0
  77. data/TODO.align/README.round29.md +64 -0
  78. data/TODO.align/README.round30.md +50 -0
  79. data/TODO.align/README.round31.md +54 -0
  80. data/TODO.align/README.round32.md +67 -0
  81. data/TODO.align/README.round33.md +62 -0
  82. data/TODO.align/README.round34.md +70 -0
  83. data/docs/constraints.md +161 -0
  84. data/docs/generate_constraint_docs.rb +44 -0
  85. data/exe/chemicalml +6 -0
  86. data/lib/chemicalml/cli/base.rb +68 -0
  87. data/lib/chemicalml/cli/command.rb +43 -0
  88. data/lib/chemicalml/cli/constraints_command.rb +27 -0
  89. data/lib/chemicalml/cli/conventions_command.rb +14 -0
  90. data/lib/chemicalml/cli/dictionaries_command.rb +14 -0
  91. data/lib/chemicalml/cli/elements_command.rb +17 -0
  92. data/lib/chemicalml/cli/enums_command.rb +17 -0
  93. data/lib/chemicalml/cli/info_command.rb +77 -0
  94. data/lib/chemicalml/cli/inspect_command.rb +39 -0
  95. data/lib/chemicalml/cli/validate_command.rb +54 -0
  96. data/lib/chemicalml/cli.rb +43 -0
  97. data/lib/chemicalml/cml/base/abundance.rb +21 -5
  98. data/lib/chemicalml/cml/base/action.rb +44 -6
  99. data/lib/chemicalml/cml/base/action_list.rb +44 -8
  100. data/lib/chemicalml/cml/base/alternative.rb +37 -0
  101. data/lib/chemicalml/cml/base/amount.rb +15 -5
  102. data/lib/chemicalml/cml/base/angle.rb +34 -7
  103. data/lib/chemicalml/cml/base/annotation.rb +38 -0
  104. data/lib/chemicalml/cml/base/any_cml.rb +12 -5
  105. data/lib/chemicalml/cml/base/appinfo.rb +40 -0
  106. data/lib/chemicalml/cml/base/arg.rb +80 -0
  107. data/lib/chemicalml/cml/base/array.rb +51 -8
  108. data/lib/chemicalml/cml/base/array_list.rb +15 -5
  109. data/lib/chemicalml/cml/base/atom.rb +93 -19
  110. data/lib/chemicalml/cml/base/atom_array.rb +77 -2
  111. data/lib/chemicalml/cml/base/atom_parity.rb +18 -2
  112. data/lib/chemicalml/cml/base/atom_set.rb +15 -5
  113. data/lib/chemicalml/cml/base/atom_type.rb +27 -7
  114. data/lib/chemicalml/cml/base/atom_type_list.rb +23 -7
  115. data/lib/chemicalml/cml/base/atomic_basis_function.rb +35 -11
  116. data/lib/chemicalml/cml/base/band.rb +22 -7
  117. data/lib/chemicalml/cml/base/band_list.rb +15 -7
  118. data/lib/chemicalml/cml/base/basis_set.rb +26 -7
  119. data/lib/chemicalml/cml/base/bond.rb +39 -7
  120. data/lib/chemicalml/cml/base/bond_array.rb +41 -2
  121. data/lib/chemicalml/cml/base/bond_set.rb +15 -5
  122. data/lib/chemicalml/cml/base/bond_stereo.rb +25 -4
  123. data/lib/chemicalml/cml/base/bond_type.rb +24 -7
  124. data/lib/chemicalml/cml/base/bond_type_list.rb +23 -7
  125. data/lib/chemicalml/cml/base/cell_parameter.rb +30 -8
  126. data/lib/chemicalml/cml/base/cml_module.rb +36 -11
  127. data/lib/chemicalml/cml/base/common_children.rb +35 -0
  128. data/lib/chemicalml/cml/base/complex_object.rb +37 -0
  129. data/lib/chemicalml/cml/base/condition_list.rb +28 -8
  130. data/lib/chemicalml/cml/base/crystal.rb +27 -9
  131. data/lib/chemicalml/cml/base/definition.rb +12 -5
  132. data/lib/chemicalml/cml/base/description.rb +15 -5
  133. data/lib/chemicalml/cml/base/dictionary.rb +30 -7
  134. data/lib/chemicalml/cml/base/dictionary_entry.rb +67 -8
  135. data/lib/chemicalml/cml/base/dimension.rb +31 -7
  136. data/lib/chemicalml/cml/base/document.rb +18 -8
  137. data/lib/chemicalml/cml/base/documentation.rb +12 -5
  138. data/lib/chemicalml/cml/base/eigen.rb +33 -7
  139. data/lib/chemicalml/cml/base/electron.rb +29 -9
  140. data/lib/chemicalml/cml/base/enumeration.rb +41 -0
  141. data/lib/chemicalml/cml/base/expression.rb +47 -0
  142. data/lib/chemicalml/cml/base/float.rb +52 -0
  143. data/lib/chemicalml/cml/base/float_array.rb +55 -0
  144. data/lib/chemicalml/cml/base/formula.rb +31 -10
  145. data/lib/chemicalml/cml/base/fragment.rb +33 -9
  146. data/lib/chemicalml/cml/base/fragment_list.rb +26 -7
  147. data/lib/chemicalml/cml/base/gradient.rb +19 -6
  148. data/lib/chemicalml/cml/base/identifier.rb +22 -4
  149. data/lib/chemicalml/cml/base/integer.rb +52 -0
  150. data/lib/chemicalml/cml/base/integer_array.rb +55 -0
  151. data/lib/chemicalml/cml/base/isotope.rb +29 -8
  152. data/lib/chemicalml/cml/base/isotope_list.rb +18 -7
  153. data/lib/chemicalml/cml/base/join.rb +28 -6
  154. data/lib/chemicalml/cml/base/kpoint.rb +17 -6
  155. data/lib/chemicalml/cml/base/kpoint_list.rb +15 -7
  156. data/lib/chemicalml/cml/base/label.rb +15 -5
  157. data/lib/chemicalml/cml/base/lattice.rb +36 -9
  158. data/lib/chemicalml/cml/base/lattice_vector.rb +17 -6
  159. data/lib/chemicalml/cml/base/length.rb +31 -7
  160. data/lib/chemicalml/cml/base/line3.rb +21 -5
  161. data/lib/chemicalml/cml/base/link.rb +51 -10
  162. data/lib/chemicalml/cml/base/list.rb +23 -9
  163. data/lib/chemicalml/cml/base/map.rb +38 -5
  164. data/lib/chemicalml/cml/base/matrix.rb +38 -9
  165. data/lib/chemicalml/cml/base/mechanism.rb +20 -7
  166. data/lib/chemicalml/cml/base/mechanism_component.rb +14 -6
  167. data/lib/chemicalml/cml/base/metadata.rb +20 -6
  168. data/lib/chemicalml/cml/base/metadata_list.rb +30 -5
  169. data/lib/chemicalml/cml/base/molecule.rb +95 -21
  170. data/lib/chemicalml/cml/base/molecule_list.rb +27 -7
  171. data/lib/chemicalml/cml/base/name.rb +11 -3
  172. data/lib/chemicalml/cml/base/object.rb +20 -6
  173. data/lib/chemicalml/cml/base/observation.rb +18 -5
  174. data/lib/chemicalml/cml/base/operator.rb +40 -0
  175. data/lib/chemicalml/cml/base/parameter.rb +38 -8
  176. data/lib/chemicalml/cml/base/parameter_list.rb +30 -5
  177. data/lib/chemicalml/cml/base/particle.rb +23 -6
  178. data/lib/chemicalml/cml/base/peak.rb +86 -11
  179. data/lib/chemicalml/cml/base/peak_group.rb +81 -7
  180. data/lib/chemicalml/cml/base/peak_list.rb +25 -8
  181. data/lib/chemicalml/cml/base/peak_structure.rb +47 -6
  182. data/lib/chemicalml/cml/base/plane3.rb +15 -5
  183. data/lib/chemicalml/cml/base/point3.rb +15 -5
  184. data/lib/chemicalml/cml/base/potential.rb +17 -6
  185. data/lib/chemicalml/cml/base/potential_form.rb +31 -6
  186. data/lib/chemicalml/cml/base/potential_list.rb +15 -7
  187. data/lib/chemicalml/cml/base/product.rb +53 -2
  188. data/lib/chemicalml/cml/base/product_list.rb +36 -2
  189. data/lib/chemicalml/cml/base/property.rb +32 -8
  190. data/lib/chemicalml/cml/base/property_list.rb +26 -5
  191. data/lib/chemicalml/cml/base/reactant.rb +53 -2
  192. data/lib/chemicalml/cml/base/reactant_list.rb +36 -2
  193. data/lib/chemicalml/cml/base/reaction.rb +82 -12
  194. data/lib/chemicalml/cml/base/reaction_list.rb +29 -2
  195. data/lib/chemicalml/cml/base/reaction_scheme.rb +48 -8
  196. data/lib/chemicalml/cml/base/reaction_step.rb +33 -9
  197. data/lib/chemicalml/cml/base/reaction_step_list.rb +34 -7
  198. data/lib/chemicalml/cml/base/reactive_centre.rb +31 -6
  199. data/lib/chemicalml/cml/base/region.rb +24 -5
  200. data/lib/chemicalml/cml/base/related_entry.rb +34 -0
  201. data/lib/chemicalml/cml/base/sample.rb +32 -5
  202. data/lib/chemicalml/cml/base/scalar.rb +41 -6
  203. data/lib/chemicalml/cml/base/spectator.rb +26 -5
  204. data/lib/chemicalml/cml/base/spectator_list.rb +20 -7
  205. data/lib/chemicalml/cml/base/spectrum.rb +63 -12
  206. data/lib/chemicalml/cml/base/spectrum_data.rb +20 -8
  207. data/lib/chemicalml/cml/base/spectrum_list.rb +30 -7
  208. data/lib/chemicalml/cml/base/sphere3.rb +15 -5
  209. data/lib/chemicalml/cml/base/stmml.rb +12 -5
  210. data/lib/chemicalml/cml/base/string.rb +40 -0
  211. data/lib/chemicalml/cml/base/string_array.rb +52 -0
  212. data/lib/chemicalml/cml/base/substance.rb +42 -8
  213. data/lib/chemicalml/cml/base/substance_list.rb +32 -7
  214. data/lib/chemicalml/cml/base/symmetry.rb +33 -7
  215. data/lib/chemicalml/cml/base/system.rb +23 -8
  216. data/lib/chemicalml/cml/base/table.rb +43 -8
  217. data/lib/chemicalml/cml/base/table_cell.rb +12 -5
  218. data/lib/chemicalml/cml/base/table_content.rb +20 -8
  219. data/lib/chemicalml/cml/base/table_header.rb +15 -7
  220. data/lib/chemicalml/cml/base/table_header_cell.rb +27 -5
  221. data/lib/chemicalml/cml/base/table_row.rb +15 -7
  222. data/lib/chemicalml/cml/base/table_row_list.rb +15 -7
  223. data/lib/chemicalml/cml/base/tcell.rb +43 -0
  224. data/lib/chemicalml/cml/base/torsion.rb +34 -7
  225. data/lib/chemicalml/cml/base/transform3.rb +12 -5
  226. data/lib/chemicalml/cml/base/transition_state.rb +23 -5
  227. data/lib/chemicalml/cml/base/trow.rb +48 -0
  228. data/lib/chemicalml/cml/base/unit.rb +45 -10
  229. data/lib/chemicalml/cml/base/unit_list.rb +40 -6
  230. data/lib/chemicalml/cml/base/unit_type.rb +32 -5
  231. data/lib/chemicalml/cml/base/unit_type_list.rb +34 -6
  232. data/lib/chemicalml/cml/base/vector3.rb +15 -5
  233. data/lib/chemicalml/cml/base/xaxis.rb +33 -7
  234. data/lib/chemicalml/cml/base/yaxis.rb +33 -7
  235. data/lib/chemicalml/cml/base/z_matrix.rb +26 -5
  236. data/lib/chemicalml/cml/base.rb +139 -121
  237. data/lib/chemicalml/cml/canonical_comparison.rb +54 -0
  238. data/lib/chemicalml/cml/elements.rb +168 -124
  239. data/lib/chemicalml/cml/enums.rb +55 -0
  240. data/lib/chemicalml/cml/namespace.rb +2 -3
  241. data/lib/chemicalml/cml/patterns.rb +33 -0
  242. data/lib/chemicalml/cml/reference_resolver.rb +137 -0
  243. data/lib/chemicalml/cml/role/alternative.rb +10 -0
  244. data/lib/chemicalml/cml/role/annotation.rb +10 -0
  245. data/lib/chemicalml/cml/role/appinfo.rb +10 -0
  246. data/lib/chemicalml/cml/role/arg.rb +10 -0
  247. data/lib/chemicalml/cml/role/cml_module.rb +2 -3
  248. data/lib/chemicalml/cml/role/complex_object.rb +10 -0
  249. data/lib/chemicalml/cml/role/enumeration.rb +10 -0
  250. data/lib/chemicalml/cml/role/expression.rb +10 -0
  251. data/lib/chemicalml/cml/role/float.rb +10 -0
  252. data/lib/chemicalml/cml/role/float_array.rb +10 -0
  253. data/lib/chemicalml/cml/role/integer.rb +10 -0
  254. data/lib/chemicalml/cml/role/integer_array.rb +10 -0
  255. data/lib/chemicalml/cml/role/operator.rb +10 -0
  256. data/lib/chemicalml/cml/role/related_entry.rb +10 -0
  257. data/lib/chemicalml/cml/role/string.rb +10 -0
  258. data/lib/chemicalml/cml/role/string_array.rb +10 -0
  259. data/lib/chemicalml/cml/role/tcell.rb +10 -0
  260. data/lib/chemicalml/cml/role/trow.rb +10 -0
  261. data/lib/chemicalml/cml/role.rb +138 -121
  262. data/lib/chemicalml/cml/schema24/alternative.rb +14 -0
  263. data/lib/chemicalml/cml/schema24/annotation.rb +15 -0
  264. data/lib/chemicalml/cml/schema24/appinfo.rb +15 -0
  265. data/lib/chemicalml/cml/schema24/arg.rb +14 -0
  266. data/lib/chemicalml/cml/schema24/cml_module.rb +18 -0
  267. data/lib/chemicalml/cml/schema24/complex_object.rb +14 -0
  268. data/lib/chemicalml/cml/schema24/configuration.rb +15 -2
  269. data/lib/chemicalml/cml/schema24/enumeration.rb +16 -0
  270. data/lib/chemicalml/cml/schema24/expression.rb +14 -0
  271. data/lib/chemicalml/cml/schema24/float.rb +14 -0
  272. data/lib/chemicalml/cml/schema24/float_array.rb +14 -0
  273. data/lib/chemicalml/cml/schema24/integer.rb +14 -0
  274. data/lib/chemicalml/cml/schema24/integer_array.rb +14 -0
  275. data/lib/chemicalml/cml/schema24/operator.rb +14 -0
  276. data/lib/chemicalml/cml/schema24/related_entry.rb +14 -0
  277. data/lib/chemicalml/cml/schema24/string.rb +14 -0
  278. data/lib/chemicalml/cml/schema24/string_array.rb +14 -0
  279. data/lib/chemicalml/cml/schema24/tcell.rb +14 -0
  280. data/lib/chemicalml/cml/schema24/trow.rb +14 -0
  281. data/lib/chemicalml/cml/schema24.rb +154 -126
  282. data/lib/chemicalml/cml/schema3.rb +123 -123
  283. data/lib/chemicalml/cml/visitable.rb +77 -3
  284. data/lib/chemicalml/cml/wire_class_registry.rb +3 -3
  285. data/lib/chemicalml/cml.rb +164 -132
  286. data/lib/chemicalml/context_configuration.rb +12 -4
  287. data/lib/chemicalml/convention/base.rb +17 -3
  288. data/lib/chemicalml/convention/cascade/constraints/reaction_scheme_must_have_content.rb +33 -0
  289. data/lib/chemicalml/convention/cascade/constraints/reaction_step_list_must_contain_steps.rb +31 -0
  290. data/lib/chemicalml/convention/cascade/constraints/reaction_step_must_have_reaction_or_lists.rb +33 -0
  291. data/lib/chemicalml/convention/cascade/constraints/reactive_centre_atom_refs_should_be_present.rb +34 -0
  292. data/lib/chemicalml/convention/cascade/constraints.rb +18 -0
  293. data/lib/chemicalml/convention/cascade.rb +43 -0
  294. data/lib/chemicalml/convention/compchem/constraints/array_rules.rb +44 -0
  295. data/lib/chemicalml/convention/compchem/constraints/calculation_requires_finalization.rb +28 -0
  296. data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +5 -4
  297. data/lib/chemicalml/convention/compchem/constraints/environment_at_most_one_property_list.rb +26 -0
  298. data/lib/chemicalml/convention/compchem/constraints/environment_must_not_contain_parameter.rb +28 -0
  299. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_molecule.rb +26 -0
  300. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_property_list.rb +26 -0
  301. data/lib/chemicalml/convention/compchem/constraints/finalization_must_have_content.rb +44 -0
  302. data/lib/chemicalml/convention/compchem/constraints/finalization_must_not_contain_parameter.rb +27 -0
  303. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_molecule.rb +26 -0
  304. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_parameter_list.rb +26 -0
  305. data/lib/chemicalml/convention/compchem/constraints/initialization_must_have_content.rb +54 -0
  306. data/lib/chemicalml/convention/compchem/constraints/initialization_must_not_contain_property.rb +28 -0
  307. data/lib/chemicalml/convention/compchem/constraints/job_list_module_must_have_id.rb +25 -0
  308. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_environment.rb +25 -0
  309. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_finalization.rb +25 -0
  310. data/lib/chemicalml/convention/compchem/constraints/job_module_must_have_id.rb +25 -0
  311. data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +7 -9
  312. data/lib/chemicalml/convention/compchem/constraints/matrix_rules.rb +48 -0
  313. data/lib/chemicalml/convention/compchem/constraints/module_predicates.rb +50 -0
  314. data/lib/chemicalml/convention/compchem/constraints/scalar_units.rb +42 -0
  315. data/lib/chemicalml/convention/compchem/constraints.rb +40 -2
  316. data/lib/chemicalml/convention/compchem.rb +20 -2
  317. data/lib/chemicalml/convention/constraint.rb +68 -24
  318. data/lib/chemicalml/convention/coordinator.rb +90 -0
  319. data/lib/chemicalml/convention/detection.rb +39 -0
  320. data/lib/chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace.rb +29 -0
  321. data/lib/chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash.rb +32 -0
  322. data/lib/chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern.rb +33 -0
  323. data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +4 -4
  324. data/lib/chemicalml/convention/dictionary/constraints/entry_must_contain_definition.rb +29 -0
  325. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +6 -6
  326. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_unit_type.rb +31 -0
  327. data/lib/chemicalml/convention/dictionary/constraints/entry_units_co_constraints.rb +44 -0
  328. data/lib/chemicalml/convention/dictionary/constraints.rb +14 -2
  329. data/lib/chemicalml/convention/dictionary.rb +8 -2
  330. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula.rb +41 -0
  331. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +18 -9
  332. data/lib/chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired.rb +6 -6
  333. data/lib/chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table.rb +39 -0
  334. data/lib/chemicalml/convention/molecular/constraints/atom_id_must_match_pattern.rb +7 -6
  335. data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +3 -2
  336. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_element_type.rb +4 -4
  337. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_id.rb +5 -4
  338. data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb +44 -0
  339. data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb +65 -0
  340. data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb +37 -0
  341. data/lib/chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct.rb +44 -0
  342. data/lib/chemicalml/convention/molecular/constraints/bond_id_should_match_pattern.rb +38 -0
  343. data/lib/chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule.rb +44 -0
  344. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2.rb +4 -4
  345. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_order.rb +5 -4
  346. data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +4 -3
  347. data/lib/chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref.rb +34 -0
  348. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum.rb +39 -0
  349. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric.rb +3 -3
  350. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb +43 -0
  351. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb +45 -0
  352. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb +33 -0
  353. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum.rb +38 -0
  354. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2.rb +45 -0
  355. data/lib/chemicalml/convention/molecular/constraints/dict_ref_should_resolve.rb +59 -0
  356. data/lib/chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children.rb +33 -0
  357. data/lib/chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children.rb +32 -0
  358. data/lib/chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum.rb +38 -0
  359. data/lib/chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level.rb +35 -0
  360. data/lib/chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern.rb +38 -0
  361. data/lib/chemicalml/convention/molecular/constraints/molecule_must_have_id.rb +4 -4
  362. data/lib/chemicalml/convention/molecular/constraints/property_must_have_dict_ref.rb +4 -4
  363. data/lib/chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary.rb +58 -0
  364. data/lib/chemicalml/convention/molecular/constraints/references_should_resolve.rb +37 -0
  365. data/lib/chemicalml/convention/molecular/constraints/scalar_must_have_data_type.rb +4 -4
  366. data/lib/chemicalml/convention/molecular/constraints.rb +59 -13
  367. data/lib/chemicalml/convention/molecular.rb +25 -2
  368. data/lib/chemicalml/convention/registry.rb +95 -10
  369. data/lib/chemicalml/convention/simple_unit/constraints/root_must_be_unit_list.rb +24 -0
  370. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_power.rb +32 -0
  371. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_symbol.rb +31 -0
  372. data/lib/chemicalml/convention/simple_unit/constraints.rb +16 -0
  373. data/lib/chemicalml/convention/simple_unit.rb +37 -0
  374. data/lib/chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks.rb +31 -0
  375. data/lib/chemicalml/convention/spectroscopy/constraints/peak_should_have_values.rb +34 -0
  376. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content.rb +31 -0
  377. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention.rb +32 -0
  378. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format.rb +31 -0
  379. data/lib/chemicalml/convention/spectroscopy/constraints.rb +20 -0
  380. data/lib/chemicalml/convention/spectroscopy.rb +41 -0
  381. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit.rb +29 -0
  382. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace.rb +36 -0
  383. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition.rb +28 -0
  384. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_id.rb +28 -0
  385. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si.rb +34 -0
  386. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si.rb +32 -0
  387. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +4 -4
  388. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_title.rb +32 -0
  389. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve.rb +45 -0
  390. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve.rb +46 -0
  391. data/lib/chemicalml/convention/unit_dictionary/constraints.rb +19 -1
  392. data/lib/chemicalml/convention/unit_dictionary.rb +11 -2
  393. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern.rb +31 -0
  394. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type.rb +28 -0
  395. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace.rb +35 -0
  396. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition.rb +28 -0
  397. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +6 -6
  398. data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +9 -1
  399. data/lib/chemicalml/convention/unit_type_dictionary.rb +6 -2
  400. data/lib/chemicalml/convention/validation_report.rb +90 -0
  401. data/lib/chemicalml/convention/violation.rb +9 -3
  402. data/lib/chemicalml/convention.rb +49 -11
  403. data/lib/chemicalml/dictionary/entry.rb +6 -6
  404. data/lib/chemicalml/dictionary/enum.rb +1 -1
  405. data/lib/chemicalml/dictionary/link.rb +3 -3
  406. data/lib/chemicalml/dictionary/loader.rb +19 -19
  407. data/lib/chemicalml/dictionary/model.rb +5 -5
  408. data/lib/chemicalml/dictionary/registry.rb +9 -9
  409. data/lib/chemicalml/dictionary.rb +13 -9
  410. data/lib/chemicalml/logger.rb +47 -0
  411. data/lib/chemicalml/schema/definition.rb +2 -2
  412. data/lib/chemicalml/schema.rb +7 -7
  413. data/lib/chemicalml/version.rb +1 -1
  414. data/lib/chemicalml/versioned_parser.rb +28 -10
  415. data/lib/chemicalml.rb +79 -17
  416. data/lib/tasks/reference_fetch.rake +30 -31
  417. data/lib/tasks/scrape_fixtures.rake +23 -24
  418. metadata +235 -35
  419. data/lib/chemicalml/cml/child_mappings.rb +0 -168
  420. data/lib/chemicalml/cml/translator/value_translations.rb +0 -270
  421. data/lib/chemicalml/cml/translator.rb +0 -484
  422. data/lib/chemicalml/model/atom.rb +0 -64
  423. data/lib/chemicalml/model/atom_parity.rb +0 -21
  424. data/lib/chemicalml/model/bond.rb +0 -39
  425. data/lib/chemicalml/model/bond_stereo.rb +0 -29
  426. data/lib/chemicalml/model/cml_array.rb +0 -27
  427. data/lib/chemicalml/model/cml_module.rb +0 -45
  428. data/lib/chemicalml/model/document.rb +0 -29
  429. data/lib/chemicalml/model/formula.rb +0 -34
  430. data/lib/chemicalml/model/identifier.rb +0 -21
  431. data/lib/chemicalml/model/label.rb +0 -21
  432. data/lib/chemicalml/model/matrix.rb +0 -29
  433. data/lib/chemicalml/model/metadata.rb +0 -23
  434. data/lib/chemicalml/model/metadata_list.rb +0 -25
  435. data/lib/chemicalml/model/molecule.rb +0 -45
  436. data/lib/chemicalml/model/name.rb +0 -21
  437. data/lib/chemicalml/model/node.rb +0 -45
  438. data/lib/chemicalml/model/parameter.rb +0 -27
  439. data/lib/chemicalml/model/parameter_list.rb +0 -25
  440. data/lib/chemicalml/model/product.rb +0 -21
  441. data/lib/chemicalml/model/product_list.rb +0 -21
  442. data/lib/chemicalml/model/property.rb +0 -28
  443. data/lib/chemicalml/model/property_list.rb +0 -25
  444. data/lib/chemicalml/model/reactant.rb +0 -21
  445. data/lib/chemicalml/model/reactant_list.rb +0 -21
  446. data/lib/chemicalml/model/reaction.rb +0 -41
  447. data/lib/chemicalml/model/reaction_list.rb +0 -23
  448. data/lib/chemicalml/model/scalar.rb +0 -25
  449. data/lib/chemicalml/model/substance.rb +0 -25
  450. data/lib/chemicalml/model.rb +0 -40
@@ -5,31 +5,77 @@ module Chemicalml
5
5
  module Molecular
6
6
  module Constraints
7
7
  autoload :AtomArrayMustContainAtoms,
8
- "chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms"
8
+ 'chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms'
9
9
  autoload :AtomIdsUniqueWithinMolecule,
10
- "chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule"
10
+ 'chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule'
11
11
  autoload :BondMustReferenceAtomsInSameMolecule,
12
- "chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule"
12
+ 'chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule'
13
13
  autoload :AtomMustHaveId,
14
- "chemicalml/convention/molecular/constraints/atom_must_have_id"
14
+ 'chemicalml/convention/molecular/constraints/atom_must_have_id'
15
15
  autoload :AtomMustHaveElementType,
16
- "chemicalml/convention/molecular/constraints/atom_must_have_element_type"
16
+ 'chemicalml/convention/molecular/constraints/atom_must_have_element_type'
17
17
  autoload :BondMustHaveAtomRefs2,
18
- "chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2"
18
+ 'chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2'
19
19
  autoload :BondMustHaveOrder,
20
- "chemicalml/convention/molecular/constraints/bond_must_have_order"
20
+ 'chemicalml/convention/molecular/constraints/bond_must_have_order'
21
21
  autoload :MoleculeMustHaveId,
22
- "chemicalml/convention/molecular/constraints/molecule_must_have_id"
22
+ 'chemicalml/convention/molecular/constraints/molecule_must_have_id'
23
23
  autoload :AtomCoordinatesMustBePaired,
24
- "chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired"
24
+ 'chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired'
25
25
  autoload :PropertyMustHaveDictRef,
26
- "chemicalml/convention/molecular/constraints/property_must_have_dict_ref"
26
+ 'chemicalml/convention/molecular/constraints/property_must_have_dict_ref'
27
27
  autoload :ScalarMustHaveDataType,
28
- "chemicalml/convention/molecular/constraints/scalar_must_have_data_type"
28
+ 'chemicalml/convention/molecular/constraints/scalar_must_have_data_type'
29
29
  autoload :BondOrderShouldNotBeNumeric,
30
- "chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric"
30
+ 'chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric'
31
31
  autoload :AtomIdMustMatchPattern,
32
- "chemicalml/convention/molecular/constraints/atom_id_must_match_pattern"
32
+ 'chemicalml/convention/molecular/constraints/atom_id_must_match_pattern'
33
+ autoload :MoleculeCountMustNotAppearOnTopLevel,
34
+ 'chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level'
35
+ autoload :MoleculeAtomArrayMutuallyExclusiveWithChildren,
36
+ 'chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children'
37
+ autoload :MoleculeBondArrayMutuallyExclusiveWithChildren,
38
+ 'chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children'
39
+ autoload :BondStereoWedgeHashMustHaveAtomRefs2,
40
+ 'chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2'
41
+ autoload :BondStereoCisTransMustHaveAtomRefs4,
42
+ 'chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4'
43
+ autoload :BondStereoOtherMustHaveDictRef,
44
+ 'chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref'
45
+ autoload :BondIdsUniqueWithinMolecule,
46
+ 'chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule'
47
+ autoload :BondOrderOtherMustHaveDictRef,
48
+ 'chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref'
49
+ autoload :AtomArrayMustBeChildOfMoleculeOrFormula,
50
+ 'chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula'
51
+ autoload :BondArrayMustBeChildOfMolecule,
52
+ 'chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule'
53
+ autoload :BondOrderShouldBeInEnum,
54
+ 'chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum'
55
+ autoload :BondStereoShouldBeInEnum,
56
+ 'chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum'
57
+ autoload :MoleculeChiralityShouldBeInEnum,
58
+ 'chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum'
59
+ autoload :BondAtomRefs2ShouldBeDistinct,
60
+ 'chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct'
61
+ autoload :ReferencesShouldResolve,
62
+ 'chemicalml/convention/molecular/constraints/references_should_resolve'
63
+ autoload :BondStereoAtomRefs4ShouldBeDistinct,
64
+ 'chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct'
65
+ autoload :AtomParityAtomRefs4ShouldBeDistinct,
66
+ 'chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct'
67
+ autoload :AtomElementTypeShouldBeInPeriodicTable,
68
+ 'chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table'
69
+ autoload :DictRefShouldResolve,
70
+ 'chemicalml/convention/molecular/constraints/dict_ref_should_resolve'
71
+ autoload :MoleculeIdShouldMatchPattern,
72
+ 'chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern'
73
+ autoload :BondIdShouldMatchPattern,
74
+ 'chemicalml/convention/molecular/constraints/bond_id_should_match_pattern'
75
+ autoload :AtomParityShouldIncludeParentAtom,
76
+ 'chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom'
77
+ autoload :PropertyScalarDataTypeMatchesDictionary,
78
+ 'chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary'
33
79
  end
34
80
  end
35
81
  end
@@ -8,9 +8,9 @@ module Chemicalml
8
8
  module Molecular
9
9
  extend Base
10
10
 
11
- autoload :Constraints, "chemicalml/convention/molecular/constraints"
11
+ autoload :Constraints, 'chemicalml/convention/molecular/constraints'
12
12
 
13
- QNAME = "convention:molecular".freeze
13
+ QNAME = 'convention:molecular'
14
14
  NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}molecular".freeze
15
15
 
16
16
  def self.qname
@@ -34,6 +34,29 @@ module Chemicalml
34
34
  register Constraints::ScalarMustHaveDataType
35
35
  register Constraints::BondOrderShouldNotBeNumeric
36
36
  register Constraints::AtomIdMustMatchPattern
37
+ register Constraints::MoleculeCountMustNotAppearOnTopLevel
38
+ register Constraints::MoleculeAtomArrayMutuallyExclusiveWithChildren
39
+ register Constraints::MoleculeBondArrayMutuallyExclusiveWithChildren
40
+ register Constraints::BondStereoWedgeHashMustHaveAtomRefs2
41
+ register Constraints::BondStereoCisTransMustHaveAtomRefs4
42
+ register Constraints::BondStereoOtherMustHaveDictRef
43
+ register Constraints::BondIdsUniqueWithinMolecule
44
+ register Constraints::BondOrderOtherMustHaveDictRef
45
+ register Constraints::AtomArrayMustBeChildOfMoleculeOrFormula
46
+ register Constraints::BondArrayMustBeChildOfMolecule
47
+ register Constraints::BondOrderShouldBeInEnum
48
+ register Constraints::BondStereoShouldBeInEnum
49
+ register Constraints::MoleculeChiralityShouldBeInEnum
50
+ register Constraints::BondAtomRefs2ShouldBeDistinct
51
+ register Constraints::ReferencesShouldResolve
52
+ register Constraints::BondStereoAtomRefs4ShouldBeDistinct
53
+ register Constraints::AtomParityAtomRefs4ShouldBeDistinct
54
+ register Constraints::AtomElementTypeShouldBeInPeriodicTable
55
+ register Constraints::DictRefShouldResolve
56
+ register Constraints::MoleculeIdShouldMatchPattern
57
+ register Constraints::BondIdShouldMatchPattern
58
+ register Constraints::AtomParityShouldIncludeParentAtom
59
+ register Constraints::PropertyScalarDataTypeMatchesDictionary
37
60
  end
38
61
  end
39
62
  end
@@ -23,25 +23,110 @@ module Chemicalml
23
23
  convention.validate(document)
24
24
  end
25
25
 
26
+ def self.validate_report(document, qname:)
27
+ convention = lookup(qname)
28
+ raise ArgumentError, "unknown convention: #{qname.inspect}" unless convention
29
+
30
+ convention.validate_report(document)
31
+ end
32
+
33
+ # Detect the convention from the document's root `convention`
34
+ # attribute and validate. Raises ArgumentError if the document
35
+ # declares no convention or an unknown one.
36
+ def self.detect_and_validate(document)
37
+ qname = Convention::Detection.convention_of(document)
38
+ raise ArgumentError, 'document declares no convention attribute' unless qname
39
+
40
+ validate_report(document, qname: qname)
41
+ end
42
+
26
43
  def self.builtin_qnames
27
44
  load_cache.keys.sort
28
45
  end
29
46
 
47
+ # Iterate every registered convention. Yields the convention
48
+ # module. Enumerable-style: returns an Enumerator if no block.
49
+ #
50
+ # @yield [Module] each convention module.
51
+ # @return [Array<Module>, Enumerator] the conventions if a
52
+ # block is given; otherwise an Enumerator.
53
+ def self.each(&)
54
+ return to_enum(:each) unless block_given?
55
+
56
+ load_cache.values.sort_by(&:qname).each(&)
57
+ end
58
+
59
+ # Iterate every constraint across every convention. Yields
60
+ # (convention, constraint_class) pairs. Useful for documentation
61
+ # generation and code introspection.
62
+ #
63
+ # @yieldparam convention [Module] the convention module.
64
+ # @yieldparam constraint [Class] the constraint class.
65
+ # @return [Enumerator, Integer] count if block given; otherwise
66
+ # an Enumerator.
67
+ def self.each_constraint(&)
68
+ return to_enum(:each_constraint) unless block_given?
69
+
70
+ count = 0
71
+ each do |conv|
72
+ conv.constraints.each do |constraint|
73
+ yield(conv, constraint)
74
+ count += 1
75
+ end
76
+ end
77
+ count
78
+ end
79
+
80
+ # Total count of registered constraints across all conventions.
81
+ def self.total_constraint_count
82
+ each.sum(&:constraint_count)
83
+ end
84
+
85
+ # True if a document with the given Role could carry a
86
+ # convention attribute that Detection would recognise.
87
+ #
88
+ # @param role [Module] a Role::* module.
89
+ # @return [Boolean]
90
+ def self.convention_root?(role)
91
+ Detection::CONVENTION_ROOTS.any? { |r| role <= r }
92
+ rescue StandardError
93
+ Detection::CONVENTION_ROOTS.include?(role)
94
+ end
95
+
96
+ # Register a custom convention at runtime. The convention module
97
+ # must extend `Chemicalml::Convention::Base` and implement
98
+ # `qname` and `namespace_uri`.
99
+ #
100
+ # @param mod [Module] the convention module (must `extend Base`).
101
+ # @return [Module] the registered module.
102
+ # @raise [ArgumentError] if `mod.qname` is nil/empty.
103
+ def self.register_custom(mod)
104
+ qname = mod.qname
105
+ raise ArgumentError, 'convention module must return a non-empty qname' if qname.nil? || qname.to_s.empty?
106
+
107
+ # Force load_cache to populate (acquires/releases mutex internally),
108
+ # then mutate directly without re-entering the mutex.
109
+ cache = load_cache
110
+ cache[qname.to_s] = mod
111
+ mod
112
+ end
113
+
30
114
  def self.reset!
31
- @mutex.synchronize { @cache = nil }
115
+ @mutex.synchronize { @load_cache = nil }
32
116
  end
33
117
 
34
118
  def self.load_cache
35
119
  @mutex.synchronize do
36
- @cache ||= begin
37
- {
38
- Molecular.qname => Molecular,
39
- Compchem.qname => Compchem,
40
- Dictionary.qname => Dictionary,
41
- UnitDictionary.qname => UnitDictionary,
42
- UnitTypeDictionary.qname => UnitTypeDictionary
43
- }
44
- end
120
+ @load_cache ||= {
121
+ Molecular.qname => Molecular,
122
+ Compchem.qname => Compchem,
123
+ Dictionary.qname => Dictionary,
124
+ UnitDictionary.qname => UnitDictionary,
125
+ UnitTypeDictionary.qname => UnitTypeDictionary,
126
+ Spectroscopy.qname => Spectroscopy,
127
+ Cascade.qname => Cascade,
128
+ SimpleUnit.qname => SimpleUnit
129
+ }
45
130
  end
46
131
  end
47
132
  private_class_method :load_cache
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module SimpleUnit
6
+ module Constraints
7
+ # The root of a simpleUnit document MUST be a `<unitList>`
8
+ # declaring `convention="convention:simpleUnit"`. Any other
9
+ # root shape is rejected.
10
+ class RootMustBeUnitList < Chemicalml::Convention::Constraint::DocumentConstraint
11
+ self.description = 'The root of a simpleUnit document MUST be a `<unitList>` declaring `convention="convention:simpleUnit"`. Any other root shape is rejected.'
12
+ def check(document)
13
+ return [] if document.is_a?(Chemicalml::Cml::Role::UnitList) &&
14
+ document.convention == 'convention:simpleUnit'
15
+
16
+ [violation(path: '/',
17
+ message: 'simpleUnit root must be <unitList> declaring ' \
18
+ 'convention="convention:simpleUnit"')]
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module SimpleUnit
6
+ module Constraints
7
+ # A `<unit>` under simpleUnit MUST declare a `power` attribute
8
+ # (integer). Without a power the unit's exponent is undefined.
9
+ class UnitMustHavePower < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = "A `<unit>` under simpleUnit MUST declare a `power` attribute (integer). Without a power the unit's exponent is undefined."
11
+ applies_to Chemicalml::Cml::Role::Unit
12
+
13
+ def check_node(node, _path)
14
+ power = node.power.to_s.strip
15
+ return [] unless power.empty?
16
+
17
+ [violation(path: yield_path(node),
18
+ message: "unit #{node.id.inspect} must declare a power attribute",
19
+ value: node.power)]
20
+ end
21
+
22
+ private
23
+
24
+ def yield_path(node)
25
+ id = node.node_id
26
+ id ? "unit[#{id}]" : 'unit'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module SimpleUnit
6
+ module Constraints
7
+ # A `<unit>` under simpleUnit MUST declare a non-empty
8
+ # `symbol` attribute. Without a symbol the unit cannot be
9
+ # rendered in formulae.
10
+ class UnitMustHaveSymbol < Chemicalml::Convention::Constraint::NodeConstraint
11
+ self.description = 'A `<unit>` under simpleUnit MUST declare a non-empty `symbol` attribute. Without a symbol the unit cannot be rendered in formulae.'
12
+ applies_to Chemicalml::Cml::Role::Unit
13
+
14
+ def check_node(node, _path)
15
+ return [] unless node.symbol.to_s.strip.empty?
16
+
17
+ [violation(path: yield_path(node),
18
+ message: "unit #{node.id.inspect} must declare a non-empty symbol attribute")]
19
+ end
20
+
21
+ private
22
+
23
+ def yield_path(node)
24
+ id = node.node_id
25
+ id ? "unit[#{id}]" : 'unit'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module SimpleUnit
6
+ module Constraints
7
+ autoload :RootMustBeUnitList,
8
+ 'chemicalml/convention/simple_unit/constraints/root_must_be_unit_list'
9
+ autoload :UnitMustHavePower,
10
+ 'chemicalml/convention/simple_unit/constraints/unit_must_have_power'
11
+ autoload :UnitMustHaveSymbol,
12
+ 'chemicalml/convention/simple_unit/constraints/unit_must_have_symbol'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ # The simpleUnit convention — the worked example from the CMLLite
6
+ # paper. A toy convention demonstrating the convention framework.
7
+ #
8
+ # Namespace: http://www.xml-cml.org/convention/simpleUnit
9
+ #
10
+ # Constraints:
11
+ #
12
+ # - the root MUST be a `<unitList>` declaring
13
+ # `convention="convention:simpleUnit"`
14
+ # - each `<unit>` MUST have a `power` attribute (integer)
15
+ # - each `<unit>` MUST have a non-empty `symbol` attribute
16
+ module SimpleUnit
17
+ extend Base
18
+
19
+ autoload :Constraints, 'chemicalml/convention/simple_unit/constraints'
20
+
21
+ QNAME = 'convention:simpleUnit'
22
+ NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}simpleUnit".freeze
23
+
24
+ def self.qname
25
+ QNAME
26
+ end
27
+
28
+ def self.namespace_uri
29
+ NAMESPACE_URI
30
+ end
31
+
32
+ register Constraints::UnitMustHavePower
33
+ register Constraints::UnitMustHaveSymbol
34
+ register Constraints::RootMustBeUnitList
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module Spectroscopy
6
+ module Constraints
7
+ # A `<peakList>` MUST contain at least one `<peak>` or
8
+ # `<peakGroup>` child. An empty peakList carries no data.
9
+ class PeakListMustContainPeaks < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<peakList>` MUST contain at least one `<peak>` or `<peakGroup>` child. An empty peakList carries no data.'
11
+ applies_to Chemicalml::Cml::Role::PeakList
12
+
13
+ def check_node(node, _path)
14
+ peaks = (node.peaks || []) + (node.peak_groups || [])
15
+ return [] unless peaks.empty?
16
+
17
+ [violation(path: yield_path(node),
18
+ message: 'peakList must contain at least one peak or peakGroup')]
19
+ end
20
+
21
+ private
22
+
23
+ def yield_path(node)
24
+ id = node.node_id
25
+ id ? "peakList[#{id}]" : 'peakList'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module Spectroscopy
6
+ module Constraints
7
+ # A `<peak>` SHOULD declare at least one of `xValue` or
8
+ # `yValue`. A peak with neither carries no position
9
+ # information. Warning severity — there are edge cases
10
+ # (e.g. group-references) where this is intentional.
11
+ class PeakShouldHaveValues < Chemicalml::Convention::Constraint::NodeConstraint
12
+ self.description = 'A `<peak>` SHOULD declare at least one of `xValue` or `yValue`. A peak with neither carries no position information. Warning severity — there are edge cases'
13
+ applies_to Chemicalml::Cml::Role::Peak
14
+
15
+ def check_node(node, _path)
16
+ return [] unless node.xValue.to_s.empty? && node.yValue.to_s.empty?
17
+
18
+ [violation(path: yield_path(node),
19
+ message: "peak #{node.id.inspect} should have at least one of xValue or yValue",
20
+ severity: :warning,
21
+ value: { xValue: node.xValue, yValue: node.yValue }.freeze)]
22
+ end
23
+
24
+ private
25
+
26
+ def yield_path(node)
27
+ id = node.node_id
28
+ id ? "peak[#{id}]" : 'peak'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module Spectroscopy
6
+ module Constraints
7
+ # A `<spectrum>` MUST contain at least one of: `<xaxis>`,
8
+ # `<yaxis>`, `<peakList>`. An empty spectrum carries no data.
9
+ class SpectrumMustHaveContent < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<spectrum>` MUST contain at least one of: `<xaxis>`, `<yaxis>`, `<peakList>`. An empty spectrum carries no data.'
11
+ applies_to Chemicalml::Cml::Role::Spectrum
12
+
13
+ def check_node(node, _path)
14
+ return [] if node.xaxis || node.yaxis || node.peak_list
15
+
16
+ [violation(path: yield_path(node),
17
+ message: "spectrum #{node.id.inspect} must contain at least one of " \
18
+ 'xaxis, yaxis, or peakList')]
19
+ end
20
+
21
+ private
22
+
23
+ def yield_path(node)
24
+ id = node.node_id
25
+ id ? "spectrum[#{id}]" : 'spectrum'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module Spectroscopy
6
+ module Constraints
7
+ # A `<spectrum>` MUST declare its own convention attribute so
8
+ # consumers know how to interpret its format and peaks. Per
9
+ # the molecular convention: "spectrum — any number, each MUST
10
+ # specify its own convention".
11
+ class SpectrumMustHaveConvention < Chemicalml::Convention::Constraint::NodeConstraint
12
+ self.description = 'A `<spectrum>` MUST declare its own convention attribute so consumers know how to interpret its format and peaks. Per the molecular convention: "spectrum — any number, each MUST'
13
+ applies_to Chemicalml::Cml::Role::Spectrum
14
+
15
+ def check_node(node, _path)
16
+ return [] unless node.convention.to_s.empty?
17
+
18
+ [violation(path: yield_path(node),
19
+ message: "spectrum #{node.id.inspect} must declare its own convention attribute")]
20
+ end
21
+
22
+ private
23
+
24
+ def yield_path(node)
25
+ id = node.node_id
26
+ id ? "spectrum[#{id}]" : 'spectrum'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module Spectroscopy
6
+ module Constraints
7
+ # A `<spectrum>` MUST have a `format` attribute (e.g. "mass",
8
+ # "ir", "nmr", "uv") so consumers know the measurement type.
9
+ class SpectrumMustHaveFormat < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<spectrum>` MUST have a `format` attribute (e.g. "mass", "ir", "nmr", "uv") so consumers know the measurement type.'
11
+ applies_to Chemicalml::Cml::Role::Spectrum
12
+
13
+ def check_node(node, _path)
14
+ return [] unless node.format.to_s.empty?
15
+
16
+ [violation(path: yield_path(node),
17
+ message: "spectrum #{node.id.inspect} must have a format attribute " \
18
+ "(e.g. 'mass', 'ir', 'nmr', 'uv')")]
19
+ end
20
+
21
+ private
22
+
23
+ def yield_path(node)
24
+ id = node.node_id
25
+ id ? "spectrum[#{id}]" : 'spectrum'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module Spectroscopy
6
+ module Constraints
7
+ autoload :SpectrumMustHaveConvention,
8
+ 'chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention'
9
+ autoload :SpectrumMustHaveFormat,
10
+ 'chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format'
11
+ autoload :SpectrumMustHaveContent,
12
+ 'chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content'
13
+ autoload :PeakListMustContainPeaks,
14
+ 'chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks'
15
+ autoload :PeakShouldHaveValues,
16
+ 'chemicalml/convention/spectroscopy/constraints/peak_should_have_values'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ # The Spectroscopy convention. A chemicalml-specific convention for
6
+ # CML spectroscopy data (`<spectrum>`, `<peakList>`, `<peak>`).
7
+ #
8
+ # Upstream CML does not define a spectroscopy convention; the wire
9
+ # elements exist in the XSD but no constraints guard their
10
+ # well-formedness. This convention covers the gaps:
11
+ #
12
+ # - spectra MUST declare their own convention (per the molecular
13
+ # convention's "each spectrum MUST specify its own convention")
14
+ # - spectra MUST have a format (e.g. "mass", "ir", "nmr")
15
+ # - spectra MUST contain at least one of xaxis/yaxis/peakList
16
+ # - peakList MUST contain at least one peak/peakGroup
17
+ # - peaks SHOULD have at least one of xValue/yValue (warning)
18
+ module Spectroscopy
19
+ extend Base
20
+
21
+ autoload :Constraints, 'chemicalml/convention/spectroscopy/constraints'
22
+
23
+ QNAME = 'convention:spectroscopy'
24
+ NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}spectroscopy".freeze
25
+
26
+ def self.qname
27
+ QNAME
28
+ end
29
+
30
+ def self.namespace_uri
31
+ NAMESPACE_URI
32
+ end
33
+
34
+ register Constraints::SpectrumMustHaveConvention
35
+ register Constraints::SpectrumMustHaveFormat
36
+ register Constraints::SpectrumMustHaveContent
37
+ register Constraints::PeakListMustContainPeaks
38
+ register Constraints::PeakShouldHaveValues
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unitList>` element MUST contain one or more `<unit>`
8
+ # children, and MUST NOT contain any other CML-namespace
9
+ # child elements.
10
+ class UnitListMustContainAtLeastOneUnit < Chemicalml::Convention::Constraint::NodeConstraint
11
+ self.description = 'A `<unitList>` element MUST contain one or more `<unit>` children, and MUST NOT contain any other CML-namespace child elements.'
12
+ applies_to Chemicalml::Cml::Role::UnitList
13
+ def check_node(node, path)
14
+ return [] if (node.units || []).length.positive?
15
+
16
+ [violation(path: path.join('/'),
17
+ message: 'unitList must contain at least one unit child')]
18
+ end
19
+
20
+ private
21
+
22
+ def unit_list?(node)
23
+ node.is_a?(Chemicalml::Cml::Role::UnitList)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end