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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74eb61f89b283d3263aa4ead18e9b76061700defcc54232002855af6b1291c0a
4
- data.tar.gz: 228f8f704086b458a73b3f4cb679b25133cf5ee36aa16d58c688a6439abb1974
3
+ metadata.gz: ed7130181d6d55cbadb29d2630668ab4f16746f10dc20db9c9d1d0ff066586c6
4
+ data.tar.gz: ac78d79e906862aa42639b2d237ec66661a8c3061e4b25c04535f8941520b3d3
5
5
  SHA512:
6
- metadata.gz: ba13a979cce6d48bbef181917107ac9ad24291131e8568652a012698c5e3ebe7f43851f908408478e4b4481d270cb9b83371d9c3f19093700df19a97565af2a9
7
- data.tar.gz: 8557d650ab706936b25031541e256e840cc638d025d5c4b7308a512aa46e27d4223a050ba27b37c7d9c5408c1b1dd7af4974697699af317fec17a3ecfa262f92
6
+ metadata.gz: 5909cc1b258048f0c5aefba5844fff9136f2db4713d5cd5b80a22c07045e60a5ee5ead34680a841a9044039824c76cd4913d48d8f3410f86bf99b043f5739ba4
7
+ data.tar.gz: ff4f54de5ce734ae649554f593cfc1affa766b7e541fef394704bcd1b4d192dbae3e788241ced6a4dc6306bacf62b409fad2fdafe6861617497d7ff2ced16dee
@@ -16,13 +16,19 @@ on:
16
16
  Also, you can pass 'skip' to skip 'git tag' and do 'gem push' for the current version
17
17
  required: true
18
18
  default: 'skip'
19
+ acknowledge_breaking_in_patch:
20
+ description: 'Override the patch-release breaking-change guard when the trip is a known false-positive'
21
+ required: false
22
+ type: boolean
23
+ default: false
19
24
  repository_dispatch:
20
25
  types: [ do-release ]
21
26
 
22
27
  jobs:
23
28
  release:
24
- uses: relaton/support/.github/workflows/release.yml@main
29
+ uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
25
30
  with:
26
31
  next_version: ${{ github.event.inputs.next_version }}
32
+ acknowledge_breaking_in_patch: ${{ inputs.acknowledge_breaking_in_patch }}
27
33
  secrets:
28
34
  rubygems-api-key: ${{ secrets.LUTAML_CI_RUBYGEMS_API_KEY }}
data/.rubocop.yml CHANGED
@@ -10,11 +10,34 @@ AllCops:
10
10
  - "coverage/**/*"
11
11
  - "reference-docs/**/*"
12
12
  - "spec/fixtures/**/*"
13
+ - "data/**/*"
13
14
 
14
15
  Metrics/BlockLength:
15
16
  Exclude:
16
17
  - "spec/**/*"
17
18
  - "*.gemspec"
19
+ - "lib/chemicalml/cml/base/**/*.rb"
20
+
21
+ # Base::* modules are structurally long: each declares every attribute
22
+ # and both xml + key_value mapping blocks for one CML element in a
23
+ # single `self.included` hook. This is the correct structure — the
24
+ # alternative (splitting into smaller methods) would scatter one
25
+ # element's declarations across multiple files.
26
+ Metrics/MethodLength:
27
+ Exclude:
28
+ - "lib/chemicalml/cml/base/**/*.rb"
29
+
30
+ Metrics/AbcSize:
31
+ Exclude:
32
+ - "lib/chemicalml/cml/base/**/*.rb"
33
+
34
+ Metrics/ModuleLength:
35
+ Exclude:
36
+ - "lib/chemicalml/cml/base/**/*.rb"
37
+
38
+ Layout/LineLength:
39
+ Exclude:
40
+ - "lib/chemicalml/cml/base/**/*.rb"
18
41
 
19
42
  Style/Documentation:
20
43
  Enabled: false
data/CLAUDE.md CHANGED
@@ -26,6 +26,9 @@ bundle exec rspec -e "round-trips" # by name match
26
26
  bundle exec rake # alias for rspec
27
27
  bundle exec rubocop # lint
28
28
  bundle exec rubocop -A # autocorrect
