chemicalml 0.2.1 → 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 (347) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +23 -0
  3. data/CLAUDE.md +49 -1
  4. data/README.adoc +154 -26
  5. data/TODO.align/61-parallel-array-attrs.md +54 -0
  6. data/TODO.align/62-per-element-attribute-gaps.md +49 -0
  7. data/TODO.align/63-universal-children-mixin.md +42 -0
  8. data/TODO.align/64-per-element-child-gaps.md +81 -0
  9. data/TODO.align/65-simple-unit-convention.md +40 -0
  10. data/TODO.align/66-schema24-legacy-children.md +32 -0
  11. data/TODO.align/67-final-verification.md +32 -0
  12. data/TODO.align/68-detection-coverage.md +33 -0
  13. data/TODO.align/69-json-yaml-roundtrip.md +27 -0
  14. data/TODO.align/70-iterative-walker.md +40 -0
  15. data/TODO.align/71-violation-value.md +25 -0
  16. data/TODO.align/72-close-child-gaps.md +66 -0
  17. data/TODO.align/73-schema24-parse-limitation.md +64 -0
  18. data/TODO.align/74-final-verification-round18.md +15 -0
  19. data/TODO.align/75-key-value-mappings.md +43 -0
  20. data/TODO.align/76-schema24-workaround.md +57 -0
  21. data/TODO.align/77-convention-coverage-matrix.md +31 -0
  22. data/TODO.align/78-fixture-audit.md +23 -0
  23. data/TODO.align/79-final-verification-round19.md +15 -0
  24. data/TODO.align/80-xsd-enum-constants.md +35 -0
  25. data/TODO.align/81-enum-validation-constraints.md +29 -0
  26. data/TODO.align/82-reference-resolver.md +33 -0
  27. data/TODO.align/83-toml-roundtrip.md +20 -0
  28. data/TODO.align/84-final-verification-round21.md +16 -0
  29. data/TODO.align/85-bond-distinct-atoms.md +25 -0
  30. data/TODO.align/86-references-should-resolve.md +20 -0
  31. data/TODO.align/87-cli-utility.md +26 -0
  32. data/TODO.align/88-diverse-fixtures.md +26 -0
  33. data/TODO.align/89-final-verification-round22.md +15 -0
  34. data/TODO.align/90-bond-stereo-completeness.md +31 -0
  35. data/TODO.align/91-atomparity-distinct.md +21 -0
  36. data/TODO.align/92-unit-dictionary-completeness.md +28 -0
  37. data/TODO.align/93-yard-docs.md +23 -0
  38. data/TODO.align/94-final-verification-round23.md +15 -0
  39. data/TODO.align/README.round17.md +28 -0
  40. data/TODO.align/README.round18.md +37 -0
  41. data/TODO.align/README.round19.md +40 -0
  42. data/TODO.align/README.round20.md +49 -0
  43. data/TODO.align/README.round21.md +35 -0
  44. data/TODO.align/README.round22.md +48 -0
  45. data/TODO.align/README.round23.md +46 -0
  46. data/TODO.align/README.round24.md +53 -0
  47. data/TODO.align/README.round25.md +66 -0
  48. data/TODO.align/README.round26.md +55 -0
  49. data/TODO.align/README.round27.md +51 -0
  50. data/TODO.align/README.round28.md +61 -0
  51. data/TODO.align/README.round29.md +64 -0
  52. data/TODO.align/README.round30.md +50 -0
  53. data/TODO.align/README.round31.md +54 -0
  54. data/TODO.align/README.round32.md +67 -0
  55. data/TODO.align/README.round33.md +62 -0
  56. data/TODO.align/README.round34.md +70 -0
  57. data/docs/constraints.md +161 -0
  58. data/docs/generate_constraint_docs.rb +44 -0
  59. data/exe/chemicalml +6 -0
  60. data/lib/chemicalml/cli/base.rb +68 -0
  61. data/lib/chemicalml/cli/command.rb +43 -0
  62. data/lib/chemicalml/cli/constraints_command.rb +27 -0
  63. data/lib/chemicalml/cli/conventions_command.rb +14 -0
  64. data/lib/chemicalml/cli/dictionaries_command.rb +14 -0
  65. data/lib/chemicalml/cli/elements_command.rb +17 -0
  66. data/lib/chemicalml/cli/enums_command.rb +17 -0
  67. data/lib/chemicalml/cli/info_command.rb +77 -0
  68. data/lib/chemicalml/cli/inspect_command.rb +39 -0
  69. data/lib/chemicalml/cli/validate_command.rb +54 -0
  70. data/lib/chemicalml/cli.rb +43 -0
  71. data/lib/chemicalml/cml/base/abundance.rb +18 -8
  72. data/lib/chemicalml/cml/base/action.rb +34 -16
  73. data/lib/chemicalml/cml/base/action_list.rb +35 -17
  74. data/lib/chemicalml/cml/base/alternative.rb +13 -4
  75. data/lib/chemicalml/cml/base/amount.rb +14 -6
  76. data/lib/chemicalml/cml/base/angle.rb +28 -13
  77. data/lib/chemicalml/cml/base/annotation.rb +14 -6
  78. data/lib/chemicalml/cml/base/any_cml.rb +12 -5
  79. data/lib/chemicalml/cml/base/appinfo.rb +14 -6
  80. data/lib/chemicalml/cml/base/arg.rb +47 -13
  81. data/lib/chemicalml/cml/base/array.rb +40 -19
  82. data/lib/chemicalml/cml/base/array_list.rb +14 -6
  83. data/lib/chemicalml/cml/base/atom.rb +84 -28
  84. data/lib/chemicalml/cml/base/atom_array.rb +60 -23
  85. data/lib/chemicalml/cml/base/atom_parity.rb +14 -6
  86. data/lib/chemicalml/cml/base/atom_set.rb +14 -6
  87. data/lib/chemicalml/cml/base/atom_type.rb +25 -9
  88. data/lib/chemicalml/cml/base/atom_type_list.rb +22 -8
  89. data/lib/chemicalml/cml/base/atomic_basis_function.rb +33 -13
  90. data/lib/chemicalml/cml/base/band.rb +21 -8
  91. data/lib/chemicalml/cml/base/band_list.rb +15 -7
  92. data/lib/chemicalml/cml/base/basis_set.rb +24 -9
  93. data/lib/chemicalml/cml/base/bond.rb +36 -10
  94. data/lib/chemicalml/cml/base/bond_array.rb +34 -11
  95. data/lib/chemicalml/cml/base/bond_set.rb +14 -6
  96. data/lib/chemicalml/cml/base/bond_stereo.rb +20 -9
  97. data/lib/chemicalml/cml/base/bond_type.rb +23 -8
  98. data/lib/chemicalml/cml/base/bond_type_list.rb +22 -8
  99. data/lib/chemicalml/cml/base/cell_parameter.rb +28 -10
  100. data/lib/chemicalml/cml/base/cml_module.rb +36 -11
  101. data/lib/chemicalml/cml/base/common_children.rb +35 -0
  102. data/lib/chemicalml/cml/base/complex_object.rb +12 -5
  103. data/lib/chemicalml/cml/base/condition_list.rb +26 -10
  104. data/lib/chemicalml/cml/base/crystal.rb +26 -10
  105. data/lib/chemicalml/cml/base/definition.rb +12 -5
  106. data/lib/chemicalml/cml/base/description.rb +14 -6
  107. data/lib/chemicalml/cml/base/dictionary.rb +27 -10
  108. data/lib/chemicalml/cml/base/dictionary_entry.rb +67 -8
  109. data/lib/chemicalml/cml/base/dimension.rb +26 -12
  110. data/lib/chemicalml/cml/base/document.rb +18 -8
  111. data/lib/chemicalml/cml/base/documentation.rb +12 -5
  112. data/lib/chemicalml/cml/base/eigen.rb +32 -8
  113. data/lib/chemicalml/cml/base/electron.rb +26 -12
  114. data/lib/chemicalml/cml/base/enumeration.rb +16 -7
  115. data/lib/chemicalml/cml/base/expression.rb +21 -6
  116. data/lib/chemicalml/cml/base/float.rb +22 -10
  117. data/lib/chemicalml/cml/base/float_array.rb +24 -11
  118. data/lib/chemicalml/cml/base/formula.rb +31 -10
  119. data/lib/chemicalml/cml/base/fragment.rb +30 -12
  120. data/lib/chemicalml/cml/base/fragment_list.rb +24 -9
  121. data/lib/chemicalml/cml/base/gradient.rb +19 -6
  122. data/lib/chemicalml/cml/base/identifier.rb +18 -8
  123. data/lib/chemicalml/cml/base/integer.rb +22 -10
  124. data/lib/chemicalml/cml/base/integer_array.rb +24 -11
  125. data/lib/chemicalml/cml/base/isotope.rb +28 -9
  126. data/lib/chemicalml/cml/base/isotope_list.rb +17 -8
  127. data/lib/chemicalml/cml/base/join.rb +25 -9
  128. data/lib/chemicalml/cml/base/kpoint.rb +16 -7
  129. data/lib/chemicalml/cml/base/kpoint_list.rb +15 -7
  130. data/lib/chemicalml/cml/base/label.rb +14 -6
  131. data/lib/chemicalml/cml/base/lattice.rb +35 -10
  132. data/lib/chemicalml/cml/base/lattice_vector.rb +16 -7
  133. data/lib/chemicalml/cml/base/length.rb +26 -12
  134. data/lib/chemicalml/cml/base/line3.rb +18 -8
  135. data/lib/chemicalml/cml/base/link.rb +41 -20
  136. data/lib/chemicalml/cml/base/list.rb +22 -10
  137. data/lib/chemicalml/cml/base/map.rb +32 -11
  138. data/lib/chemicalml/cml/base/matrix.rb +32 -15
  139. data/lib/chemicalml/cml/base/mechanism.rb +20 -7
  140. data/lib/chemicalml/cml/base/mechanism_component.rb +14 -6
  141. data/lib/chemicalml/cml/base/metadata.rb +18 -8
  142. data/lib/chemicalml/cml/base/metadata_list.rb +27 -8
  143. data/lib/chemicalml/cml/base/molecule.rb +90 -26
  144. data/lib/chemicalml/cml/base/molecule_list.rb +26 -8
  145. data/lib/chemicalml/cml/base/name.rb +10 -4
  146. data/lib/chemicalml/cml/base/object.rb +18 -8
  147. data/lib/chemicalml/cml/base/observation.rb +16 -7
  148. data/lib/chemicalml/cml/base/operator.rb +14 -6
  149. data/lib/chemicalml/cml/base/parameter.rb +33 -13
  150. data/lib/chemicalml/cml/base/parameter_list.rb +27 -8
  151. data/lib/chemicalml/cml/base/particle.rb +20 -9
  152. data/lib/chemicalml/cml/base/peak.rb +71 -26
  153. data/lib/chemicalml/cml/base/peak_group.rb +62 -26
  154. data/lib/chemicalml/cml/base/peak_list.rb +24 -9
  155. data/lib/chemicalml/cml/base/peak_structure.rb +40 -13
  156. data/lib/chemicalml/cml/base/plane3.rb +14 -6
  157. data/lib/chemicalml/cml/base/point3.rb +14 -6
  158. data/lib/chemicalml/cml/base/potential.rb +17 -6
  159. data/lib/chemicalml/cml/base/potential_form.rb +30 -7
  160. data/lib/chemicalml/cml/base/potential_list.rb +15 -7
  161. data/lib/chemicalml/cml/base/product.rb +45 -10
  162. data/lib/chemicalml/cml/base/product_list.rb +29 -9
  163. data/lib/chemicalml/cml/base/property.rb +29 -11
  164. data/lib/chemicalml/cml/base/property_list.rb +23 -8
  165. data/lib/chemicalml/cml/base/reactant.rb +45 -10
  166. data/lib/chemicalml/cml/base/reactant_list.rb +29 -9
  167. data/lib/chemicalml/cml/base/reaction.rb +74 -20
  168. data/lib/chemicalml/cml/base/reaction_list.rb +23 -8
  169. data/lib/chemicalml/cml/base/reaction_scheme.rb +43 -13
  170. data/lib/chemicalml/cml/base/reaction_step.rb +30 -12
  171. data/lib/chemicalml/cml/base/reaction_step_list.rb +30 -11
  172. data/lib/chemicalml/cml/base/reactive_centre.rb +31 -6
  173. data/lib/chemicalml/cml/base/region.rb +20 -9
  174. data/lib/chemicalml/cml/base/related_entry.rb +11 -3
  175. data/lib/chemicalml/cml/base/sample.rb +30 -7
  176. data/lib/chemicalml/cml/base/scalar.rb +32 -15
  177. data/lib/chemicalml/cml/base/spectator.rb +25 -6
  178. data/lib/chemicalml/cml/base/spectator_list.rb +20 -7
  179. data/lib/chemicalml/cml/base/spectrum.rb +57 -18
  180. data/lib/chemicalml/cml/base/spectrum_data.rb +19 -9
  181. data/lib/chemicalml/cml/base/spectrum_list.rb +28 -9
  182. data/lib/chemicalml/cml/base/sphere3.rb +14 -6
  183. data/lib/chemicalml/cml/base/stmml.rb +12 -5
  184. data/lib/chemicalml/cml/base/string.rb +14 -6
  185. data/lib/chemicalml/cml/base/string_array.rb +22 -10
  186. data/lib/chemicalml/cml/base/substance.rb +36 -14
  187. data/lib/chemicalml/cml/base/substance_list.rb +29 -10
  188. data/lib/chemicalml/cml/base/symmetry.rb +31 -9
  189. data/lib/chemicalml/cml/base/system.rb +21 -10
  190. data/lib/chemicalml/cml/base/table.rb +38 -13
  191. data/lib/chemicalml/cml/base/table_cell.rb +12 -5
  192. data/lib/chemicalml/cml/base/table_content.rb +19 -9
  193. data/lib/chemicalml/cml/base/table_header.rb +15 -7
  194. data/lib/chemicalml/cml/base/table_header_cell.rb +22 -10
  195. data/lib/chemicalml/cml/base/table_row.rb +15 -7
  196. data/lib/chemicalml/cml/base/table_row_list.rb +15 -7
  197. data/lib/chemicalml/cml/base/tcell.rb +16 -7
  198. data/lib/chemicalml/cml/base/torsion.rb +28 -13
  199. data/lib/chemicalml/cml/base/transform3.rb +12 -5
  200. data/lib/chemicalml/cml/base/transition_state.rb +23 -5
  201. data/lib/chemicalml/cml/base/trow.rb +22 -6
  202. data/lib/chemicalml/cml/base/unit.rb +39 -16
  203. data/lib/chemicalml/cml/base/unit_list.rb +34 -12
  204. data/lib/chemicalml/cml/base/unit_type.rb +27 -10
  205. data/lib/chemicalml/cml/base/unit_type_list.rb +29 -11
  206. data/lib/chemicalml/cml/base/vector3.rb +14 -6
  207. data/lib/chemicalml/cml/base/xaxis.rb +30 -10
  208. data/lib/chemicalml/cml/base/yaxis.rb +30 -10
  209. data/lib/chemicalml/cml/base/z_matrix.rb +26 -5
  210. data/lib/chemicalml/cml/base.rb +139 -138
  211. data/lib/chemicalml/cml/canonical_comparison.rb +54 -0
  212. data/lib/chemicalml/cml/elements.rb +145 -136
  213. data/lib/chemicalml/cml/enums.rb +55 -0
  214. data/lib/chemicalml/cml/namespace.rb +2 -3
  215. data/lib/chemicalml/cml/patterns.rb +33 -0
  216. data/lib/chemicalml/cml/reference_resolver.rb +137 -0
  217. data/lib/chemicalml/cml/role.rb +138 -138
  218. data/lib/chemicalml/cml/schema24/configuration.rb +11 -1
  219. data/lib/chemicalml/cml/schema24.rb +139 -139
  220. data/lib/chemicalml/cml/schema3.rb +123 -123
  221. data/lib/chemicalml/cml/visitable.rb +77 -3
  222. data/lib/chemicalml/cml/wire_class_registry.rb +3 -3
  223. data/lib/chemicalml/cml.rb +163 -129
  224. data/lib/chemicalml/context_configuration.rb +4 -2
  225. data/lib/chemicalml/convention/cascade/constraints/reaction_scheme_must_have_content.rb +33 -0
  226. data/lib/chemicalml/convention/cascade/constraints/reaction_step_list_must_contain_steps.rb +31 -0
  227. data/lib/chemicalml/convention/cascade/constraints/reaction_step_must_have_reaction_or_lists.rb +33 -0
  228. data/lib/chemicalml/convention/cascade/constraints/reactive_centre_atom_refs_should_be_present.rb +34 -0
  229. data/lib/chemicalml/convention/cascade/constraints.rb +18 -0
  230. data/lib/chemicalml/convention/cascade.rb +43 -0
  231. data/lib/chemicalml/convention/compchem/constraints/array_rules.rb +1 -1
  232. data/lib/chemicalml/convention/compchem/constraints/calculation_requires_finalization.rb +1 -1
  233. data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +2 -1
  234. data/lib/chemicalml/convention/compchem/constraints/environment_at_most_one_property_list.rb +1 -1
  235. data/lib/chemicalml/convention/compchem/constraints/environment_must_not_contain_parameter.rb +1 -1
  236. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_molecule.rb +1 -1
  237. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_property_list.rb +1 -1
  238. data/lib/chemicalml/convention/compchem/constraints/finalization_must_have_content.rb +44 -0
  239. data/lib/chemicalml/convention/compchem/constraints/finalization_must_not_contain_parameter.rb +1 -1
  240. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_molecule.rb +1 -1
  241. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_parameter_list.rb +1 -1
  242. data/lib/chemicalml/convention/compchem/constraints/initialization_must_have_content.rb +54 -0
  243. data/lib/chemicalml/convention/compchem/constraints/initialization_must_not_contain_property.rb +1 -1
  244. data/lib/chemicalml/convention/compchem/constraints/job_list_module_must_have_id.rb +1 -0
  245. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_environment.rb +1 -1
  246. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_finalization.rb +1 -1
  247. data/lib/chemicalml/convention/compchem/constraints/job_module_must_have_id.rb +1 -0
  248. data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +1 -0
  249. data/lib/chemicalml/convention/compchem/constraints/matrix_rules.rb +1 -1
  250. data/lib/chemicalml/convention/compchem/constraints/scalar_units.rb +1 -1
  251. data/lib/chemicalml/convention/compchem/constraints.rb +4 -0
  252. data/lib/chemicalml/convention/compchem.rb +4 -2
  253. data/lib/chemicalml/convention/constraint.rb +40 -9
  254. data/lib/chemicalml/convention/coordinator.rb +4 -3
  255. data/lib/chemicalml/convention/detection.rb +10 -4
  256. data/lib/chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace.rb +1 -0
  257. data/lib/chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash.rb +1 -1
  258. data/lib/chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern.rb +1 -1
  259. data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +1 -1
  260. data/lib/chemicalml/convention/dictionary/constraints/entry_must_contain_definition.rb +1 -0
  261. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +1 -1
  262. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_unit_type.rb +1 -0
  263. data/lib/chemicalml/convention/dictionary/constraints/entry_units_co_constraints.rb +1 -1
  264. data/lib/chemicalml/convention/dictionary/constraints.rb +8 -8
  265. data/lib/chemicalml/convention/dictionary.rb +2 -2
  266. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula.rb +1 -0
  267. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +15 -6
  268. data/lib/chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired.rb +1 -1
  269. data/lib/chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table.rb +39 -0
  270. data/lib/chemicalml/convention/molecular/constraints/atom_id_must_match_pattern.rb +3 -2
  271. data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +1 -0
  272. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_element_type.rb +1 -1
  273. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_id.rb +2 -1
  274. data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb +44 -0
  275. data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb +65 -0
  276. data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb +1 -0
  277. data/lib/chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct.rb +44 -0
  278. data/lib/chemicalml/convention/molecular/constraints/bond_id_should_match_pattern.rb +38 -0
  279. data/lib/chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule.rb +1 -0
  280. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2.rb +1 -1
  281. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_order.rb +2 -1
  282. data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +1 -0
  283. data/lib/chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref.rb +1 -0
  284. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum.rb +39 -0
  285. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric.rb +1 -1
  286. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb +43 -0
  287. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb +1 -1
  288. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb +1 -0
  289. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum.rb +38 -0
  290. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2.rb +1 -1
  291. data/lib/chemicalml/convention/molecular/constraints/dict_ref_should_resolve.rb +59 -0
  292. data/lib/chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children.rb +1 -0
  293. data/lib/chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children.rb +1 -0
  294. data/lib/chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum.rb +38 -0
  295. data/lib/chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level.rb +1 -0
  296. data/lib/chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern.rb +38 -0
  297. data/lib/chemicalml/convention/molecular/constraints/molecule_must_have_id.rb +1 -1
  298. data/lib/chemicalml/convention/molecular/constraints/property_must_have_dict_ref.rb +1 -1
  299. data/lib/chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary.rb +58 -0
  300. data/lib/chemicalml/convention/molecular/constraints/references_should_resolve.rb +37 -0
  301. data/lib/chemicalml/convention/molecular/constraints/scalar_must_have_data_type.rb +1 -1
  302. data/lib/chemicalml/convention/molecular/constraints.rb +49 -23
  303. data/lib/chemicalml/convention/molecular.rb +15 -2
  304. data/lib/chemicalml/convention/registry.rb +79 -11
  305. data/lib/chemicalml/convention/simple_unit/constraints/root_must_be_unit_list.rb +24 -0
  306. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_power.rb +32 -0
  307. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_symbol.rb +31 -0
  308. data/lib/chemicalml/convention/simple_unit/constraints.rb +16 -0
  309. data/lib/chemicalml/convention/simple_unit.rb +37 -0
  310. data/lib/chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks.rb +31 -0
  311. data/lib/chemicalml/convention/spectroscopy/constraints/peak_should_have_values.rb +34 -0
  312. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content.rb +31 -0
  313. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention.rb +32 -0
  314. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format.rb +31 -0
  315. data/lib/chemicalml/convention/spectroscopy/constraints.rb +20 -0
  316. data/lib/chemicalml/convention/spectroscopy.rb +41 -0
  317. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit.rb +1 -0
  318. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace.rb +1 -1
  319. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition.rb +1 -0
  320. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_id.rb +1 -0
  321. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si.rb +34 -0
  322. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si.rb +32 -0
  323. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +1 -1
  324. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_title.rb +32 -0
  325. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve.rb +45 -0
  326. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve.rb +46 -0
  327. data/lib/chemicalml/convention/unit_dictionary/constraints.rb +10 -0
  328. data/lib/chemicalml/convention/unit_dictionary.rb +7 -2
  329. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern.rb +1 -1
  330. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type.rb +1 -0
  331. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace.rb +1 -1
  332. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition.rb +1 -0
  333. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +1 -1
  334. data/lib/chemicalml/convention/unit_type_dictionary.rb +2 -2
  335. data/lib/chemicalml/convention/validation_report.rb +30 -3
  336. data/lib/chemicalml/convention/violation.rb +9 -3
  337. data/lib/chemicalml/convention.rb +41 -14
  338. data/lib/chemicalml/dictionary.rb +13 -9
  339. data/lib/chemicalml/logger.rb +47 -0
  340. data/lib/chemicalml/schema/definition.rb +2 -2
  341. data/lib/chemicalml/schema.rb +7 -7
  342. data/lib/chemicalml/version.rb +1 -1
  343. data/lib/chemicalml/versioned_parser.rb +10 -10
  344. data/lib/chemicalml.rb +78 -15
  345. data/lib/tasks/reference_fetch.rake +30 -31
  346. data/lib/tasks/scrape_fixtures.rake +23 -24
  347. metadata +114 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5aff6a0c43deeff73ff9684286df7c1a7ef97d61fa919415a381e70de217995c
