chemicalml 0.2.1 → 0.4.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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +21 -14
- data/.rubocop.yml +23 -0
- data/CHANGELOG.md +32 -0
- data/CLAUDE.md +49 -1
- data/README.adoc +154 -26
- data/TODO.align/61-parallel-array-attrs.md +54 -0
- data/TODO.align/62-per-element-attribute-gaps.md +49 -0
- data/TODO.align/63-universal-children-mixin.md +42 -0
- data/TODO.align/64-per-element-child-gaps.md +81 -0
- data/TODO.align/65-simple-unit-convention.md +40 -0
- data/TODO.align/66-schema24-legacy-children.md +32 -0
- data/TODO.align/67-final-verification.md +32 -0
- data/TODO.align/68-detection-coverage.md +33 -0
- data/TODO.align/69-json-yaml-roundtrip.md +27 -0
- data/TODO.align/70-iterative-walker.md +40 -0
- data/TODO.align/71-violation-value.md +25 -0
- data/TODO.align/72-close-child-gaps.md +66 -0
- data/TODO.align/73-schema24-parse-limitation.md +64 -0
- data/TODO.align/74-final-verification-round18.md +15 -0
- data/TODO.align/75-key-value-mappings.md +43 -0
- data/TODO.align/76-schema24-workaround.md +57 -0
- data/TODO.align/77-convention-coverage-matrix.md +31 -0
- data/TODO.align/78-fixture-audit.md +23 -0
- data/TODO.align/79-final-verification-round19.md +15 -0
- data/TODO.align/80-xsd-enum-constants.md +35 -0
- data/TODO.align/81-enum-validation-constraints.md +29 -0
- data/TODO.align/82-reference-resolver.md +33 -0
- data/TODO.align/83-toml-roundtrip.md +20 -0
- data/TODO.align/84-final-verification-round21.md +16 -0
- data/TODO.align/85-bond-distinct-atoms.md +25 -0
- data/TODO.align/86-references-should-resolve.md +20 -0
- data/TODO.align/87-cli-utility.md +26 -0
- data/TODO.align/88-diverse-fixtures.md +26 -0
- data/TODO.align/89-final-verification-round22.md +15 -0
- data/TODO.align/90-bond-stereo-completeness.md +31 -0
- data/TODO.align/91-atomparity-distinct.md +21 -0
- data/TODO.align/92-unit-dictionary-completeness.md +28 -0
- data/TODO.align/93-yard-docs.md +23 -0
- data/TODO.align/94-final-verification-round23.md +15 -0
- data/TODO.align/README.round17.md +28 -0
- data/TODO.align/README.round18.md +37 -0
- data/TODO.align/README.round19.md +40 -0
- data/TODO.align/README.round20.md +49 -0
- data/TODO.align/README.round21.md +35 -0
- data/TODO.align/README.round22.md +48 -0
- data/TODO.align/README.round23.md +46 -0
- data/TODO.align/README.round24.md +53 -0
- data/TODO.align/README.round25.md +66 -0
- data/TODO.align/README.round26.md +55 -0
- data/TODO.align/README.round27.md +51 -0
- data/TODO.align/README.round28.md +61 -0
- data/TODO.align/README.round29.md +64 -0
- data/TODO.align/README.round30.md +50 -0
- data/TODO.align/README.round31.md +54 -0
- data/TODO.align/README.round32.md +67 -0
- data/TODO.align/README.round33.md +62 -0
- data/TODO.align/README.round34.md +70 -0
- data/chemicalml.gemspec +1 -1
- data/docs/constraints.md +161 -0
- data/docs/generate_constraint_docs.rb +44 -0
- data/exe/chemicalml +6 -0
- data/lib/chemicalml/cli/base.rb +68 -0
- data/lib/chemicalml/cli/command.rb +43 -0
- data/lib/chemicalml/cli/constraints_command.rb +27 -0
- data/lib/chemicalml/cli/conventions_command.rb +14 -0
- data/lib/chemicalml/cli/dictionaries_command.rb +14 -0
- data/lib/chemicalml/cli/elements_command.rb +17 -0
- data/lib/chemicalml/cli/enums_command.rb +17 -0
- data/lib/chemicalml/cli/info_command.rb +77 -0
- data/lib/chemicalml/cli/inspect_command.rb +39 -0
- data/lib/chemicalml/cli/validate_command.rb +54 -0
- data/lib/chemicalml/cli.rb +43 -0
- data/lib/chemicalml/cml/base/abundance.rb +18 -8
- data/lib/chemicalml/cml/base/action.rb +34 -16
- data/lib/chemicalml/cml/base/action_list.rb +35 -17
- data/lib/chemicalml/cml/base/alternative.rb +13 -4
- data/lib/chemicalml/cml/base/amount.rb +14 -6
- data/lib/chemicalml/cml/base/angle.rb +28 -13
- data/lib/chemicalml/cml/base/annotation.rb +14 -6
- data/lib/chemicalml/cml/base/any_cml.rb +12 -5
- data/lib/chemicalml/cml/base/appinfo.rb +14 -6
- data/lib/chemicalml/cml/base/arg.rb +47 -13
- data/lib/chemicalml/cml/base/array.rb +40 -19
- data/lib/chemicalml/cml/base/array_list.rb +14 -6
- data/lib/chemicalml/cml/base/atom.rb +84 -28
- data/lib/chemicalml/cml/base/atom_array.rb +60 -23
- data/lib/chemicalml/cml/base/atom_parity.rb +14 -6
- data/lib/chemicalml/cml/base/atom_set.rb +14 -6
- data/lib/chemicalml/cml/base/atom_type.rb +25 -9
- data/lib/chemicalml/cml/base/atom_type_list.rb +22 -8
- data/lib/chemicalml/cml/base/atomic_basis_function.rb +33 -13
- data/lib/chemicalml/cml/base/band.rb +21 -8
- data/lib/chemicalml/cml/base/band_list.rb +15 -7
- data/lib/chemicalml/cml/base/basis_set.rb +24 -9
- data/lib/chemicalml/cml/base/bond.rb +36 -10
- data/lib/chemicalml/cml/base/bond_array.rb +34 -11
- data/lib/chemicalml/cml/base/bond_set.rb +14 -6
- data/lib/chemicalml/cml/base/bond_stereo.rb +20 -9
- data/lib/chemicalml/cml/base/bond_type.rb +23 -8
- data/lib/chemicalml/cml/base/bond_type_list.rb +22 -8
- data/lib/chemicalml/cml/base/cell_parameter.rb +28 -10
- data/lib/chemicalml/cml/base/cml_module.rb +42 -11
- data/lib/chemicalml/cml/base/common_children.rb +35 -0
- data/lib/chemicalml/cml/base/complex_object.rb +12 -5
- data/lib/chemicalml/cml/base/condition_list.rb +26 -10
- data/lib/chemicalml/cml/base/crystal.rb +26 -10
- data/lib/chemicalml/cml/base/definition.rb +14 -5
- data/lib/chemicalml/cml/base/description.rb +16 -6
- data/lib/chemicalml/cml/base/dictionary.rb +28 -11
- data/lib/chemicalml/cml/base/dictionary_entry.rb +69 -10
- data/lib/chemicalml/cml/base/dimension.rb +26 -12
- data/lib/chemicalml/cml/base/document.rb +24 -8
- data/lib/chemicalml/cml/base/documentation.rb +12 -5
- data/lib/chemicalml/cml/base/eigen.rb +32 -8
- data/lib/chemicalml/cml/base/electron.rb +26 -12
- data/lib/chemicalml/cml/base/enumeration.rb +16 -7
- data/lib/chemicalml/cml/base/expression.rb +21 -6
- data/lib/chemicalml/cml/base/float.rb +22 -10
- data/lib/chemicalml/cml/base/float_array.rb +24 -11
- data/lib/chemicalml/cml/base/formula.rb +31 -10
- data/lib/chemicalml/cml/base/fragment.rb +30 -12
- data/lib/chemicalml/cml/base/fragment_list.rb +24 -9
- data/lib/chemicalml/cml/base/gradient.rb +19 -6
- data/lib/chemicalml/cml/base/identifier.rb +18 -8
- data/lib/chemicalml/cml/base/integer.rb +22 -10
- data/lib/chemicalml/cml/base/integer_array.rb +24 -11
- data/lib/chemicalml/cml/base/isotope.rb +28 -9
- data/lib/chemicalml/cml/base/isotope_list.rb +17 -8
- data/lib/chemicalml/cml/base/join.rb +25 -9
- data/lib/chemicalml/cml/base/kpoint.rb +16 -7
- data/lib/chemicalml/cml/base/kpoint_list.rb +15 -7
- data/lib/chemicalml/cml/base/label.rb +14 -6
- data/lib/chemicalml/cml/base/lattice.rb +35 -10
- data/lib/chemicalml/cml/base/lattice_vector.rb +16 -7
- data/lib/chemicalml/cml/base/length.rb +26 -12
- data/lib/chemicalml/cml/base/line3.rb +18 -8
- data/lib/chemicalml/cml/base/link.rb +41 -20
- data/lib/chemicalml/cml/base/list.rb +22 -10
- data/lib/chemicalml/cml/base/map.rb +32 -11
- data/lib/chemicalml/cml/base/matrix.rb +32 -15
- data/lib/chemicalml/cml/base/mechanism.rb +20 -7
- data/lib/chemicalml/cml/base/mechanism_component.rb +14 -6
- data/lib/chemicalml/cml/base/metadata.rb +18 -8
- data/lib/chemicalml/cml/base/metadata_list.rb +27 -8
- data/lib/chemicalml/cml/base/molecule.rb +92 -26
- data/lib/chemicalml/cml/base/molecule_list.rb +26 -8
- data/lib/chemicalml/cml/base/name.rb +10 -4
- data/lib/chemicalml/cml/base/object.rb +18 -8
- data/lib/chemicalml/cml/base/observation.rb +16 -7
- data/lib/chemicalml/cml/base/operator.rb +14 -6
- data/lib/chemicalml/cml/base/parameter.rb +33 -13
- data/lib/chemicalml/cml/base/parameter_list.rb +27 -8
- data/lib/chemicalml/cml/base/particle.rb +20 -9
- data/lib/chemicalml/cml/base/peak.rb +73 -26
- data/lib/chemicalml/cml/base/peak_group.rb +62 -26
- data/lib/chemicalml/cml/base/peak_list.rb +24 -9
- data/lib/chemicalml/cml/base/peak_structure.rb +40 -13
- data/lib/chemicalml/cml/base/plane3.rb +14 -6
- data/lib/chemicalml/cml/base/point3.rb +14 -6
- data/lib/chemicalml/cml/base/potential.rb +17 -6
- data/lib/chemicalml/cml/base/potential_form.rb +30 -7
- data/lib/chemicalml/cml/base/potential_list.rb +15 -7
- data/lib/chemicalml/cml/base/product.rb +45 -10
- data/lib/chemicalml/cml/base/product_list.rb +29 -9
- data/lib/chemicalml/cml/base/property.rb +29 -11
- data/lib/chemicalml/cml/base/property_list.rb +23 -8
- data/lib/chemicalml/cml/base/reactant.rb +45 -10
- data/lib/chemicalml/cml/base/reactant_list.rb +29 -9
- data/lib/chemicalml/cml/base/reaction.rb +74 -20
- data/lib/chemicalml/cml/base/reaction_list.rb +23 -8
- data/lib/chemicalml/cml/base/reaction_scheme.rb +43 -13
- data/lib/chemicalml/cml/base/reaction_step.rb +30 -12
- data/lib/chemicalml/cml/base/reaction_step_list.rb +30 -11
- data/lib/chemicalml/cml/base/reactive_centre.rb +31 -6
- data/lib/chemicalml/cml/base/region.rb +20 -9
- data/lib/chemicalml/cml/base/related_entry.rb +11 -3
- data/lib/chemicalml/cml/base/sample.rb +30 -7
- data/lib/chemicalml/cml/base/scalar.rb +32 -15
- data/lib/chemicalml/cml/base/spectator.rb +25 -6
- data/lib/chemicalml/cml/base/spectator_list.rb +20 -7
- data/lib/chemicalml/cml/base/spectrum.rb +57 -18
- data/lib/chemicalml/cml/base/spectrum_data.rb +19 -9
- data/lib/chemicalml/cml/base/spectrum_list.rb +28 -9
- data/lib/chemicalml/cml/base/sphere3.rb +14 -6
- data/lib/chemicalml/cml/base/stmml.rb +12 -5
- data/lib/chemicalml/cml/base/string.rb +14 -6
- data/lib/chemicalml/cml/base/string_array.rb +22 -10
- data/lib/chemicalml/cml/base/substance.rb +36 -14
- data/lib/chemicalml/cml/base/substance_list.rb +29 -10
- data/lib/chemicalml/cml/base/symmetry.rb +31 -9
- data/lib/chemicalml/cml/base/system.rb +21 -10
- data/lib/chemicalml/cml/base/table.rb +38 -13
- data/lib/chemicalml/cml/base/table_cell.rb +12 -5
- data/lib/chemicalml/cml/base/table_content.rb +19 -9
- data/lib/chemicalml/cml/base/table_header.rb +15 -7
- data/lib/chemicalml/cml/base/table_header_cell.rb +22 -10
- data/lib/chemicalml/cml/base/table_row.rb +15 -7
- data/lib/chemicalml/cml/base/table_row_list.rb +15 -7
- data/lib/chemicalml/cml/base/tcell.rb +16 -7
- data/lib/chemicalml/cml/base/torsion.rb +28 -13
- data/lib/chemicalml/cml/base/transform3.rb +12 -5
- data/lib/chemicalml/cml/base/transition_state.rb +23 -5
- data/lib/chemicalml/cml/base/trow.rb +22 -6
- data/lib/chemicalml/cml/base/unit.rb +39 -16
- data/lib/chemicalml/cml/base/unit_list.rb +34 -12
- data/lib/chemicalml/cml/base/unit_type.rb +30 -10
- data/lib/chemicalml/cml/base/unit_type_list.rb +29 -11
- data/lib/chemicalml/cml/base/vector3.rb +14 -6
- data/lib/chemicalml/cml/base/xaxis.rb +32 -10
- data/lib/chemicalml/cml/base/yaxis.rb +30 -10
- data/lib/chemicalml/cml/base/z_matrix.rb +26 -5
- data/lib/chemicalml/cml/base.rb +139 -138
- data/lib/chemicalml/cml/canonical_comparison.rb +54 -0
- data/lib/chemicalml/cml/elements.rb +145 -136
- data/lib/chemicalml/cml/enums.rb +55 -0
- data/lib/chemicalml/cml/namespace.rb +2 -3
- data/lib/chemicalml/cml/patterns.rb +33 -0
- data/lib/chemicalml/cml/reference_resolver.rb +137 -0
- data/lib/chemicalml/cml/role.rb +138 -138
- data/lib/chemicalml/cml/schema24/configuration.rb +11 -1
- data/lib/chemicalml/cml/schema24.rb +139 -139
- data/lib/chemicalml/cml/schema3.rb +123 -123
- data/lib/chemicalml/cml/visitable.rb +77 -3
- data/lib/chemicalml/cml/wire_class_registry.rb +3 -3
- data/lib/chemicalml/cml/xhtml/p.rb +21 -0
- data/lib/chemicalml/cml/xhtml.rb +20 -0
- data/lib/chemicalml/cml.rb +164 -129
- data/lib/chemicalml/context_configuration.rb +4 -2
- data/lib/chemicalml/convention/cascade/constraints/reaction_scheme_must_have_content.rb +33 -0
- data/lib/chemicalml/convention/cascade/constraints/reaction_step_list_must_contain_steps.rb +31 -0
- data/lib/chemicalml/convention/cascade/constraints/reaction_step_must_have_reaction_or_lists.rb +33 -0
- data/lib/chemicalml/convention/cascade/constraints/reactive_centre_atom_refs_should_be_present.rb +34 -0
- data/lib/chemicalml/convention/cascade/constraints.rb +18 -0
- data/lib/chemicalml/convention/cascade.rb +43 -0
- data/lib/chemicalml/convention/compchem/constraints/array_rules.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/calculation_requires_finalization.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +2 -1
- data/lib/chemicalml/convention/compchem/constraints/environment_at_most_one_property_list.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/environment_must_not_contain_parameter.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_molecule.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_property_list.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/finalization_must_have_content.rb +44 -0
- data/lib/chemicalml/convention/compchem/constraints/finalization_must_not_contain_parameter.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_molecule.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_parameter_list.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/initialization_must_have_content.rb +54 -0
- data/lib/chemicalml/convention/compchem/constraints/initialization_must_not_contain_property.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/job_list_module_must_have_id.rb +1 -0
- data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_environment.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_finalization.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/job_module_must_have_id.rb +1 -0
- data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +1 -0
- data/lib/chemicalml/convention/compchem/constraints/matrix_rules.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/scalar_units.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints.rb +4 -0
- data/lib/chemicalml/convention/compchem.rb +4 -2
- data/lib/chemicalml/convention/constraint.rb +40 -9
- data/lib/chemicalml/convention/coordinator.rb +4 -3
- data/lib/chemicalml/convention/detection.rb +10 -4
- data/lib/chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace.rb +1 -0
- data/lib/chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_contain_definition.rb +15 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_unit_type.rb +1 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_units_co_constraints.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints.rb +8 -8
- data/lib/chemicalml/convention/dictionary.rb +2 -2
- data/lib/chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +15 -6
- data/lib/chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table.rb +39 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_id_must_match_pattern.rb +3 -2
- data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_must_have_element_type.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/atom_must_have_id.rb +2 -1
- data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb +44 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb +65 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct.rb +44 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_id_should_match_pattern.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_must_have_order.rb +2 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum.rb +39 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb +43 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/dict_ref_should_resolve.rb +59 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_must_have_id.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/property_must_have_dict_ref.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary.rb +58 -0
- data/lib/chemicalml/convention/molecular/constraints/references_should_resolve.rb +37 -0
- data/lib/chemicalml/convention/molecular/constraints/scalar_must_have_data_type.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints.rb +49 -23
- data/lib/chemicalml/convention/molecular.rb +15 -2
- data/lib/chemicalml/convention/registry.rb +79 -11
- data/lib/chemicalml/convention/simple_unit/constraints/root_must_be_unit_list.rb +24 -0
- data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_power.rb +32 -0
- data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_symbol.rb +31 -0
- data/lib/chemicalml/convention/simple_unit/constraints.rb +16 -0
- data/lib/chemicalml/convention/simple_unit.rb +37 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/peak_should_have_values.rb +34 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention.rb +32 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints.rb +20 -0
- data/lib/chemicalml/convention/spectroscopy.rb +41 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit.rb +1 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace.rb +1 -1
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition.rb +1 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_id.rb +1 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si.rb +34 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si.rb +32 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +1 -1
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_title.rb +32 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve.rb +45 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve.rb +46 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints.rb +10 -0
- data/lib/chemicalml/convention/unit_dictionary.rb +7 -2
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern.rb +1 -1
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type.rb +1 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace.rb +1 -1
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition.rb +1 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +1 -1
- data/lib/chemicalml/convention/unit_type_dictionary.rb +2 -2
- data/lib/chemicalml/convention/validation_report.rb +30 -3
- data/lib/chemicalml/convention/violation.rb +9 -3
- data/lib/chemicalml/convention.rb +41 -14
- data/lib/chemicalml/dictionary.rb +13 -9
- data/lib/chemicalml/logger.rb +47 -0
- data/lib/chemicalml/schema/definition.rb +2 -2
- data/lib/chemicalml/schema.rb +7 -7
- data/lib/chemicalml/version.rb +1 -1
- data/lib/chemicalml/versioned_parser.rb +10 -10
- data/lib/chemicalml.rb +78 -15
- data/lib/tasks/reference_fetch.rake +30 -31
- data/lib/tasks/scrape_fixtures.rake +23 -24
- metadata +118 -5
|
@@ -8,9 +8,11 @@ module Chemicalml
|
|
|
8
8
|
# element. The schema allows it as a string; this constraint
|
|
9
9
|
# enforces presence.
|
|
10
10
|
class EntryMustContainDefinition < Chemicalml::Convention::Constraint::NodeConstraint
|
|
11
|
+
self.description = 'An `<entry>` MUST contain a single `definition` child element. '
|
|
11
12
|
applies_to Chemicalml::Cml::Role::DictionaryEntry
|
|
12
13
|
def check_node(node, path)
|
|
13
|
-
|
|
14
|
+
definition = node.definition
|
|
15
|
+
return [] if definition_contained?(definition)
|
|
14
16
|
|
|
15
17
|
[violation(path: path.join('/'),
|
|
16
18
|
message: "entry #{node.id.inspect} must contain a single definition child")]
|
|
@@ -18,6 +20,18 @@ module Chemicalml
|
|
|
18
20
|
|
|
19
21
|
private
|
|
20
22
|
|
|
23
|
+
# CML allows the definition content as plain text or as
|
|
24
|
+
# rich text (<xhtml:p> paragraphs); the wire class types it
|
|
25
|
+
# structured, but a plain string remains valid at
|
|
26
|
+
# construction.
|
|
27
|
+
def definition_contained?(definition)
|
|
28
|
+
return false if definition.nil?
|
|
29
|
+
return !definition.to_s.strip.empty? if definition.is_a?(String)
|
|
30
|
+
|
|
31
|
+
!(definition.content.to_s.strip.empty? &&
|
|
32
|
+
Array(definition.paragraphs).empty?)
|
|
33
|
+
end
|
|
34
|
+
|
|
21
35
|
def entry?(node)
|
|
22
36
|
node.is_a?(Chemicalml::Cml::Role::DictionaryEntry)
|
|
23
37
|
end
|
|
@@ -7,9 +7,9 @@ module Chemicalml
|
|
|
7
7
|
# Every entry MUST have an `id` and a `term` attribute per the
|
|
8
8
|
# dictionary convention.
|
|
9
9
|
class EntryMustHaveIdAndTerm < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
self.description = 'Every entry MUST have an `id` and a `term` attribute per the dictionary convention.'
|
|
10
11
|
applies_to Chemicalml::Cml::Role::DictionaryEntry
|
|
11
12
|
def check_node(node, path)
|
|
12
|
-
|
|
13
13
|
violations = []
|
|
14
14
|
if node.id.to_s.empty?
|
|
15
15
|
violations << violation(path: path.join('/'),
|
|
@@ -9,6 +9,7 @@ module Chemicalml
|
|
|
9
9
|
# of a program) should reference `none` in the standard CML
|
|
10
10
|
# unitType dictionary.
|
|
11
11
|
class EntryMustHaveUnitType < Chemicalml::Convention::Constraint::NodeConstraint
|
|
12
|
+
self.description = 'An `<entry>` MUST have a `unitType` attribute. Entries describing concepts that should not have units (e.g. name of a program) should reference `none` in the standard CML'
|
|
12
13
|
applies_to Chemicalml::Cml::Role::DictionaryEntry
|
|
13
14
|
def check_node(node, path)
|
|
14
15
|
return [] unless node.unit_type.to_s.empty?
|
|
@@ -10,11 +10,11 @@ module Chemicalml
|
|
|
10
10
|
# - If `unitType` is `none`, `units` MUST be present and point
|
|
11
11
|
# to `http://www.xml-cml.org/unit/si#none`.
|
|
12
12
|
class EntryUnitsCoConstraints < Chemicalml::Convention::Constraint::NodeConstraint
|
|
13
|
+
self.description = 'Co-constraint between `unitType` and `units`: - If `unitType` is `unknown`, `units` MUST NOT be present. - If `unitType` is `none`, `units` MUST be present and point'
|
|
13
14
|
applies_to Chemicalml::Cml::Role::DictionaryEntry
|
|
14
15
|
NONE_UNITS = 'http://www.xml-cml.org/unit/si#none'
|
|
15
16
|
|
|
16
17
|
def check_node(node, path)
|
|
17
|
-
|
|
18
18
|
unit_type = node.unit_type.to_s
|
|
19
19
|
units = node.units.to_s
|
|
20
20
|
violations = []
|
|
@@ -5,21 +5,21 @@ module Chemicalml
|
|
|
5
5
|
module Dictionary
|
|
6
6
|
module Constraints
|
|
7
7
|
autoload :EntryMustHaveIdAndTerm,
|
|
8
|
-
|
|
8
|
+
'chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term'
|
|
9
9
|
autoload :EntryIdsUniqueWithinDictionary,
|
|
10
|
-
|
|
10
|
+
'chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary'
|
|
11
11
|
autoload :DictionaryMustHaveNamespace,
|
|
12
|
-
|
|
12
|
+
'chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace'
|
|
13
13
|
autoload :DictionaryNamespaceShouldEndWithSlashOrHash,
|
|
14
|
-
|
|
14
|
+
'chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash'
|
|
15
15
|
autoload :EntryMustContainDefinition,
|
|
16
|
-
|
|
16
|
+
'chemicalml/convention/dictionary/constraints/entry_must_contain_definition'
|
|
17
17
|
autoload :EntryIdMustMatchPattern,
|
|
18
|
-
|
|
18
|
+
'chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern'
|
|
19
19
|
autoload :EntryMustHaveUnitType,
|
|
20
|
-
|
|
20
|
+
'chemicalml/convention/dictionary/constraints/entry_must_have_unit_type'
|
|
21
21
|
autoload :EntryUnitsCoConstraints,
|
|
22
|
-
|
|
22
|
+
'chemicalml/convention/dictionary/constraints/entry_units_co_constraints'
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -8,9 +8,9 @@ module Chemicalml
|
|
|
8
8
|
module Dictionary
|
|
9
9
|
extend Base
|
|
10
10
|
|
|
11
|
-
autoload :Constraints,
|
|
11
|
+
autoload :Constraints, 'chemicalml/convention/dictionary/constraints'
|
|
12
12
|
|
|
13
|
-
QNAME =
|
|
13
|
+
QNAME = 'convention:dictionary'
|
|
14
14
|
NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}dictionary".freeze
|
|
15
15
|
|
|
16
16
|
def self.qname
|
|
@@ -10,6 +10,7 @@ module Chemicalml
|
|
|
10
10
|
# when visiting a non-molecule/non-formula node, look for
|
|
11
11
|
# misplaced atomArray children.
|
|
12
12
|
class AtomArrayMustBeChildOfMoleculeOrFormula < Chemicalml::Convention::Constraint::NodeConstraint
|
|
13
|
+
self.description = "Molecular convention: `<atomArray>` MUST be a child of `<molecule>` or `<formula>`. Any other parent is invalid. Checked from the parent's perspective during the walk —"
|
|
13
14
|
def check_node(node, path)
|
|
14
15
|
return [] if allowed_parent?(node)
|
|
15
16
|
|
|
@@ -4,15 +4,17 @@ module Chemicalml
|
|
|
4
4
|
module Convention
|
|
5
5
|
module Molecular
|
|
6
6
|
module Constraints
|
|
7
|
-
# `atomArray` MUST contain at least one
|
|
8
|
-
#
|
|
7
|
+
# `atomArray` MUST contain at least one atom. The atoms can
|
|
8
|
+
# be in the child form (`<atom>` elements) or the parallel-array
|
|
9
|
+
# form (`atomID` attribute with whitespace-separated ids).
|
|
9
10
|
class AtomArrayMustContainAtoms < Chemicalml::Convention::Constraint::NodeConstraint
|
|
11
|
+
self.description = '`atomArray` MUST contain at least one atom. The atoms can be in the child form (`<atom>` elements) or the parallel-array form (`atomID` attribute with whitespace-separated ids).'
|
|
10
12
|
applies_to Chemicalml::Cml::Role::Molecule
|
|
11
13
|
def check_node(node, path)
|
|
12
14
|
return [] unless node.atom_array
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
return []
|
|
16
|
+
aa = node.atom_array
|
|
17
|
+
return [] if has_child_atoms?(aa) || has_parallel_array_atoms?(aa)
|
|
16
18
|
|
|
17
19
|
[violation(path: path.empty? ? 'molecule' : path.join('/'),
|
|
18
20
|
message: 'atomArray must contain at least one atom')]
|
|
@@ -20,8 +22,15 @@ module Chemicalml
|
|
|
20
22
|
|
|
21
23
|
private
|
|
22
24
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
+
def has_child_atoms?(atom_array)
|
|
26
|
+
!(atom_array.atoms || []).empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Parallel-array form is identified by a non-empty atomIDArray
|
|
30
|
+
# attribute — the XSD marks it as mandatory when atoms are
|
|
31
|
+
# encoded as parallel arrays.
|
|
32
|
+
def has_parallel_array_atoms?(atom_array)
|
|
33
|
+
!atom_array.atom_id_array.to_s.strip.empty?
|
|
25
34
|
end
|
|
26
35
|
end
|
|
27
36
|
end
|
|
@@ -5,9 +5,9 @@ module Chemicalml
|
|
|
5
5
|
module Molecular
|
|
6
6
|
module Constraints
|
|
7
7
|
class AtomCoordinatesMustBePaired < Chemicalml::Convention::Constraint::NodeConstraint
|
|
8
|
+
self.description = '<atom> 2D coordinates (x2, y2) must appear together; 3D coordinates (x3, y3, z3) must appear together.'
|
|
8
9
|
applies_to Chemicalml::Cml::Role::Atom
|
|
9
10
|
def check_node(node, path)
|
|
10
|
-
|
|
11
11
|
violations = []
|
|
12
12
|
has_x2 = !node.x2.to_s.empty?
|
|
13
13
|
has_y2 = !node.y2.to_s.empty?
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# An `<atom>`'s `elementType` attribute SHOULD be one of the
|
|
8
|
+
# XSD elementTypeType enum values (the periodic table plus
|
|
9
|
+
# the special "Du" dummy and "R" group placeholder). Warns
|
|
10
|
+
# on typos like "Carb" or "X".
|
|
11
|
+
class AtomElementTypeShouldBeInPeriodicTable < Chemicalml::Convention::Constraint::NodeConstraint
|
|
12
|
+
self.description = "An `<atom>`'s `elementType` attribute SHOULD be one of the XSD elementTypeType enum values (the periodic table plus the special \"Du\" dummy and \"R\" group placeholder). Warns"
|
|
13
|
+
include Chemicalml::Cml::Enums
|
|
14
|
+
|
|
15
|
+
applies_to Chemicalml::Cml::Role::Atom
|
|
16
|
+
|
|
17
|
+
def check_node(node, _path)
|
|
18
|
+
et = node.element_type.to_s.strip
|
|
19
|
+
return [] if et.empty?
|
|
20
|
+
return [] if ELEMENT_TYPE_VALUES.include?(et)
|
|
21
|
+
|
|
22
|
+
[violation(path: yield_path(node),
|
|
23
|
+
message: "atom #{node.id.inspect} elementType #{et.inspect} should be one of " \
|
|
24
|
+
"the periodic table symbols (e.g. C, H, O, Fe) — got #{et.inspect}",
|
|
25
|
+
severity: :warning,
|
|
26
|
+
value: et)]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def yield_path(node)
|
|
32
|
+
id = node.node_id
|
|
33
|
+
id ? "atom[#{id}]" : 'atom'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -5,11 +5,11 @@ module Chemicalml
|
|
|
5
5
|
module Molecular
|
|
6
6
|
module Constraints
|
|
7
7
|
class AtomIdMustMatchPattern < Chemicalml::Convention::Constraint::NodeConstraint
|
|
8
|
+
self.description = "An <atom>'s id attribute SHOULD start with a letter and contain only letters, digits, dot, hyphen, or underscore."
|
|
8
9
|
applies_to Chemicalml::Cml::Role::Atom
|
|
9
10
|
ID_PATTERN = /\A[A-Za-z][A-Za-z0-9._-]*\z/
|
|
10
11
|
|
|
11
12
|
def check_node(node, path)
|
|
12
|
-
|
|
13
13
|
id = node.id.to_s
|
|
14
14
|
return [] if id.empty?
|
|
15
15
|
return [] if id.match?(ID_PATTERN)
|
|
@@ -17,7 +17,8 @@ module Chemicalml
|
|
|
17
17
|
[violation(path: path.empty? ? 'atom' : path.join('/'),
|
|
18
18
|
message: "atom id #{id.inspect} must start with a letter and " \
|
|
19
19
|
'contain only letters, digits, dot, hyphen, or underscore',
|
|
20
|
-
severity: :warning
|
|
20
|
+
severity: :warning,
|
|
21
|
+
value: id)]
|
|
21
22
|
end
|
|
22
23
|
end
|
|
23
24
|
end
|
|
@@ -7,6 +7,7 @@ module Chemicalml
|
|
|
7
7
|
# Atom ids MUST be unique within the eldest containing molecule
|
|
8
8
|
# per the molecular convention.
|
|
9
9
|
class AtomIdsUniqueWithinMolecule < Chemicalml::Convention::Constraint::DocumentConstraint
|
|
10
|
+
self.description = 'Atom ids MUST be unique within the eldest containing molecule per the molecular convention.'
|
|
10
11
|
def check(document)
|
|
11
12
|
violations = []
|
|
12
13
|
walk_nodes(document) do |node, path|
|
|
@@ -5,9 +5,9 @@ module Chemicalml
|
|
|
5
5
|
module Molecular
|
|
6
6
|
module Constraints
|
|
7
7
|
class AtomMustHaveElementType < Chemicalml::Convention::Constraint::NodeConstraint
|
|
8
|
+
self.description = 'An <atom> inside an atomArray in a molecule MUST have an elementType attribute.'
|
|
8
9
|
applies_to Chemicalml::Cml::Role::Atom
|
|
9
10
|
def check_node(node, path)
|
|
10
|
-
|
|
11
11
|
return [] unless node.element_type.to_s.empty?
|
|
12
12
|
|
|
13
13
|
[violation(path: path.empty? ? 'atom' : path.join('/'),
|
|
@@ -6,8 +6,9 @@ module Chemicalml
|
|
|
6
6
|
module Constraints
|
|
7
7
|
class AtomMustHaveId < Chemicalml::Convention::Constraint::NodeConstraint
|
|
8
8
|
applies_to Chemicalml::Cml::Role::Atom
|
|
9
|
-
|
|
9
|
+
self.description = 'An <atom> inside an atomArray in a molecule MUST have an id attribute.'
|
|
10
10
|
|
|
11
|
+
def check_node(node, path)
|
|
11
12
|
return [] unless node.id.to_s.empty?
|
|
12
13
|
|
|
13
14
|
[violation(path: path.empty? ? 'atom' : path.join('/'),
|
data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# An `<atomParity>` element's `atomRefs4` attribute SHOULD
|
|
8
|
+
# reference four distinct atom ids around a chiral center.
|
|
9
|
+
# Duplicate ids make the parity descriptor meaningless.
|
|
10
|
+
# Warning severity.
|
|
11
|
+
class AtomParityAtomRefs4ShouldBeDistinct < Chemicalml::Convention::Constraint::NodeConstraint
|
|
12
|
+
self.description = "An `<atomParity>` element's `atomRefs4` attribute SHOULD reference four distinct atom ids around a chiral center. Duplicate ids make the parity descriptor meaningless."
|
|
13
|
+
applies_to Chemicalml::Cml::Role::AtomParity
|
|
14
|
+
|
|
15
|
+
def check_node(node, _path)
|
|
16
|
+
ids = parse_refs(node.atom_refs4)
|
|
17
|
+
return [] if ids.size < 2
|
|
18
|
+
return [] if ids.uniq.size == ids.size
|
|
19
|
+
|
|
20
|
+
duplicates = ids.group_by { |i| i }.select { |_, v| v.size > 1 }.keys
|
|
21
|
+
[violation(path: yield_path(node),
|
|
22
|
+
message: "atomParity #{node.id.inspect} atomRefs4 should reference four distinct atoms; " \
|
|
23
|
+
"duplicate ids: #{duplicates.inspect}",
|
|
24
|
+
severity: :warning,
|
|
25
|
+
value: node.atom_refs4)]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def yield_path(node)
|
|
31
|
+
id = node.node_id
|
|
32
|
+
id ? "atomParity[#{id}]" : 'atomParity'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def parse_refs(value)
|
|
36
|
+
return [] if value.nil?
|
|
37
|
+
|
|
38
|
+
value.to_s.split(/\s+/).reject(&:empty?)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# Walks the document and warns on `<atomParity>` elements
|
|
8
|
+
# whose parent `<atom>`'s id does not appear in atomRefs4.
|
|
9
|
+
# The CML convention requires the parent atom to be one of
|
|
10
|
+
# the four references (otherwise the parity is meaningless).
|
|
11
|
+
#
|
|
12
|
+
# DocumentConstraint because it needs to walk the tree and
|
|
13
|
+
# track parent-child relationships.
|
|
14
|
+
class AtomParityShouldIncludeParentAtom < Chemicalml::Convention::Constraint::DocumentConstraint
|
|
15
|
+
self.description = "Walks the document and warns on `<atomParity>` elements whose parent `<atom>`'s id does not appear in atomRefs4. The CML convention requires the parent atom to be one of"
|
|
16
|
+
def check(document)
|
|
17
|
+
violations = []
|
|
18
|
+
visit_with_parent(document, nil) do |node, parent|
|
|
19
|
+
next unless atom_parity?(node)
|
|
20
|
+
next unless atom?(parent)
|
|
21
|
+
|
|
22
|
+
refs = parse_refs(node.atom_refs4)
|
|
23
|
+
next if refs.empty?
|
|
24
|
+
next if refs.include?(parent.id)
|
|
25
|
+
|
|
26
|
+
violations << violation(
|
|
27
|
+
path: "atom[#{parent.id}]/atomParity",
|
|
28
|
+
message: "atomParity atomRefs4 #{refs.inspect} should include the parent atom id " \
|
|
29
|
+
"#{parent.id.inspect}",
|
|
30
|
+
severity: :warning,
|
|
31
|
+
value: node.atom_refs4
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
violations
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def visit_with_parent(node, parent, &block)
|
|
40
|
+
return unless node.is_a?(Lutaml::Model::Serializable)
|
|
41
|
+
|
|
42
|
+
block.call(node, parent)
|
|
43
|
+
return unless node.is_a?(Chemicalml::Cml::Visitable)
|
|
44
|
+
|
|
45
|
+
node.wire_children.each { |c| visit_with_parent(c, node, &block) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def atom_parity?(node)
|
|
49
|
+
node.is_a?(Chemicalml::Cml::Role::AtomParity)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def atom?(node)
|
|
53
|
+
node.is_a?(Chemicalml::Cml::Role::Atom)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def parse_refs(value)
|
|
57
|
+
return [] if value.nil?
|
|
58
|
+
|
|
59
|
+
value.to_s.split(/\s+/).reject(&:empty?)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb
CHANGED
|
@@ -7,6 +7,7 @@ module Chemicalml
|
|
|
7
7
|
# Molecular convention: `<bondArray>` MUST be a child of
|
|
8
8
|
# `<molecule>`. Any other parent is invalid.
|
|
9
9
|
class BondArrayMustBeChildOfMolecule < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
self.description = 'Molecular convention: `<bondArray>` MUST be a child of `<molecule>`. Any other parent is invalid.'
|
|
10
11
|
def check_node(node, path)
|
|
11
12
|
return [] if molecule?(node)
|
|
12
13
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# A `<bond>`'s `atomRefs2` SHOULD reference two distinct atom
|
|
8
|
+
# ids. A self-bond (`a1 a1`) is chemically meaningless.
|
|
9
|
+
# Warning severity — there are edge cases in non-classical
|
|
10
|
+
# chemistry (e.g. agostic interactions).
|
|
11
|
+
class BondAtomRefs2ShouldBeDistinct < Chemicalml::Convention::Constraint::NodeConstraint
|
|
12
|
+
self.description = "A `<bond>`'s `atomRefs2` SHOULD reference two distinct atom ids. A self-bond (`a1 a1`) is chemically meaningless. Warning severity — there are edge cases in non-classical"
|
|
13
|
+
applies_to Chemicalml::Cml::Role::Bond
|
|
14
|
+
|
|
15
|
+
def check_node(node, _path)
|
|
16
|
+
ids = parse_refs(node.atom_refs2)
|
|
17
|
+
return [] if ids.size < 2
|
|
18
|
+
return [] if ids.uniq.size == ids.size
|
|
19
|
+
|
|
20
|
+
duplicates = ids.group_by { |i| i }.select { |_, v| v.size > 1 }.keys
|
|
21
|
+
[violation(path: yield_path(node),
|
|
22
|
+
message: "bond #{node.id.inspect} atomRefs2 should reference two distinct atoms; " \
|
|
23
|
+
"duplicate ids: #{duplicates.inspect}",
|
|
24
|
+
severity: :warning,
|
|
25
|
+
value: node.atom_refs2)]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def yield_path(node)
|
|
31
|
+
id = node.node_id
|
|
32
|
+
id ? "bond[#{id}]" : 'bond'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def parse_refs(value)
|
|
36
|
+
return [] if value.nil?
|
|
37
|
+
|
|
38
|
+
value.to_s.split(/\s+/).reject(&:empty?)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# A `<bond>`'s `id` attribute SHOULD match the XSD idType
|
|
8
|
+
# pattern (letter start, alphanumeric body with dots/dashes).
|
|
9
|
+
# Warning severity.
|
|
10
|
+
class BondIdShouldMatchPattern < Chemicalml::Convention::Constraint::NodeConstraint
|
|
11
|
+
self.description = "A `<bond>`'s `id` attribute SHOULD match the XSD idType pattern (letter start, alphanumeric body with dots/dashes). Warning severity."
|
|
12
|
+
include Chemicalml::Cml::Patterns
|
|
13
|
+
|
|
14
|
+
applies_to Chemicalml::Cml::Role::Bond
|
|
15
|
+
|
|
16
|
+
def check_node(node, _path)
|
|
17
|
+
id = node.id.to_s
|
|
18
|
+
return [] if id.empty?
|
|
19
|
+
return [] if id.match?(/\A#{ID_PATTERN.source}\z/)
|
|
20
|
+
|
|
21
|
+
[violation(path: yield_path(node),
|
|
22
|
+
message: "bond id #{id.inspect} should match the idType pattern " \
|
|
23
|
+
'(letter start, alphanumeric body with . and -)',
|
|
24
|
+
severity: :warning,
|
|
25
|
+
value: id)]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def yield_path(node)
|
|
31
|
+
id = node.node_id
|
|
32
|
+
id ? "bond[#{id}]" : 'bond'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -8,6 +8,7 @@ module Chemicalml
|
|
|
8
8
|
# the eldest containing molecule. Mirrors the existing
|
|
9
9
|
# `AtomIdsUniqueWithinMolecule` constraint for bonds.
|
|
10
10
|
class BondIdsUniqueWithinMolecule < Chemicalml::Convention::Constraint::DocumentConstraint
|
|
11
|
+
self.description = 'Molecular convention: a `<bond>` `id` MUST be unique within the eldest containing molecule. Mirrors the existing `AtomIdsUniqueWithinMolecule` constraint for bonds.'
|
|
11
12
|
def check(document)
|
|
12
13
|
violations = []
|
|
13
14
|
walk_nodes(document) do |node, path|
|
|
@@ -5,9 +5,9 @@ module Chemicalml
|
|
|
5
5
|
module Molecular
|
|
6
6
|
module Constraints
|
|
7
7
|
class BondMustHaveAtomRefs2 < Chemicalml::Convention::Constraint::NodeConstraint
|
|
8
|
+
self.description = 'A <bond> MUST have an atomRefs2 attribute referencing two distinct atoms in the same molecule.'
|
|
8
9
|
applies_to Chemicalml::Cml::Role::Bond
|
|
9
10
|
def check_node(node, path)
|
|
10
|
-
|
|
11
11
|
return [] unless node.atom_refs2.to_s.strip.empty?
|
|
12
12
|
|
|
13
13
|
[violation(path: path.empty? ? 'bond' : path.join('/'),
|
|
@@ -6,8 +6,9 @@ module Chemicalml
|
|
|
6
6
|
module Constraints
|
|
7
7
|
class BondMustHaveOrder < Chemicalml::Convention::Constraint::NodeConstraint
|
|
8
8
|
applies_to Chemicalml::Cml::Role::Bond
|
|
9
|
-
|
|
9
|
+
self.description = 'A <bond> MUST have an order attribute (S/D/T/A recommended).'
|
|
10
10
|
|
|
11
|
+
def check_node(node, path)
|
|
11
12
|
return [] unless node.order.to_s.strip.empty?
|
|
12
13
|
|
|
13
14
|
[violation(path: path.empty? ? 'bond' : path.join('/'),
|
data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb
CHANGED
|
@@ -7,6 +7,7 @@ module Chemicalml
|
|
|
7
7
|
# Bonds MUST reference atoms within the same parent molecule
|
|
8
8
|
# per the molecular convention.
|
|
9
9
|
class BondMustReferenceAtomsInSameMolecule < Chemicalml::Convention::Constraint::DocumentConstraint
|
|
10
|
+
self.description = 'Bonds MUST reference atoms within the same parent molecule per the molecular convention.'
|
|
10
11
|
def check(document)
|
|
11
12
|
violations = []
|
|
12
13
|
walk_nodes(document) do |node, path|
|
|
@@ -9,6 +9,7 @@ module Chemicalml
|
|
|
9
9
|
# the order semantics. The warning-level "should not be
|
|
10
10
|
# numeric" rule is separate; this is the hard error.
|
|
11
11
|
class BondOrderOtherMustHaveDictRef < Chemicalml::Convention::Constraint::NodeConstraint
|
|
12
|
+
self.description = 'Molecular convention: `<bond>` with `order="other"` MUST have a `dictRef` pointing to the dictionary that defines the order semantics. The warning-level "should not be'
|
|
12
13
|
applies_to Chemicalml::Cml::Role::Bond
|
|
13
14
|
def check_node(node, path)
|
|
14
15
|
return [] unless node.order.to_s.downcase == 'other'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# A `<bond>`'s `order` attribute SHOULD be one of the XSD
|
|
8
|
+
# orderType enum values: S/1/D/2/T/3/A/unknown/other.
|
|
9
|
+
# Warning severity — CML permits extension values via
|
|
10
|
+
# convention-specific dicts.
|
|
11
|
+
class BondOrderShouldBeInEnum < Chemicalml::Convention::Constraint::NodeConstraint
|
|
12
|
+
self.description = "A `<bond>`'s `order` attribute SHOULD be one of the XSD orderType enum values: S/1/D/2/T/3/A/unknown/other. Warning severity — CML permits extension values via"
|
|
13
|
+
include Chemicalml::Cml::Enums
|
|
14
|
+
|
|
15
|
+
applies_to Chemicalml::Cml::Role::Bond
|
|
16
|
+
|
|
17
|
+
def check_node(node, _path)
|
|
18
|
+
order = node.order.to_s
|
|
19
|
+
return [] if order.empty?
|
|
20
|
+
return [] if ORDER_VALUES.include?(order)
|
|
21
|
+
|
|
22
|
+
[violation(path: yield_path(node),
|
|
23
|
+
message: "bond #{node.id.inspect} order #{order.inspect} should be one of " \
|
|
24
|
+
"#{ORDER_VALUES.to_a.sort.inspect}",
|
|
25
|
+
severity: :warning,
|
|
26
|
+
value: order)]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def yield_path(node)
|
|
32
|
+
id = node.node_id
|
|
33
|
+
id ? "bond[#{id}]" : 'bond'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -5,11 +5,11 @@ module Chemicalml
|
|
|
5
5
|
module Molecular
|
|
6
6
|
module Constraints
|
|
7
7
|
class BondOrderShouldNotBeNumeric < Chemicalml::Convention::Constraint::NodeConstraint
|
|
8
|
+
self.description = "A <bond>'s order attribute SHOULD be one of S/D/T/Q/A or 'other' (numeric values are not recommended)."
|
|
8
9
|
applies_to Chemicalml::Cml::Role::Bond
|
|
9
10
|
VALID_ORDERS = %w[S D T Q A other].freeze
|
|
10
11
|
|
|
11
12
|
def check_node(node, path)
|
|
12
|
-
|
|
13
13
|
order = node.order.to_s
|
|
14
14
|
return [] if order.empty?
|
|
15
15
|
|
data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# A `<bondStereo>` with `atomRefs4` (used by C/T stereo)
|
|
8
|
+
# SHOULD reference four distinct atom ids. Duplicate ids make
|
|
9
|
+
# the stereo descriptor meaningless. Warning severity.
|
|
10
|
+
class BondStereoAtomRefs4ShouldBeDistinct < Chemicalml::Convention::Constraint::NodeConstraint
|
|
11
|
+
self.description = 'A `<bondStereo>` with `atomRefs4` (used by C/T stereo) SHOULD reference four distinct atom ids. Duplicate ids make the stereo descriptor meaningless. Warning severity.'
|
|
12
|
+
applies_to Chemicalml::Cml::Role::BondStereo
|
|
13
|
+
|
|
14
|
+
def check_node(node, _path)
|
|
15
|
+
ids = parse_refs(node.atom_refs4)
|
|
16
|
+
return [] if ids.size < 2
|
|
17
|
+
return [] if ids.uniq.size == ids.size
|
|
18
|
+
|
|
19
|
+
duplicates = ids.group_by { |i| i }.select { |_, v| v.size > 1 }.keys
|
|
20
|
+
[violation(path: yield_path(node),
|
|
21
|
+
message: "bondStereo #{node.id.inspect} atomRefs4 should reference four distinct atoms; " \
|
|
22
|
+
"duplicate ids: #{duplicates.inspect}",
|
|
23
|
+
severity: :warning,
|
|
24
|
+
value: node.atom_refs4)]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def yield_path(node)
|
|
30
|
+
id = node.node_id
|
|
31
|
+
id ? "bondStereo[#{id}]" : 'bondStereo'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_refs(value)
|
|
35
|
+
return [] if value.nil?
|
|
36
|
+
|
|
37
|
+
value.to_s.split(/\s+/).reject(&:empty?)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb
CHANGED
|
@@ -7,11 +7,11 @@ module Chemicalml
|
|
|
7
7
|
# Molecular convention: `<bondStereo>` value C (cis) or T
|
|
8
8
|
# (trans) MUST have `atomRefs4` and MUST NOT have `atomRefs2`.
|
|
9
9
|
class BondStereoCisTransMustHaveAtomRefs4 < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
self.description = 'Molecular convention: `<bondStereo>` value C (cis) or T (trans) MUST have `atomRefs4` and MUST NOT have `atomRefs2`.'
|
|
10
11
|
applies_to Chemicalml::Cml::Role::BondStereo
|
|
11
12
|
CIS_TRANS_VALUES = %w[C T].freeze
|
|
12
13
|
|
|
13
14
|
def check_node(node, path)
|
|
14
|
-
|
|
15
15
|
value = node.content.to_s.upcase
|
|
16
16
|
return [] unless CIS_TRANS_VALUES.include?(value)
|
|
17
17
|
|
data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb
CHANGED
|
@@ -8,6 +8,7 @@ module Chemicalml
|
|
|
8
8
|
# MUST have a `dictRef` pointing to the convention that
|
|
9
9
|
# defines the stereo semantics.
|
|
10
10
|
class BondStereoOtherMustHaveDictRef < Chemicalml::Convention::Constraint::NodeConstraint
|
|
11
|
+
self.description = 'Molecular convention: `<bondStereo>` with value `other` MUST have a `dictRef` pointing to the convention that defines the stereo semantics.'
|
|
11
12
|
applies_to Chemicalml::Cml::Role::BondStereo
|
|
12
13
|
def check_node(node, path)
|
|
13
14
|
return [] unless node.content.to_s.downcase == 'other'
|