29
+ bundle exec chemicalml validate <file.cml> # CLI validation
30
+ bundle exec chemicalml conventions # list registered conventions
31
+ bundle exec chemicalml dictionaries # list built-in dictionaries
29
32
  ```
30
33
 
31
34
  `spec/examples.txt` and `Gemfile.lock` are gitignored — `spec/examples.txt`
@@ -33,23 +36,30 @@ is regenerated by RSpec on every run, and `Gemfile.lock` is development-only.
33
36
 
34
37
  ## Architecture (big picture)
35
38
 
36
- There are **five namespaces** under `Chemicalml`, deliberately kept
39
+ There are **four namespaces** under `Chemicalml`, deliberately kept
37
40
  independent:
38
41
 
39
42
  ```
40
43
  Chemicalml
41
44
  ├── parse(xml, schema: :schema3) # top-level entry point
42
- ├── Model # canonical, format-agnostic chemistry objects
43
- ├── Cml # CML XML wire-format classes
44
- │ ├── Base::* # shared attribute+mapping mixins
45
- │ ├── Schema3::* # Schema 3 wire classes
46
- │ ├── Schema24::* # Schema 2.4 wire classes
47
- └── Translator # the only adapter between Model and Cml
45
+ ├── Cml # CML model (lutaml-model Serializables)
46
+ ├── Base::* # shared attribute + xml-mapping mixins
47
+ │ ├── Schema3::* # Schema 3 classes (121 elements)
48
+ │ ├── Schema24::* # Schema 2.4 classes (121 + 3 legacy)
49
+ │ ├── Role::* # type-marker modules (schema-version-agnostic)
50
+ ├── Visitable # constraint-walker interface
51
+ │ └── WireClassRegistry # (schema, role) → class lookup
48
52
  ├── Schema # version registry
49
53
  ├── Convention # 5 conventions + constraint framework
50
54
  └── Dictionary # dictionary model + YAML store
51
55
  ```
52
56
 
57
+ The Cml classes ARE the model — every one is a
58
+ `Lutaml::Model::Serializable` subclass with `attribute` declarations
59
+ and an `xml do ... end` mapping block. lutaml-model handles XML
60
+ parsing and serialization directly. There is no separate "canonical"
61
+ layer and no translator.
62
+
53
63
  ### Schema-versioned wire classes (mirrors `mml` V3/V4 pattern)
54
64
 
55
65
  Both Schema 3 and Schema 2.4 have **real, distinct** class hierarchies
@@ -83,43 +93,50 @@ Chemicalml::Cml::Schema3.parse(xml) # version-specific
83
93
  Chemicalml::Cml::Schema24.parse(xml) # version-specific
84
94
  ```
85
95
 
86
- ### The canonical model (`Chemicalml::Model::*`)
87
-
88
- Plain Ruby `Node` subclasses. **No XML, no JSON, no YAML**. Format-agnostic.
89
- Every adapter speaks this model; adapters never talk to each other directly.
90
- `Node` provides `accept(visitor)`, `value_attributes`, `children`, and
91
- class-level `short_name` so visitors written against the canonical model
92
- work across every adapter.
96
+ ### The CML model (`Chemicalml::Cml::*`)
93
97
 
94
- ### The CML wire layer (`Chemicalml::Cml::*`)
98
+ Every CML element is a `Lutaml::Model::Serializable` subclass — these
99
+ ARE the model. lutaml-model is format-agnostic (XML/JSON/YAML/TOML
100
+ via adapters), so the same class supports whatever serialization
101
+ lutaml-model supports. Each class includes three mixins:
95
102
 