4
- data.tar.gz: 52237d4db8715d8244b88c124cf3bdc1ad43102b4468d8217099ebdec6b89f1b
3
+ metadata.gz: ed7130181d6d55cbadb29d2630668ab4f16746f10dc20db9c9d1d0ff066586c6
4
+ data.tar.gz: ac78d79e906862aa42639b2d237ec66661a8c3061e4b25c04535f8941520b3d3
5
5
  SHA512:
6
- metadata.gz: b9f636c62b495c14f5ab10df0c8fd30e49aa23587731587f2fc9f4d87837376da38db8814df48482e1725751090518722ff3e6ca43ea5197fefb53ab5055183a
7
- data.tar.gz: c7b42562ce98060bd3ca135d137be215f25a2ae71e2fc2693b70ab7e1030f8c6868744dea7aa9d60d078ba6adc11fc28a7c8d63de4f5f69c0a9b16ff8fe6f65f
6
+ metadata.gz: 5909cc1b258048f0c5aefba5844fff9136f2db4713d5cd5b80a22c07045e60a5ee5ead34680a841a9044039824c76cd4913d48d8f3410f86bf99b043f5739ba4
7
+ data.tar.gz: ff4f54de5ce734ae649554f593cfc1affa766b7e541fef394704bcd1b4d192dbae3e788241ced6a4dc6306bacf62b409fad2fdafe6861617497d7ff2ced16dee
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`
@@ -126,17 +129,35 @@ Schema 3's unified `scalar`/`array`/`table` machinery.
126
129
  The framework handles (de)serialization; **never** add `def to_xml` /
127
130
  `def from_xml` / `def to_h` to these classes.
128
131
 
132
+ ### JSON / YAML wire names
133
+
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.
140
+
129
141
  ### Schema versions
130
142
 
131
143
  `Chemicalml::Schema::Registry` is the registry for schema versions
132
144
  (`schema24`, `schema3`). The schema XSDs are **archival source** —
133
145
  they must never be deleted, edited, or regenerated from code.
134
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
+
135
155
  ### Conventions
136
156
 
137
157
  `Chemicalml::Convention` is a registry of named constraint sets
138
158
  (`molecular`, `compchem`, `dictionary`, `unit-dictionary`,
139
- `unitType-dictionary`). Each convention owns:
159
+ `unitType-dictionary`, `spectroscopy`, `cascade`, `simpleUnit`).
160
+ Each convention owns:
140
161
 
141
162
  - a namespace URI (e.g. `http://www.xml-cml.org/convention/molecular`)