96
- Every CML element is a `Lutaml::Model::Serializable` subclass that
97
- includes three mixins: a `Base::*` module (shared declarations +
98
- xml mapping + `namespace Chemicalml::Cml::Namespace`), a
99
- `Role::*` marker module (type identity used by constraints and the
100
- translator independent of which schema version's class it is), and
101
- `Cml::Visitable` (uniform interface for the constraint walker —
102
- `wire_children`, `node_id`, `element_name`).
103
+ - a `Base::*` module (shared `attribute` declarations + `xml do ... end`
104
+ mapping block + `namespace Chemicalml::Cml::Namespace`)
105
+ - a `Role::*` marker module (type identity used by constraints —
106
+ independent of which schema version's class it is)
107
+ - `Cml::Visitable` (uniform interface for the constraint walker
108
+ `wire_children`, `node_id`, `element_name`)
103
109
 
104
110
  Wire classes are generated in-place by each schema module from the
105
- shared `Chemicalml::Cml::Elements::ALL` table — no 70+ boilerplate
106
- files. Adding a new CML element = adding one entry to `Elements::ALL`,
107
- one `Base::*` mixin, and one `Role::*` module. Both `Schema3::*` and
108
- `Schema24::*` pick up the new element automatically (unless it's
109
- listed in `Elements::SCHEMA3_ONLY`).
111
+ shared `Chemicalml::Cml::Elements::ALL` table — 121 entries spanning
112
+ the entire Schema 3 XSD. Adding a new CML element = adding one entry
113
+ to `Elements::ALL`, one `Base::*` mixin, and one `Role::*` module.
114
+ Both `Schema3::*` and `Schema24::*` pick up the new element
115
+ automatically unless it's listed in `Elements::SCHEMA3_ONLY`
116
+ (currently `%i[AnyCml]` — the only element in Schema 3 that Schema 2.4
117
+ lacks; verified by direct XSD diff).
118
+
119
+ Schema 2.4 actually declares 137 elements (a superset of Schema 3 in
120
+ most respects); 17 legacy elements (`alternative`, `annotation`,
121
+ `appinfo`, `arg`, `complexObject`, `enumeration`, `expression`,
122
+ `float`, `floatArray`, `integer`, `integerArray`, `operator`,
123
+ `relatedEntry`, `string`, `stringArray`, `tcell`, `trow`) are
124
+ Schema-2.4-only. Three of them (`annotation`, `appinfo`,
125
+ `enumeration`) are explicitly modeled in `Elements::SCHEMA24_ONLY`
126
+ because they carry semantic meaning; the rest are redundant with
127
+ Schema 3's unified `scalar`/`array`/`table` machinery.
110
128
 
111
129
  The framework handles (de)serialization; **never** add `def to_xml` /
112
- `def from_xml` /
113
- `def to_h` to these classes.
130
+ `def from_xml` / `def to_h` to these classes.
114
131
 
115
- ### The translator (`Chemicalml::Cml::Translator`)
132
+ ### JSON / YAML wire names
116
133
 
117
- Pure transformation. `to_canonical(wire_doc)` accepts either Schema3
118
- or Schema24 wire documents (dispatches via `is_a?`).
119
- `from_canonical(model_doc, schema: :schema3)` produces the requested
120
- schema's wire format. It is the **only** place that imports both
121
- `Chemicalml::Model` and `Chemicalml::Cml`. Adding a new CML element
122
- means updating translator rules — the two namespaces stay decoupled.
134
+ Every `Base::*` module declares a `key_value do ... end` block
135
+ alongside its `xml do ... end` block, mirroring the XML wire names
136
+ explicitly. JSON and YAML output carry CML wire names
137
+ (`elementType`, `atomArray`) rather than Ruby snake_case names.
138
+ The blocks are written out by hand per Base module — no runtime
139
+ introspection.
123
140
 
124
141
  ### Schema versions
125
142
 
@@ -127,11 +144,20 @@ means updating translator rules — the two namespaces stay decoupled.
127
144
  (`schema24`, `schema3`). The schema XSDs are **archival source** —
128
145
  they must never be deleted, edited, or regenerated from code.
129
146
 
147
+ **Type-name collision**: Schema24's legacy `<string>`, `<integer>`,
148
+ `<float>` elements share their XML names with lutaml-model primitives
149
+ (`:string`, `:integer`, `:float`). They are NOT registered as types
150
+ in the Schema24 context (would shadow the primitive, breaking every
151
+ `:string` attribute cast). They remain defined as wire classes and
152
+ parseable as document roots. See
153
+ `Cml::Elements::SCHEMA24_TYPE_COLLISIONS`.
154
+
130
155
  ### Conventions
131
156
 
132
157
  `Chemicalml::Convention` is a registry of named constraint sets
133
158
  (`molecular`, `compchem`, `dictionary`, `unit-dictionary`,
134
- `unitType-dictionary`). Each convention owns:
159
+ `unitType-dictionary`, `spectroscopy`, `cascade`, `simpleUnit`).
160
+ Each convention owns:
135
161
 
136
162
  - a namespace URI (e.g. `http://www.xml-cml.org/convention/molecular`)
137
163
  - a set of constraint classes registered against it
@@ -143,6 +169,33 @@ renderer code (Open/Closed Principle).
143
169
  The constraint walker uses `Cml::Visitable#wire_children` to traverse
144
170
  the tree — no `respond_to?` duck typing.
145
171
 
172
+ The shared `Cml::Base::CommonChildren` mixin provides `metadataList`,
173
+ `label`, `name`, and `description` children that the XSD grants to
174
+ most container elements. Including it in a `Base::*` module is the
175
+ DRY way to declare the universal child set.
176
+
177
+ `Cml::Enums` is the canonical Ruby source of truth for every XSD
178
+ enum simpleType. Three constraints validate against it
179
+ (`BondOrderShouldBeInEnum`, `BondStereoShouldBeInEnum`,
180
+ `MoleculeChiralityShouldBeInEnum`) at warning severity — extension
181
+ values are permitted but flagged.
182
+
183
+ `Cml::ReferenceResolver` walks a document and resolves
184
+ id-references (`atomRefs2`, `atomRefs4`, `bondRefs`, `ref`) to
185
+ target wire instances. `unresolved_refs` lists missing targets;
186
+ useful for callers that need to follow CML references.
187
+
188
+ ### Parallel-array serialisation
189
+
190
+ `<atomArray>` and `<bondArray>` support two equivalent XML forms: the
191
+ child form (`<atomArray><atom.../><atom.../></atomArray>`) and the
192
+ parallel-array form (`<atomArray atomID="a1 a2" elementType="C O"/>`).
193
+ The wire attribute names in the parallel-array form match the
194
+ singular `<atom>` / `<bond>` attribute names (e.g. `elementType`,
195
+ `atomRef1`) — the XSD attribute *group* is named `elementTypeArray`
196
+ etc., but the wire attribute is `elementType`. Ruby attribute names
197
+ carry an `_array` suffix for clarity at the call site.
198
+
146
199
  ### Dictionaries
147
200
 
148
201
  `Chemicalml::Dictionary` is the model; entries have `id`, `term`,
@@ -172,9 +225,11 @@ These are non-negotiable and apply in addition to the user's global rules:
172
225
  or `respond_to?` for type checks.** Use `is_a?` or design the type
173
226
  hierarchy so the check isn't needed.
174
227
 
175
- 5. **One concern, one place (MECE).** Adding a CML element = adding one
176
- model class + one Cml class + one translator rule + specs. Don't
177
- entangle them.
228
+ 5. **One concern, one place (MECE).** Adding a CML element = adding
229
+ one `Base::*` mixin + one `Role::*` module + one entry to
230
+ `Elements::ALL` + (if needed) one entry to `SCHEMA3_ONLY` or
231
+ `SCHEMA24_ONLY`. Both Schema3::* and Schema24::* pick it up
232
+ automatically.
178
233
 
179
234
  ## Reference docs (in-tree)
180
235
 
data/README.adoc CHANGED
@@ -4,13 +4,19 @@ ChemML provides a https://www.xml-cml.org/[Chemical Markup Language (CML)]
4
4
  object model for Ruby, built on
5
5
  {lutaml-model}[https://github.com/lutaml/lutaml-model].
6
6
 
7
- == Purpose
8
-
9
- CML is the established XML standard for chemistry. Existing Ruby
10
- libraries either don't exist or use hand-rolled XML manipulation that
11
- drifts from the CML schema. ChemML gives CML a proper model layer
12
- with declarative attribute/mapping definitions and framework-backed
13
- serialization.
7
+ == Features
8
+
9
+ * Full Schema 3 and Schema 2.4 coverage — every CML element, attribute,
10
+ and XSD enum/pattern modelled as Ruby classes.
11
+ * Format-agnostic serialization XML, JSON, YAML, and TOML round-trip
12
+ via lutaml-model adapters.
13
+ * Eight built-in conventions with 88+ registered constraints:
14
+ `molecular`, `compchem`, `dictionary`, `unit-dictionary`,
15
+ `unitType-dictionary`, `spectroscopy`, `cascade`, `simpleUnit`.
16
+ * Built-in dictionaries (cml, compchem, cif, unit_si, unit_non_si,
17
+ unit_type, cml_name, cml_formula) loaded from YAML.
18
+ * CLI for command-line validation and inspection.
19
+ * Ruby-native query API (`doc.find_atom(id)`, `doc.each_molecule`).
14
20
 
15
21
  == Install
16
22
 
@@ -30,45 +36,167 @@ $ gem install chemicalml
30
36
 
31
37
  == Usage
32
38
 
33
- Parse a CML document:
39
+ === Parse a CML document
34
40
 
35
41
  [source,ruby]
36
42
  ----
37
43
  require "chemicalml"
38
44
 
39
- doc = Chemicalml::Cml::Document.from_xml(File.read("ethanol.cml"))
40
- doc.molecules.first.atoms.first.element_type # => "C"
45
+ doc = Chemicalml.parse_file("ethanol.cml")
46
+ doc.molecules.first.atom_array.atoms.first.element_type # => "C"
47
+ ----
48
+
49
+ === Validate against a convention
50
+
51
+ [source,ruby]
52
+ ----
53
+ report = Chemicalml.validate(doc)
54
+ puts report.summary
55
+ puts report.errors.map(&:message)
41
56
  ----
42
57
 
43
- Build a document programmatically:
58
+ === Build a document programmatically
44
59
 
45
60
  [source,ruby]
46
61
  ----
47
62
  atom = Chemicalml::Cml::Atom.new(id: "a1", element_type: "C")
48
- mol = Chemicalml::Cml::Molecule.new(id: "m1", atoms: [atom])
63
+ mol = Chemicalml::Cml::Molecule.new(
64
+ id: "m1",
65
+ atom_array: Chemicalml::Cml::AtomArray.new(atoms: [atom])
66
+ )
49
67
  doc = Chemicalml::Cml::Document.new(molecules: [mol])
50
68
 
51
69
  doc.to_xml # => well-formed CML XML
52
70
  ----
53
71
 
54
- == Design
72
+ === Query the tree
55
73
 
56
- Each CML element is a `Lutaml::Model::Serializable` subclass with
57
- declared attributes and an XML mapping block. Serialization goes
58
- through lutaml-model no hand-rolled XML.
74
+ [source,ruby]
75
+ ----
76
+ doc.each_atom { |a| puts "#{a.id}: #{a.element_type}" }
77
+ doc.find_atom("a1") # => Atom instance
78
+ doc.atom_count # => recursive count
79
+ doc.find_molecule("m1") # => Molecule instance
80
+ ----
81
+
82
+ === Cross-format serialisation
83
+
84
+ [source,ruby]
85
+ ----
86
+ doc.to_xml # CML XML
87
+ doc.to_json # JSON with CML wire names (elementType, atomArray, ...)
88
+ doc.to_yaml # YAML
89
+ ----
90
+
91
+ === Resolve references
92
+
93
+ [source,ruby]
94
+ ----
95
+ resolver = Chemicalml::Cml::ReferenceResolver.new(doc)
96
+ resolver.unresolved_refs # => [{ node: <Bond>, attr: :atom_refs2, missing: ["a99"] }]
97
+ ----
98
+
99
+ == CLI
100
+
101
+ [source,sh]
102
+ ----
103
+ $ chemicalml validate file.cml # auto-detect convention, print violations
104
+ $ chemicalml inspect file.cml # tree-style summary
105
+ $ chemicalml conventions # list 8 registered conventions
106
+ $ chemicalml dictionaries # list built-in YAML dictionaries
107
+ $ chemicalml elements # list all CML wire classes
108
+ ----
109
+
110
+ == Conventions
111
+
112
+ Eight conventions register 88+ constraints:
113
+
114
+ [cols="1,1,1"]
115
+ |===
116
+ | Convention | Constraints | Description
117
+
118
+ | molecular | 35 | Atom/bond/molecule well-formedness, id patterns,
119
+ element type validation, dictRef resolution, AtomParity parent
120
+ check, references-resolve check.
121
+ | compchem | 20 | JobList/Job/Initialization/Calculation/Finalization
122
+ module structure, scalar/array/matrix value rules.
123
+ | dictionary | 8 | Dictionary namespace, prefix, entry id/term/definition,
124
+ unitType/units co-constraints.
125
+ | unit-dictionary | 8 | UnitList namespace, unit id/title/symbol/parentSI/
126
+ multiplierToSI, definition child.
127
+ | unitType-dictionary | 5 | UnitTypeList namespace, unitType id/name/definition.
128
+ | spectroscopy | 5 | Spectrum convention/format/content, peakList, peak values.
129
+ | cascade | 4 | ReactionScheme content, reactionStepList, reactiveCentre atomRefs.
130
+ | simpleUnit | 3 | simpleUnit root, unit power, unit symbol.
131
+ |===
132
+
133
+ Add a custom convention at runtime:
134
+
135
+ [source,ruby]
136
+ ----
137
+ module MyConvention
138
+ extend Chemicalml::Convention::Base
139
+
140
+ def self.qname; "convention:my"; end
141
+ def self.namespace_uri; "http://example.com/my"; end
142
+
143
+ register MyConstraintClass
144
+ end
59
145
 
60
- The library is designed as a sibling model layer to other chemistry
61
- tooling. AsciiChem (https://www.asciichem.org) uses ChemML for CML
62
- round-trip support via a translator adapter.
146
+ Chemicalml::Convention::Registry.register_custom(MyConvention)
147
+ ----
148
+
149
+ == Dictionaries
150
+
151
+ Eight built-in YAML dictionaries ship under `data/dictionaries/`:
152
+
153
+ [cols="1,1"]
154
+ |===
155
+ | Dictionary | Prefix
156
+
157
+ | cml | cml
158
+ | cml_name | cmlName
159
+ | cml_formula | cmlFormula
160
+ | cif | cif
161
+ | compchem | compchem
162
+ | unit_si | si
163
+ | unit_non_si | nonsi
164
+ | unit_type | unitType
165
+ |===
166
+
167
+ Load via:
63
168
 
64
- Both Schema 2.4 and Schema 3 are supported via
65
- `Chemicalml::Cml::Schema3` and `Chemicalml::Cml::Schema24` namespaces
66
- (see `lib/chemicalml/schema.rb`).
169
+ [source,ruby]
170
+ ----
171
+ dict = Chemicalml::Dictionary.load(:cml)
172
+ entry = Chemicalml::Dictionary::Registry.lookup("cml:bp")
173
+ entry.term # => "Boiling Point"
174
+ entry.definition # => "The temperature at which a substance boils..."
175
+ ----
67
176
 
68
- The CML conventions (molecular, compchem, dictionary, unit-dictionary,
69
- unitType-dictionary) are implemented as registered constraint sets in
70
- `Chemicalml::Convention`. Built-in dictionaries ship as YAML under
71
- `data/dictionaries/`.
177
+ == Design
178
+
179
+ Each CML element is a `Lutaml::Model::Serializable` subclass with
180
+ declared attributes and parallel `xml do ... end` and
181
+ `key_value do ... end` mapping blocks. Serialisation is fully
182
+ framework-backed — no hand-rolled XML.
183
+
184
+ * `Cml::Base::*` modules declare shared attributes (mixed into both
185
+ Schema3 and Schema24 wire classes).
186
+ * `Cml::Role::*` modules are type-markers used by the convention
187
+ constraint walker for `is_a?` dispatch.
188
+ * `Cml::Elements::ALL` is the single source of truth for the CML
189
+ element table.
190
+ * `Cml::Enums` and `Cml::Patterns` mirror XSD simpleTypes.
191
+ * `Cml::CommonChildren` provides the universal `metadataList`,
192
+ `label`, `name`, `description` child set as a DRY mixin.
193
+
194
+ == Reference docs
195
+
196
+ `reference-docs/` holds archival source material from xml-cml.org:
197
+ the XSDs (`schemas/schema3/schema.xsd`, `schemas/schema24/schema.xsd`),
198
+ the convention specifications, and the CMLLite paper. These are
199
+ **source material, not derived output** — never delete or edit them.
72
200
 
73
201
  == Acknowledgements
74
202
 
@@ -0,0 +1,46 @@
1
+ # 36 — Molecular convention: full upstream constraint coverage
2
+
3
+ ## Why
4
+
5
+ `reference-docs/conventions/molecular.md` mandates more rules than the
6
+ gem currently enforces. The walker framework already exists; this is
7
+ purely additive constraint registration.
8
+
9
+ ## Current constraints (13)
10
+
11
+ AtomArrayMustContainAtoms, AtomIdsUniqueWithinMolecule,
12
+ BondMustReferenceAtomsInSameMolecule, AtomMustHaveId,
13
+ AtomMustHaveElementType, BondMustHaveAtomRefs2, BondMustHaveOrder,
14
+ MoleculeMustHaveId, AtomCoordinatesMustBePaired,
15
+ PropertyMustHaveDictRef, ScalarMustHaveDataType,
16
+ BondOrderShouldNotBeNumeric, AtomIdMustMatchPattern.
17
+
18
+ ## Missing rules per upstream spec
19
+
20
+ - `count` attribute MUST NOT appear on top-level molecules (only on
21
+ children of another molecule).
22
+ - `atomArray` — at most one per molecule; mutually exclusive with
23
+ child `<molecule>` elements.
24
+ - `bondArray` — at most one per molecule; mutually exclusive with
25
+ child `<molecule>` elements.
26
+ - `bondStereo` value `W` or `H` MUST have `atomRefs2`, MUST NOT have
27
+ `atomRefs4`.
28
+ - `bondStereo` value `C` or `T` MUST have `atomRefs4`, MUST NOT have
29
+ `atomRefs2`.
30
+ - `bondStereo` value `other` MUST have `dictRef`.
31
+ - `bond` `id` MUST be unique within the eldest containing molecule.
32
+ - `bond` with `order="other"` MUST have `dictRef`.
33
+ - `atomArray` MUST be a child of `molecule` or `formula`.
34
+ - `bondArray` MUST be a child of `molecule`.
35
+
36
+ ## Implementation
37
+
38
+ Each rule → one Constraint subclass + `register` call in
39
+ `lib/chemicalml/convention/molecular.rb`. New files autoloaded from
40
+ `lib/chemicalml/convention/molecular/constraints.rb`.
41
+
42
+ ## Acceptance
43
+
44
+ - 10 new constraint classes registered.
45
+ - Per-class spec under `spec/chemicalml/convention/molecular/`.
46
+ - Full suite still green.
@@ -0,0 +1,55 @@
1
+ # 37 — CompChem convention: full upstream constraint coverage
2
+
3
+ ## Why
4
+
5
+ `reference-docs/conventions/compchem.md` defines a deep module
6
+ structure (jobList → job → initialization/calculation/finalization/
7
+ environment) and value-container rules. The gem currently ships only
8
+ 2 of the ~19 rules.
9
+
10
+ ## Current constraints (2)
11
+
12
+ CompchemModuleMustContainJobList, JobMustContainInitialization.
13
+
14
+ ## Missing structural rules
15
+
16
+ - `jobList` module MUST have `id` unique within compchem module.
17
+ - `job` module MUST have `id` unique within compchem module.
18
+ - `job` module MUST contain at most one `finalization`.
19
+ - `job` module MUST contain at most one `environment`.
20
+ - If a `calculation` is present in a `job`, a `finalization` MUST
21
+ also be present (co-constraint).
22
+ - `initialization` module MUST NOT contain `property`/`propertyList`.
23
+ - `initialization` module MUST NOT contain more than one `molecule`.
24
+ - `initialization` module MUST NOT contain more than one
25
+ `parameterList`.
26
+ - `finalization` module MUST NOT contain `parameter`/`parameterList`.
27
+ - `finalization` module MUST NOT contain more than one `molecule`.
28
+ - `finalization` module MUST NOT contain more than one `propertyList`.
29
+ - `environment` module MUST NOT contain more than one `propertyList`.
30
+ - `environment` module MUST NOT contain `parameter`/`parameterList`
31
+ directly.
32
+
33
+ ## Missing value-container rules
34
+
35
+ - `scalar` with `dataType` `xsd:integer` or `xsd:double` MUST have
36
+ `units`.
37
+ - `scalar` with `dataType` `xsd:string` MUST NOT have `units`.
38
+ - `array` MUST have `size` ≥ 1; `dataType` MUST be integer or double;
39
+ MUST have `units`.
40
+ - `matrix` MUST have `rows` ≥ 1 and `columns` ≥ 1; `dataType` MUST
41
+ be integer or double; MUST have `units`.
42
+
43
+ ## Implementation
44
+
45
+ Use `DocumentConstraint` for cross-node rules (calculation→finalization
46
+ co-constraint), `NodeConstraint` for single-node rules.
47
+
48
+ CompChem modules are `<module dictRef="compchem:*">`. Match by
49
+ `node.is_a?(Chemicalml::Cml::Role::Module)` then check `dict_ref`.
50
+
51
+ ## Acceptance
52
+
53
+ - 17 new constraint classes registered.
54
+ - Per-class spec under `spec/chemicalml/convention/compchem/`.
55
+ - Full suite still green.
@@ -0,0 +1,34 @@
1
+ # 38 — Dictionary convention: full upstream constraint coverage
2
+
3
+ ## Why
4
+
5
+ `reference-docs/conventions/dictionary.md` requires namespace,
6
+ definition children, id patterns, and unitType/units co-constraints.
7
+ Currently 2 of 7 rules enforced.
8
+
9
+ ## Current constraints (2)
10
+
11
+ EntryMustHaveIdAndTerm, EntryIdsUniqueWithinDictionary.
12
+
13
+ ## Missing rules
14
+
15
+ - `dictionary` MUST have `namespace` attribute.
16
+ - `dictionary` `namespace` SHOULD end with `/` or `#` (warning).
17
+ - `entry` MUST contain a single `definition` child.
18
+ - `entry` `id` MUST match `[A-Za-z][A-Za-z0-9._-]*`.
19
+ - `entry` MUST have `unitType` attribute.
20
+ - If `unitType` is `unknown`, `units` MUST NOT be present.
21
+ - If `unitType` is `none`, `units` MUST be present and point to
22
+ `http://www.xml-cml.org/unit/si#none`.
23
+
24
+ ## Implementation
25
+
26
+ New files under
27
+ `lib/chemicalml/convention/dictionary/constraints/`, registered in
28
+ `lib/chemicalml/convention/dictionary.rb`.
29
+
30
+ ## Acceptance
31
+
32
+ - 7 new constraint classes registered.
33
+ - Per-class spec under `spec/chemicalml/convention/dictionary/`.
34
+ - Full suite still green.
@@ -0,0 +1,33 @@
1
+ # 39 — Unit-dictionary convention: full upstream constraint coverage
2
+
3
+ ## Why
4
+
5
+ `reference-docs/conventions/unit-dictionary.md` requires 8 attributes
6
+ on every `<unit>`. The gem currently ships 1 rule.
7
+
8
+ ## Current constraints (1)
9
+
10
+ UnitMustHaveSymbolAndUnitType.
11
+
12
+ ## Missing rules
13
+
14
+ - `unitList` MUST have `namespace` attribute.
15
+ - `unit` MUST have `id` (unique within unitList — already implied by
16
+ pattern rules but enforce presence).
17
+ - `unit` MUST have `title` attribute.
18
+ - `unit` MUST have `parentSI` attribute.
19
+ - `unit` MUST have at least one of `multiplierToSI` or `constantToSI`.
20
+ - `unit` MUST contain a single `definition` child.
21
+ - `unitList` MUST contain at least one `unit` child.
22
+
23
+ ## Implementation
24
+
25
+ NodeConstraint subclasses. Register in
26
+ `lib/chemicalml/convention/unit_dictionary.rb`.
27
+
28
+ ## Acceptance
29
+
30
+ - 7 new constraint classes registered (some may consolidate with
31
+ existing UnitMustHaveSymbolAndUnitType).
32
+ - Per-class spec under `spec/chemicalml/convention/unit_dictionary/`.
33
+ - Full suite still green.