142
163
  - a set of constraint classes registered against it
@@ -148,6 +169,33 @@ renderer code (Open/Closed Principle).
148
169
  The constraint walker uses `Cml::Visitable#wire_children` to traverse
149
170
  the tree — no `respond_to?` duck typing.
150
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
+
151
199
  ### Dictionaries
152
200
 
153
201
  `Chemicalml::Dictionary` is the model; entries have `id`, `term`,
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,54 @@
1
+ # 61 — Parallel-array attrs for atomArray/bondArray
2
+
3
+ ## Why
4
+
5
+ CML supports two equivalent serialisations of atom/bond tables:
6
+
7
+ ```xml
8
+ <!-- child form (we already model this) -->
9
+ <atomArray><atom id="a1" elementType="C" x3="1.0" .../><atom .../></atomArray>
10
+
11
+ <!-- parallel-array form (we do NOT model this) -->
12
+ <atomArray atomID="a1 a2" elementType="C O" x3="1.0 2.0" .../>
13
+ ```
14
+
15
+ The parallel-array form is heavily used in real CML documents and is
16
+ the only form some legacy tools emit. Round-trip currently drops these
17
+ attributes silently.
18
+
19
+ ## Source of truth
20
+
21
+ `reference-docs/schemas/schema3/schema.xsd` — `atomArrayType` complexType
22
+ declares these 14 parallel-array attributes:
23
+
24
+ - `atomID` (idArrayType)
25
+ - `count`, `countArrayType`
26
+ - `elementType`, `elementTypeArrayType`
27
+ - `formalCharge`, `formalChargeArrayType`
28
+ - `hydrogenCount`, `hydrogenCountArrayType`
29
+ - `occupancy`, `occupancyArrayType`
30
+ - `x2`, `y2` (coordinateComponentArrayType)
31
+ - `x3`, `y3`, `z3` (coordinateComponentArrayType)
32
+ - `xFract`, `yFract`, `zFract` (coordinateComponentArrayType)
33
+
34
+ `bondArrayType` declares 4:
35
+
36
+ - `atomRef1`, `atomRef2` (atomRefArrayType)
37
+ - `bondID` (idArrayType)
38
+ - `order` (orderArrayType)
39
+
40
+ ## Work
41
+
42
+ 1. `lib/chemicalml/cml/base/atom_array.rb` — declare the 14 attributes
43
+ as `:string` (lutaml-model does not need to validate the inner
44
+ whitespace-separated format) with matching `map_attribute` entries.
45
+ 2. `lib/chemicalml/cml/base/bond_array.rb` — declare the 4 attributes.
46
+ 3. Add specs in `spec/chemicalml/cml/parallel_array_spec.rb` asserting
47
+ round-trip for both forms.
48
+
49
+ ## Acceptance
50
+
51
+ - `Chemicalml.parse` of a parallel-array `<atomArray>` populates the
52
+ attributes; `Chemicalml.serialize` emits them back unchanged.
53
+ - Both forms supported for Schema3 and Schema24.
54
+ - Specs green.
@@ -0,0 +1,49 @@
1
+ # 62 — Per-element attribute gaps
2
+
3
+ ## Why
4
+
5
+ XSD vs Ruby Base comparison reveals 53 (schema3) / 58 (schema24)
6
+ attribute declarations that are not yet modelled. They cluster across
7
+ 20 elements. Most are 1-3 missing attributes; the parallel-array
8
+ attributes for atomArray/bondArray are split out into TODO 61.
9
+
10
+ ## Gaps (after TODO 61 closes)
11
+
12
+ | Element | Missing |
13
+ |---|---|
14
+ | alternative | type |
15
+ | band | kpoint (schema24 only) |
16
+ | bond | atomRefs (singular, not atomRefs2) |
17
+ | cellParameter | error, type |
18
+ | cml (module) | fileId, version |
19
+ | eigen | orientation |
20
+ | entry | convention, title, columns, rows, length, minLength, maxLength, pattern, minExclusive, minInclusive, maxExclusive, maxInclusive, fractionDigits, totalDigits, whiteSpace (XSD facets) |
21
+ | isotope | spin |
22
+ | module | role, serial |
23
+ | molecule | formula |
24
+ | peakStructure | type |
25
+ | potential | form |
26
+ | reaction | format, role |
27
+ | reactionScheme | format, role, type |
28
+ | reactionStepList | format |
29
+ | relatedEntry | type (schema24 only) |
30
+ | spectrum | type |
31
+ | substance | id |
32
+ | substanceList | type |
33
+ | unitList | type |
34
+
35
+ ## Work
36
+
37
+ For each row above, add `attribute :foo, :string` + `map_attribute "foo", to: :foo`
38
+ to the relevant `lib/chemicalml/cml/base/*.rb` module.
39
+
40
+ The 15 XSD facets on `entry` (columns, fractionDigits, etc.) are
41
+ genuine CML — they describe the data type the entry represents, and
42
+ are emitted by the upstream dictionary files. They are not internal
43
+ XSD noise.
44
+
45
+ ## Acceptance
46
+
47
+ - XSD vs Ruby comparison reports 0 attribute gaps.
48
+ - Round-trip of an example with `entry/@pattern` etc. is preserved.
49
+ - Spec coverage for at least one new attribute on each touched element.
@@ -0,0 +1,42 @@
1
+ # 63 — Universal children mixin
2
+
3
+ ## Why
4
+
5
+ The CML Schema 2.4 XSD declares `metadataList`, `label`, `name`, and
6
+ `description` as children of nearly every element. Adding these four
7
+ declarations to every `Base::*` module individually is a DRY
8
+ violation: the same 16 lines of `attribute` + `map_element` would
9
+ appear in ~50 files.
10
+
11
+ ## Solution
12
+
13
+ Introduce a shared mixin `Chemicalml::Cml::Base::CommonChildren`
14
+ that declares:
15
+
16
+ ```ruby
17
+ attribute :metadata_lists, :metadataList, collection: true
18
+ attribute :labels, :label, collection: true
19
+ attribute :names, :name, collection: true
20
+ attribute :descriptions, :description, collection: true
21
+ ```
22
+
23
+ with matching `map_element` declarations. The mixin is included by
24
+ the `Base::*` modules whose XSD declares these children.
25
+
26
+ The mixin is **opt-in** — elements that the XSD does not grant these
27
+ children to (e.g. `<atom>`, `<bond>` in schema24) do not include it.
28
+ This keeps MECE discipline: one place owns the universal child set.
29
+
30
+ ## Work
31
+
32
+ 1. Create `lib/chemicalml/cml/base/common_children.rb`.
33
+ 2. For each Base module where the XSD declares metadataList/label/name/description as a child, add `include CommonChildren` and remove the now-redundant individual declarations.
34
+ 3. Add a spec `spec/chemicalml/cml/common_children_spec.rb` asserting
35
+ the children appear after `include CommonChildren` and round-trip
36
+ through XML.
37
+
38
+ ## Acceptance
39
+
40
+ - `CommonChildren` exists and is included by at least 10 Base modules.
41
+ - No Base module re-declares `metadata_lists` / `labels` / `names` / `descriptions` individually.
42
+ - Specs green.
@@ -0,0 +1,81 @@
1
+ # 64 — Per-element child element gaps (Schema 2.4)
2
+
3
+ ## Why
4
+
5
+ XSD vs Ruby Base comparison reveals 235 missing child declarations
6
+ across ~60 Schema 2.4 elements. These are real CML elements that
7
+ real CML documents contain but our Ruby model drops silently.
8
+
9
+ The Schema 3 XSD declares `<anyCml>` (a wildcard) as a child of every
10
+ element — that is intentionally NOT modelled as a concrete child,
11
+ because doing so would defeat the type system. The Schema 2.4 gaps
12
+ below are *specific* named children, not wildcards.
13
+
14
+ ## Largest clusters
15
+
16
+ | Element | Missing children |
17
+ |---|---|
18
+ | molecule | angle, arg, array, crystal, electron, join, length, list, matrix, metadataList, propertyList, scalar, symmetry, torsion, zMatrix |
19
+ | reaction | conditionList, identifier, label, map, mechanism, metadataList, name, object, propertyList, reactiveCentre, spectatorList, substanceList, transitionState |
20
+ | reactant, product | amount, electron, formula, identifier, label, metadataList, molecule, name, substanceList |
21
+ | atom | array, atomType, electron, label, matrix, name, particle, scalar, vector3 |
22
+ | atomType, bondType | array, atom/bond, label, matrix, molecule, property, scalar |
23
+ | join | angle, arg, label, length, metadataList, molecule, torsion |
24
+ | peak, peakGroup | atom, bond, metadataList, molecule, peakStructure/peakGroup |
25
+ | spectrum | metadataList, parameterList, sample, spectrumData, substanceList |
26
+ | reactiveCentre | atomSet, atomTypeList, bondSet, bondTypeList, description |
27
+ | parameter | expression, gradient, property |
28
+ | property | metadataList, name |
29
+ | parameterList, productList, reactantList, reactionStepList | metadataList, name, *(selfRef)* |
30
+ | moleculeList | list, metadataList, moleculeList |
31
+ | sample | metadataList, molecule, substance, substanceList |
32
+ | spectator | label, metadataList, molecule, object |
33
+ | substance | amount, metadataList, property |
34
+ | substanceList | amount, metadataList, propertyList |
35
+ | lattice | matrix, scalar, symmetry |
36
+ | gradient | array, matrix, property, scalar |
37
+ | table | arrayList, tableRowList |
38
+ | transitionState | molecule, propertyList |
39
+ | unit | annotation, metadata, metadataList, unit, unitType |
40
+ | unitType | annotation, dimension |
41
+ | zMatrix | angle, length, torsion |
42
+ | symmetry | matrix, transform3 |
43
+ | eigen | array, matrix |
44
+ | xaxis, yaxis | array |
45
+ | metadataList | metadataList |
46
+ | crystal | cellParameter |
47
+ | formula | formula |
48
+ | dictionary | annotation, unitList |
49
+ | dictionary entry | alternative, annotation, enumeration, metadataList, relatedEntry |
50
+ | map | link |
51
+ | mechanism | description, label, name |
52
+ | peakStructure | metadataList, peakStructure |
53
+ | reactionList | metadataList, reactionScheme |
54
+ | reactionScheme | identifier, label, metadataList, name, reactionScheme |
55
+ | reactionStep | label, metadataList, name, reactionScheme |
56
+ | potentialForm | arg, expression, parameter |
57
+ | trow | tcell |
58
+ | peakList | metadataList |
59
+ | atomTypeList, bondTypeList, basisSet | metadataList, name |
60
+ | conditionList | list, metadataList, name |
61
+ | propertyList | metadataList, name, observation, propertyList |
62
+ | spectrumList | list, metadataList, spectrumList |
63
+
64
+ ## Strategy
65
+
66
+ After TODO 63 (CommonChildren mixin) closes, ~80% of the
67
+ `metadataList`/`label`/`name`/`description` gaps are absorbed by
68
+ adding one `include CommonChildren` line. The remaining ~150 gaps are
69
+ element-specific and require individual declarations.
70
+
71
+ ## Work
72
+
73
+ 1. Apply CommonChildren where the XSD permits.
74
+ 2. For each remaining gap, add the specific `attribute` + `map_element` declarations.
75
+ 3. Add at least one spec per touched element covering a round-trip
76
+ with the new child.
77
+
78
+ ## Acceptance
79
+
80
+ - XSD vs Ruby child comparison reports only `<anyCml>` Schema3 wildcard gaps (intentionally unmodelled).
81
+ - Specs green.
@@ -0,0 +1,40 @@
1
+ # 65 — SimpleUnit convention
2
+
3
+ ## Why
4
+
5
+ `reference-docs/conventions/index.md` lists six CML conventions.
6
+ We implement five + two added later (spectroscopy, cascade). The
7
+ sixth "official" one is `simpleUnit`, used as the worked example in
8
+ the CMLLite paper. Without it, the convention set is incomplete
9
+ relative to the upstream CML spec.
10
+
11
+ ## Source
12
+
13
+ `reference-docs/schemas/schema3/schema.xsd` — does not define
14
+ convention rules; the rules come from the CMLLite paper. The
15
+ namespace is `http://www.xml-cml.org/convention/simpleUnit`.
16
+
17
+ The paper's simpleUnit example constraints:
18
+
19
+ - A `<unit>` under simpleUnit MUST have a `power` attribute (integer).
20
+ - A `<unit>` under simpleUnit MUST have a `symbol` attribute (non-empty string).
21
+ - The root must be a `<unitList>` declaring `convention='convention:simpleUnit'`.
22
+
23
+ ## Work
24
+
25
+ 1. `lib/chemicalml/convention/simple_unit.rb` — main module (QNAME =
26
+ `convention:simpleUnit`, NAMESPACE_URI ends in `/simpleUnit`).
27
+ 2. `lib/chemicalml/convention/simple_unit/constraints.rb` — autoload registry.
28
+ 3. Three constraint files:
29
+ - `unit_must_have_power.rb` — applies_to Role::Unit
30
+ - `unit_must_have_symbol.rb` — applies_to Role::Unit
31
+ - `root_must_be_unit_list.rb` — DocumentConstraint
32
+ 4. Register in `Convention::Registry.load_cache`.
33
+ 5. Add `autoload :SimpleUnit` to `lib/chemicalml/convention.rb`.
34
+ 6. Specs at `spec/chemicalml/convention/simple_unit_spec.rb`.
35
+
36
+ ## Acceptance
37
+
38
+ - `Chemicalml::Convention.lookup("convention:simpleUnit")` returns the module.
39
+ - Specs cover all three constraints (positive + negative cases).
40
+ - Full suite green.
@@ -0,0 +1,32 @@
1
+ # 66 — Schema24-only legacy element children
2
+
3
+ ## Why
4
+
5
+ Schema 2.4 declares 17 elements absent from Schema 3 (TODO 52 modelled
6
+ them). Of those, several have non-trivial child models that were
7
+ skipped during initial modelling. XSD vs Ruby comparison shows:
8
+
9
+ | Element | Missing children |
10
+ |---|---|
11
+ | annotation | appinfo |
12
+ | arg | array, atom, atomType, expression, matrix, scalar |
13
+ | atomType | array, atom, label, matrix, molecule, property, scalar |
14
+ | bondType | array, bond, label, matrix, molecule, property, scalar |
15
+ | expression | operator, parameter |
16
+ | enumeration | annotation |
17
+ | potentialForm | arg, expression, parameter |
18
+
19
+ These are real Schema 2.4 features; the rest of the legacy set
20
+ (`float`, `integer`, `string`, `tcell`, `trow`, etc.) are scalar
21
+ leaves without children.
22
+
23
+ ## Work
24
+
25
+ 1. Update each Schema24-only `Base::*` module to declare the missing
26
+ children + matching `map_element`.
27
+ 2. Add a spec per touched element.
28
+
29
+ ## Acceptance
30
+
31
+ - XSD vs Ruby child comparison reports 0 Schema24-only-legacy gaps.
32
+ - Specs green.
@@ -0,0 +1,32 @@
1
+ # 67 — Final spec / lint / docs verification
2
+
3
+ ## Why
4
+
5
+ After TODOs 61-66 land, the public surface area grows by ~330 element
6
+ attribute/child declarations plus a new convention. CLAUDE.md and the
7
+ spec suite need to reflect the new state.
8
+
9
+ ## Work
10
+
11
+ 1. Update `CLAUDE.md`:
12
+ - List 8 conventions (molecular, compchem, dictionary, unit-dictionary,
13
+ unitType-dictionary, spectroscopy, cascade, simpleUnit).
14
+ - Note the parallel-array atomArray/bondArray serialisation forms.
15
+ - Note the `CommonChildren` mixin pattern.
16
+ 2. Scan the codebase for forbidden patterns:
17
+ - `require_relative` inside `lib/`
18
+ - `def to_h`, `def from_h`, `def to_xml`, `def from_xml`, `def to_json`, `def from_json` on model classes
19
+ - `double(` in specs
20
+ - `\.send(`, `instance_variable_set`, `instance_variable_get`, `respond_to?`
21
+ - AI attribution trailers in commit history
22
+ 3. Run full `bundle exec rspec` — must be green.
23
+ 4. Run `bundle exec rubocop` on touched files — must be clean.
24
+ 5. Add a top-level README under `TODO.align/` summarising rounds 1-17.
25
+
26
+ ## Acceptance
27
+
28
+ - `bundle exec rspec` exits 0.
29
+ - `grep -rn 'require_relative' lib/` returns nothing.
30
+ - `grep -rn 'def to_h\|def from_h' lib/chemicalml/cml/` returns nothing.
31
+ - `grep -rn 'double(' spec/` returns nothing.
32
+ - CLAUDE.md convention list is current.