chemicalml 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +20 -0
- data/CHANGELOG.md +21 -0
- data/CLAUDE.md +198 -0
- data/Gemfile +14 -0
- data/LICENSE +24 -0
- data/NOTICES.adoc +115 -0
- data/README.adoc +87 -0
- data/Rakefile +8 -0
- data/TODO.align/01-cml-base-mixins.md +122 -0
- data/TODO.align/02-real-schema-hierarchies.md +88 -0
- data/TODO.align/03-configuration.md +50 -0
- data/TODO.align/04-parse-entry-points.md +59 -0
- data/TODO.align/05-schema-aware-translator.md +33 -0
- data/TODO.align/06-no-respond-to.md +44 -0
- data/TODO.align/07-canon-xml-comparison.md +43 -0
- data/TODO.align/08-expand-fixtures.md +37 -0
- data/TODO.align/09-final-spec-lint.md +29 -0
- data/TODO.align/10-role-marker-modules.md +54 -0
- data/TODO.align/11-dry-configuration.md +63 -0
- data/TODO.align/12-wire-class-macro.md +51 -0
- data/TODO.align/13-wire-namespace.md +44 -0
- data/TODO.align/14-schema24-correctness.md +37 -0
- data/TODO.align/15-canon-fixture-roundtrip.md +38 -0
- data/TODO.align/16-schema24-convention-coverage.md +33 -0
- data/TODO.align/17-final-spec-lint.md +28 -0
- data/TODO.align/README.md +27 -0
- data/TODO.align/README.round2.md +24 -0
- data/TODO.cml-full/01-reference-docs.md +43 -0
- data/TODO.cml-full/02-schema-versioned-model.md +70 -0
- data/TODO.cml-full/03-fixtures-scrape.md +52 -0
- data/TODO.cml-full/04-convention-framework.md +69 -0
- data/TODO.cml-full/05-dictionary-layer.md +77 -0
- data/TODO.cml-full/06-extend-canonical-translator.md +60 -0
- data/TODO.cml-full/07-attribution-notices.md +42 -0
- data/TODO.cml-full/08-autoload-only.md +48 -0
- data/TODO.cml-full/09-spec-coverage.md +38 -0
- data/TODO.cml-full/README.md +23 -0
- data/chemicalml.gemspec +35 -0
- data/data/dictionaries/_index.yaml +14 -0
- data/data/dictionaries/compchem.yaml +481 -0
- data/data/dictionaries/unit_non_si.yaml +53 -0
- data/data/dictionaries/unit_si.yaml +87 -0
- data/data/dictionaries/unit_type.yaml +113 -0
- data/lib/chemicalml/cml/array.rb +10 -0
- data/lib/chemicalml/cml/atom.rb +10 -0
- data/lib/chemicalml/cml/atom_array.rb +10 -0
- data/lib/chemicalml/cml/atom_parity.rb +10 -0
- data/lib/chemicalml/cml/base/array.rb +36 -0
- data/lib/chemicalml/cml/base/atom.rb +38 -0
- data/lib/chemicalml/cml/base/atom_array.rb +22 -0
- data/lib/chemicalml/cml/base/atom_parity.rb +24 -0
- data/lib/chemicalml/cml/base/bond.rb +28 -0
- data/lib/chemicalml/cml/base/bond_array.rb +22 -0
- data/lib/chemicalml/cml/base/bond_stereo.rb +28 -0
- data/lib/chemicalml/cml/base/cml_module.rb +40 -0
- data/lib/chemicalml/cml/base/dictionary.rb +32 -0
- data/lib/chemicalml/cml/base/dictionary_entry.rb +34 -0
- data/lib/chemicalml/cml/base/document.rb +26 -0
- data/lib/chemicalml/cml/base/formula.rb +38 -0
- data/lib/chemicalml/cml/base/identifier.rb +26 -0
- data/lib/chemicalml/cml/base/label.rb +28 -0
- data/lib/chemicalml/cml/base/list.rb +36 -0
- data/lib/chemicalml/cml/base/matrix.rb +38 -0
- data/lib/chemicalml/cml/base/metadata.rb +30 -0
- data/lib/chemicalml/cml/base/metadata_list.rb +28 -0
- data/lib/chemicalml/cml/base/molecule.rb +40 -0
- data/lib/chemicalml/cml/base/name.rb +26 -0
- data/lib/chemicalml/cml/base/parameter.rb +34 -0
- data/lib/chemicalml/cml/base/parameter_list.rb +28 -0
- data/lib/chemicalml/cml/base/product.rb +22 -0
- data/lib/chemicalml/cml/base/product_list.rb +22 -0
- data/lib/chemicalml/cml/base/property.rb +34 -0
- data/lib/chemicalml/cml/base/property_list.rb +28 -0
- data/lib/chemicalml/cml/base/reactant.rb +22 -0
- data/lib/chemicalml/cml/base/reactant_list.rb +22 -0
- data/lib/chemicalml/cml/base/reaction.rb +32 -0
- data/lib/chemicalml/cml/base/reaction_list.rb +22 -0
- data/lib/chemicalml/cml/base/scalar.rb +32 -0
- data/lib/chemicalml/cml/base/substance.rb +30 -0
- data/lib/chemicalml/cml/base/unit.rb +38 -0
- data/lib/chemicalml/cml/base/unit_list.rb +30 -0
- data/lib/chemicalml/cml/base/unit_type.rb +28 -0
- data/lib/chemicalml/cml/base/unit_type_list.rb +30 -0
- data/lib/chemicalml/cml/base.rb +51 -0
- data/lib/chemicalml/cml/bond.rb +10 -0
- data/lib/chemicalml/cml/bond_array.rb +10 -0
- data/lib/chemicalml/cml/bond_stereo.rb +10 -0
- data/lib/chemicalml/cml/cml_module.rb +10 -0
- data/lib/chemicalml/cml/dictionary.rb +10 -0
- data/lib/chemicalml/cml/dictionary_entry.rb +10 -0
- data/lib/chemicalml/cml/document.rb +10 -0
- data/lib/chemicalml/cml/elements.rb +58 -0
- data/lib/chemicalml/cml/formula.rb +10 -0
- data/lib/chemicalml/cml/identifier.rb +10 -0
- data/lib/chemicalml/cml/label.rb +10 -0
- data/lib/chemicalml/cml/list.rb +10 -0
- data/lib/chemicalml/cml/matrix.rb +10 -0
- data/lib/chemicalml/cml/metadata.rb +10 -0
- data/lib/chemicalml/cml/metadata_list.rb +10 -0
- data/lib/chemicalml/cml/molecule.rb +10 -0
- data/lib/chemicalml/cml/name.rb +10 -0
- data/lib/chemicalml/cml/namespace.rb +17 -0
- data/lib/chemicalml/cml/parameter.rb +10 -0
- data/lib/chemicalml/cml/parameter_list.rb +10 -0
- data/lib/chemicalml/cml/product.rb +10 -0
- data/lib/chemicalml/cml/product_list.rb +10 -0
- data/lib/chemicalml/cml/property.rb +10 -0
- data/lib/chemicalml/cml/property_list.rb +10 -0
- data/lib/chemicalml/cml/reactant.rb +10 -0
- data/lib/chemicalml/cml/reactant_list.rb +10 -0
- data/lib/chemicalml/cml/reaction.rb +10 -0
- data/lib/chemicalml/cml/reaction_list.rb +10 -0
- data/lib/chemicalml/cml/role/array.rb +13 -0
- data/lib/chemicalml/cml/role/atom.rb +13 -0
- data/lib/chemicalml/cml/role/atom_array.rb +13 -0
- data/lib/chemicalml/cml/role/atom_parity.rb +13 -0
- data/lib/chemicalml/cml/role/bond.rb +13 -0
- data/lib/chemicalml/cml/role/bond_array.rb +13 -0
- data/lib/chemicalml/cml/role/bond_stereo.rb +13 -0
- data/lib/chemicalml/cml/role/cml_module.rb +14 -0
- data/lib/chemicalml/cml/role/dictionary.rb +13 -0
- data/lib/chemicalml/cml/role/dictionary_entry.rb +13 -0
- data/lib/chemicalml/cml/role/document.rb +13 -0
- data/lib/chemicalml/cml/role/formula.rb +13 -0
- data/lib/chemicalml/cml/role/identifier.rb +13 -0
- data/lib/chemicalml/cml/role/label.rb +13 -0
- data/lib/chemicalml/cml/role/list.rb +13 -0
- data/lib/chemicalml/cml/role/matrix.rb +13 -0
- data/lib/chemicalml/cml/role/metadata.rb +13 -0
- data/lib/chemicalml/cml/role/metadata_list.rb +13 -0
- data/lib/chemicalml/cml/role/molecule.rb +13 -0
- data/lib/chemicalml/cml/role/name.rb +13 -0
- data/lib/chemicalml/cml/role/parameter.rb +13 -0
- data/lib/chemicalml/cml/role/parameter_list.rb +13 -0
- data/lib/chemicalml/cml/role/product.rb +13 -0
- data/lib/chemicalml/cml/role/product_list.rb +13 -0
- data/lib/chemicalml/cml/role/property.rb +13 -0
- data/lib/chemicalml/cml/role/property_list.rb +13 -0
- data/lib/chemicalml/cml/role/reactant.rb +13 -0
- data/lib/chemicalml/cml/role/reactant_list.rb +13 -0
- data/lib/chemicalml/cml/role/reaction.rb +13 -0
- data/lib/chemicalml/cml/role/reaction_list.rb +13 -0
- data/lib/chemicalml/cml/role/scalar.rb +13 -0
- data/lib/chemicalml/cml/role/substance.rb +13 -0
- data/lib/chemicalml/cml/role/unit.rb +13 -0
- data/lib/chemicalml/cml/role/unit_list.rb +13 -0
- data/lib/chemicalml/cml/role/unit_type.rb +13 -0
- data/lib/chemicalml/cml/role/unit_type_list.rb +13 -0
- data/lib/chemicalml/cml/role.rb +54 -0
- data/lib/chemicalml/cml/scalar.rb +10 -0
- data/lib/chemicalml/cml/schema24/configuration.rb +19 -0
- data/lib/chemicalml/cml/schema24.rb +40 -0
- data/lib/chemicalml/cml/schema3/configuration.rb +17 -0
- data/lib/chemicalml/cml/schema3.rb +40 -0
- data/lib/chemicalml/cml/substance.rb +10 -0
- data/lib/chemicalml/cml/translator/value_translations.rb +265 -0
- data/lib/chemicalml/cml/translator.rb +233 -0
- data/lib/chemicalml/cml/unit.rb +10 -0
- data/lib/chemicalml/cml/unit_list.rb +10 -0
- data/lib/chemicalml/cml/unit_type.rb +10 -0
- data/lib/chemicalml/cml/unit_type_list.rb +10 -0
- data/lib/chemicalml/cml/visitable.rb +65 -0
- data/lib/chemicalml/cml/wire_class_macro.rb +45 -0
- data/lib/chemicalml/cml.rb +55 -0
- data/lib/chemicalml/context_configuration.rb +127 -0
- data/lib/chemicalml/convention/base.rb +48 -0
- data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +33 -0
- data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +38 -0
- data/lib/chemicalml/convention/compchem/constraints.rb +14 -0
- data/lib/chemicalml/convention/compchem.rb +28 -0
- data/lib/chemicalml/convention/constraint.rb +79 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +33 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +34 -0
- data/lib/chemicalml/convention/dictionary/constraints.rb +14 -0
- data/lib/chemicalml/convention/dictionary.rb +28 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +30 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +42 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +54 -0
- data/lib/chemicalml/convention/molecular/constraints.rb +19 -0
- data/lib/chemicalml/convention/molecular.rb +29 -0
- data/lib/chemicalml/convention/registry.rb +50 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +46 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints.rb +12 -0
- data/lib/chemicalml/convention/unit_dictionary.rb +27 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +34 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +12 -0
- data/lib/chemicalml/convention/unit_type_dictionary.rb +27 -0
- data/lib/chemicalml/convention/violation.rb +36 -0
- data/lib/chemicalml/convention.rb +39 -0
- data/lib/chemicalml/dictionary/entry.rb +59 -0
- data/lib/chemicalml/dictionary/enum.rb +51 -0
- data/lib/chemicalml/dictionary/link.rb +32 -0
- data/lib/chemicalml/dictionary/loader.rb +60 -0
- data/lib/chemicalml/dictionary/model.rb +64 -0
- data/lib/chemicalml/dictionary/registry.rb +75 -0
- data/lib/chemicalml/dictionary.rb +26 -0
- data/lib/chemicalml/errors.rb +7 -0
- data/lib/chemicalml/model/atom.rb +40 -0
- data/lib/chemicalml/model/bond.rb +33 -0
- data/lib/chemicalml/model/cml_array.rb +27 -0
- data/lib/chemicalml/model/cml_module.rb +45 -0
- data/lib/chemicalml/model/document.rb +29 -0
- data/lib/chemicalml/model/formula.rb +34 -0
- data/lib/chemicalml/model/identifier.rb +21 -0
- data/lib/chemicalml/model/label.rb +21 -0
- data/lib/chemicalml/model/matrix.rb +29 -0
- data/lib/chemicalml/model/metadata.rb +23 -0
- data/lib/chemicalml/model/metadata_list.rb +25 -0
- data/lib/chemicalml/model/molecule.rb +38 -0
- data/lib/chemicalml/model/name.rb +21 -0
- data/lib/chemicalml/model/node.rb +45 -0
- data/lib/chemicalml/model/parameter.rb +27 -0
- data/lib/chemicalml/model/parameter_list.rb +25 -0
- data/lib/chemicalml/model/product.rb +21 -0
- data/lib/chemicalml/model/product_list.rb +21 -0
- data/lib/chemicalml/model/property.rb +28 -0
- data/lib/chemicalml/model/property_list.rb +25 -0
- data/lib/chemicalml/model/reactant.rb +21 -0
- data/lib/chemicalml/model/reactant_list.rb +21 -0
- data/lib/chemicalml/model/reaction.rb +41 -0
- data/lib/chemicalml/model/reaction_list.rb +23 -0
- data/lib/chemicalml/model/scalar.rb +25 -0
- data/lib/chemicalml/model/substance.rb +25 -0
- data/lib/chemicalml/model.rb +38 -0
- data/lib/chemicalml/schema/definition.rb +41 -0
- data/lib/chemicalml/schema/registry.rb +30 -0
- data/lib/chemicalml/schema.rb +35 -0
- data/lib/chemicalml/version.rb +5 -0
- data/lib/chemicalml/versioned_parser.rb +43 -0
- data/lib/chemicalml.rb +47 -0
- data/lib/tasks/reference_fetch.rake +105 -0
- data/lib/tasks/scrape_fixtures.rake +133 -0
- data/reference-docs/conventions/compchem.md +141 -0
- data/reference-docs/conventions/dictionary.md +119 -0
- data/reference-docs/conventions/index.md +56 -0
- data/reference-docs/conventions/molecular.md +115 -0
- data/reference-docs/conventions/unit-dictionary.md +92 -0
- data/reference-docs/conventions/unitType-dictionary.md +51 -0
- data/reference-docs/dictionaries/compchem.md +335 -0
- data/reference-docs/dictionaries/index.md +52 -0
- data/reference-docs/examples/schema24-index.md +50 -0
- data/reference-docs/examples/schema3-compchem-index.md +14 -0
- data/reference-docs/examples/schema3-molecular-index.md +28 -0
- data/reference-docs/schemas/schema24/schema.xsd +10624 -0
- data/reference-docs/schemas/schema3/schema.xsd +11459 -0
- metadata +306 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
# CML model layer. The wire classes live under `Schema3::` and
|
|
5
|
+
# `Schema24::` namespaces; the top-level `Cml::*` constants are
|
|
6
|
+
# backward-compatible aliases pointing at the Schema3 versions.
|
|
7
|
+
# Shared attribute + xml-mapping declarations live under `Base::*`
|
|
8
|
+
# and are included by both schema-versioned class hierarchies.
|
|
9
|
+
module Cml
|
|
10
|
+
autoload :Array, "chemicalml/cml/array"
|
|
11
|
+
autoload :Atom, "chemicalml/cml/atom"
|
|
12
|
+
autoload :AtomArray, "chemicalml/cml/atom_array"
|
|
13
|
+
autoload :AtomParity, "chemicalml/cml/atom_parity"
|
|
14
|
+
autoload :Base, "chemicalml/cml/base"
|
|
15
|
+
autoload :Bond, "chemicalml/cml/bond"
|
|
16
|
+
autoload :BondArray, "chemicalml/cml/bond_array"
|
|
17
|
+
autoload :BondStereo, "chemicalml/cml/bond_stereo"
|
|
18
|
+
autoload :Dictionary, "chemicalml/cml/dictionary"
|
|
19
|
+
autoload :DictionaryEntry, "chemicalml/cml/dictionary_entry"
|
|
20
|
+
autoload :Document, "chemicalml/cml/document"
|
|
21
|
+
autoload :Elements, "chemicalml/cml/elements"
|
|
22
|
+
autoload :Formula, "chemicalml/cml/formula"
|
|
23
|
+
autoload :Identifier, "chemicalml/cml/identifier"
|
|
24
|
+
autoload :Label, "chemicalml/cml/label"
|
|
25
|
+
autoload :List, "chemicalml/cml/list"
|
|
26
|
+
autoload :Matrix, "chemicalml/cml/matrix"
|
|
27
|
+
autoload :Metadata, "chemicalml/cml/metadata"
|
|
28
|
+
autoload :MetadataList, "chemicalml/cml/metadata_list"
|
|
29
|
+
autoload :Module, "chemicalml/cml/cml_module"
|
|
30
|
+
autoload :Molecule, "chemicalml/cml/molecule"
|
|
31
|
+
autoload :Name, "chemicalml/cml/name"
|
|
32
|
+
autoload :Namespace, "chemicalml/cml/namespace"
|
|
33
|
+
autoload :Parameter, "chemicalml/cml/parameter"
|
|
34
|
+
autoload :ParameterList, "chemicalml/cml/parameter_list"
|
|
35
|
+
autoload :Product, "chemicalml/cml/product"
|
|
36
|
+
autoload :ProductList, "chemicalml/cml/product_list"
|
|
37
|
+
autoload :Property, "chemicalml/cml/property"
|
|
38
|
+
autoload :PropertyList, "chemicalml/cml/property_list"
|
|
39
|
+
autoload :Reaction, "chemicalml/cml/reaction"
|
|
40
|
+
autoload :ReactionList, "chemicalml/cml/reaction_list"
|
|
41
|
+
autoload :Reactant, "chemicalml/cml/reactant"
|
|
42
|
+
autoload :ReactantList, "chemicalml/cml/reactant_list"
|
|
43
|
+
autoload :Role, "chemicalml/cml/role"
|
|
44
|
+
autoload :Scalar, "chemicalml/cml/scalar"
|
|
45
|
+
autoload :Schema3, "chemicalml/cml/schema3"
|
|
46
|
+
autoload :Schema24, "chemicalml/cml/schema24"
|
|
47
|
+
autoload :Substance, "chemicalml/cml/substance"
|
|
48
|
+
autoload :Translator, "chemicalml/cml/translator"
|
|
49
|
+
autoload :Unit, "chemicalml/cml/unit"
|
|
50
|
+
autoload :UnitList, "chemicalml/cml/unit_list"
|
|
51
|
+
autoload :UnitType, "chemicalml/cml/unit_type"
|
|
52
|
+
autoload :UnitTypeList, "chemicalml/cml/unit_type_list"
|
|
53
|
+
autoload :Visitable, "chemicalml/cml/visitable"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
# Shared behaviour for versioned schema configurations. Each schema
|
|
5
|
+
# version (`Cml::Schema3`, `Cml::Schema24`) extends this module via
|
|
6
|
+
# its own `Configuration` sub-module, giving it:
|
|
7
|
+
#
|
|
8
|
+
# - `context_id` — the lutaml-model `GlobalContext` id
|
|
9
|
+
# - `register_model(klass, id:)` — record one wire class
|
|
10
|
+
# - `populate_context!` — rebuild the type registry on demand
|
|
11
|
+
# - `ensure_registered!` — lazily populate on first parse
|
|
12
|
+
#
|
|
13
|
+
# Mirrors `Mml::ContextConfiguration`. Adapted to Chemicalml's
|
|
14
|
+
# schema-registry terminology.
|
|
15
|
+
module ContextConfiguration
|
|
16
|
+
def context_id
|
|
17
|
+
self::CONTEXT_ID
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def context
|
|
21
|
+
Lutaml::Model::GlobalContext.context(context_id)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ensure_registered!
|
|
25
|
+
return if @models_registered
|
|
26
|
+
|
|
27
|
+
register_models!
|
|
28
|
+
@models_registered = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Default: subclasses override with a centralized registration
|
|
32
|
+
# block (V4-style). The fallback eager-loads every autoloaded
|
|
33
|
+
# constant on the parent module.
|
|
34
|
+
def register_models!
|
|
35
|
+
parent = version_parent_module
|
|
36
|
+
parent.constants.each { |name| parent.const_get(name) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Walk the shared `Chemicalml::Cml::Elements::ALL` table, look up
|
|
40
|
+
# each class on the parent (schema) module, and register it. Pass
|
|
41
|
+
# `except:` to skip specific elements (used by Schema 2.4 which
|
|
42
|
+
# lacks `<module>`).
|
|
43
|
+
def register_elements!(except: [])
|
|
44
|
+
parent = version_parent_module
|
|
45
|
+
skip = except.to_set
|
|
46
|
+
Chemicalml::Cml::Elements::ALL.each do |class_name, element_id|
|
|
47
|
+
next if skip.include?(class_name)
|
|
48
|
+
next unless parent.const_defined?(class_name)
|
|
49
|
+
|
|
50
|
+
register_model(parent.const_get(class_name), id: element_id)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def register_model(klass, id:)
|
|
55
|
+
normalized_id = id.to_sym
|
|
56
|
+
registered_models[normalized_id] = klass
|
|
57
|
+
target_context = context || create_base_context
|
|
58
|
+
target_context.registry.register(normalized_id, klass)
|
|
59
|
+
klass
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def populate_context!
|
|
63
|
+
ensure_version_registered
|
|
64
|
+
Lutaml::Model::GlobalContext.unregister_context(context_id) if context
|
|
65
|
+
register_models_in(base_type_context)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def create_context(id:, registry: nil, fallback_to: [context_id])
|
|
69
|
+
normalized_id = id.to_sym
|
|
70
|
+
ensure_version_registered
|
|
71
|
+
Lutaml::Model::GlobalContext.unregister_context(normalized_id) if Lutaml::Model::GlobalContext.context(normalized_id)
|
|
72
|
+
Lutaml::Model::GlobalContext.create_context(
|
|
73
|
+
id: normalized_id,
|
|
74
|
+
registry: registry || Lutaml::Model::TypeRegistry.new,
|
|
75
|
+
fallback_to: Array(fallback_to).map(&:to_sym)
|
|
76
|
+
).tap { Lutaml::Model::GlobalContext.clear_caches }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def default_context_id
|
|
80
|
+
context_id
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def registered_models
|
|
86
|
+
@registered_models ||= {}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def create_base_context
|
|
90
|
+
Lutaml::Model::GlobalContext.create_context(
|
|
91
|
+
id: context_id,
|
|
92
|
+
registry: Lutaml::Model::TypeRegistry.new,
|
|
93
|
+
fallback_to: [:default]
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def base_type_context
|
|
98
|
+
create_base_context
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def register_models_in(type_context)
|
|
102
|
+
registered_models.each do |model_id, klass|
|
|
103
|
+
type_context.registry.register(model_id, klass)
|
|
104
|
+
end
|
|
105
|
+
Lutaml::Model::GlobalContext.clear_caches
|
|
106
|
+
type_context
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def ensure_version_registered
|
|
110
|
+
parent = version_parent_module
|
|
111
|
+
return unless parent
|
|
112
|
+
|
|
113
|
+
parent.ensure_registered!
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def version_parent_module
|
|
117
|
+
return nil unless name
|
|
118
|
+
|
|
119
|
+
parent_const_name = name.split("::")[0..-2].join("::")
|
|
120
|
+
return nil if parent_const_name.empty?
|
|
121
|
+
|
|
122
|
+
Object.const_get(parent_const_name)
|
|
123
|
+
rescue NameError
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
# Mixin that gives a convention module its registry behavior:
|
|
6
|
+
#
|
|
7
|
+
# - `qname` / `namespace_uri` (must be implemented by the host)
|
|
8
|
+
# - `constraints` (lazy array)
|
|
9
|
+
# - `register(SomeConstraintClass)`
|
|
10
|
+
# - `validate(document)` -> [Violation, ...]
|
|
11
|
+
#
|
|
12
|
+
# The framework never inspects which constraints a convention has;
|
|
13
|
+
# it just walks whatever was registered. Adding a new rule = a new
|
|
14
|
+
# constraint subclass + a `register` call.
|
|
15
|
+
module Base
|
|
16
|
+
def qname
|
|
17
|
+
raise NotImplementedError, "#{name} must define its QName"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def namespace_uri
|
|
21
|
+
raise NotImplementedError, "#{name} must define its namespace URI"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def constraints
|
|
25
|
+
@constraints ||= []
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def register(constraint_class)
|
|
29
|
+
constraints << constraint_class unless constraints.include?(constraint_class)
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def validate(document)
|
|
34
|
+
constraints.flat_map do |klass|
|
|
35
|
+
klass.new.check(document)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def constraint_count
|
|
40
|
+
constraints.length
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def reset_constraints!
|
|
44
|
+
@constraints = []
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Compchem
|
|
6
|
+
module Constraints
|
|
7
|
+
# A CompChem module MUST contain at least one jobList module
|
|
8
|
+
# child per the compchem convention.
|
|
9
|
+
class CompchemModuleMustContainJobList < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
def check_node(node, path)
|
|
11
|
+
return [] unless compchem_root?(node)
|
|
12
|
+
|
|
13
|
+
job_lists = node.modules.select { |m| m.dict_ref == "compchem:jobList" }
|
|
14
|
+
return [] unless job_lists.empty?
|
|
15
|
+
|
|
16
|
+
[violation(path: path.join("/"),
|
|
17
|
+
message: "compchem module must contain at least one jobList module")]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def compchem_root?(node)
|
|
23
|
+
module?(node) && node.convention == "convention:compchem"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def module?(node)
|
|
27
|
+
node.is_a?(Chemicalml::Cml::Role::Module)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Compchem
|
|
6
|
+
module Constraints
|
|
7
|
+
# A job module MUST contain exactly one initialization module
|
|
8
|
+
# child per the compchem convention.
|
|
9
|
+
class JobMustContainInitialization < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
def check_node(node, path)
|
|
11
|
+
return [] unless job_module?(node)
|
|
12
|
+
|
|
13
|
+
modules = node.modules || []
|
|
14
|
+
inits = modules.select { |m| m.dict_ref == "compchem:initialization" }
|
|
15
|
+
return [] if inits.length == 1
|
|
16
|
+
|
|
17
|
+
message = if inits.empty?
|
|
18
|
+
"job module must contain exactly one initialization module (found 0)"
|
|
19
|
+
else
|
|
20
|
+
"job module must contain exactly one initialization module (found #{inits.length})"
|
|
21
|
+
end
|
|
22
|
+
[violation(path: path.join("/"), message: message)]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def job_module?(node)
|
|
28
|
+
module?(node) && node.dict_ref == "compchem:job"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def module?(node)
|
|
32
|
+
node.is_a?(Chemicalml::Cml::Role::Module)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Compchem
|
|
6
|
+
module Constraints
|
|
7
|
+
autoload :CompchemModuleMustContainJobList,
|
|
8
|
+
"chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list"
|
|
9
|
+
autoload :JobMustContainInitialization,
|
|
10
|
+
"chemicalml/convention/compchem/constraints/job_must_contain_initialization"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
# The CompChem convention. Constraints based on
|
|
6
|
+
# http://www.xml-cml.org/convention/compchem (archived in
|
|
7
|
+
# `reference-docs/conventions/compchem.md`).
|
|
8
|
+
module Compchem
|
|
9
|
+
extend Base
|
|
10
|
+
|
|
11
|
+
autoload :Constraints, "chemicalml/convention/compchem/constraints"
|
|
12
|
+
|
|
13
|
+
QNAME = "convention:compchem".freeze
|
|
14
|
+
NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}compchem".freeze
|
|
15
|
+
|
|
16
|
+
def self.qname
|
|
17
|
+
QNAME
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.namespace_uri
|
|
21
|
+
NAMESPACE_URI
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
register Constraints::CompchemModuleMustContainJobList
|
|
25
|
+
register Constraints::JobMustContainInitialization
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
# Abstract base class for a single constraint. Subclasses implement
|
|
6
|
+
# `check_node` (one node at a time) or `check_document` (whole
|
|
7
|
+
# document at once). The base class provides traversal and
|
|
8
|
+
# collection plumbing.
|
|
9
|
+
#
|
|
10
|
+
# To add a new constraint:
|
|
11
|
+
#
|
|
12
|
+
# 1. Subclass `Constraint::NodeConstraint` or
|
|
13
|
+
# `Constraint::DocumentConstraint`
|
|
14
|
+
# 2. Implement `check_node` / `check_document`
|
|
15
|
+
# 3. Register it on the convention: `register(MyConstraint)`
|
|
16
|
+
#
|
|
17
|
+
# No framework code changes.
|
|
18
|
+
class Constraint
|
|
19
|
+
def self.name_for_display
|
|
20
|
+
to_s.split("::").last
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def check(_document)
|
|
24
|
+
raise NotImplementedError,
|
|
25
|
+
"#{self.class.name_for_display} must implement #check(document)"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
|
|
30
|
+
def violation(path:, message:, severity: :error)
|
|
31
|
+
Violation.new(path: path, message: message,
|
|
32
|
+
severity: severity, constraint: self.class)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Walk every wire node reachable from `node`. Yields (node, path)
|
|
36
|
+
# pairs. Path is a slash-joined breadcrumb. Nodes that don't
|
|
37
|
+
# include `Chemicalml::Cml::Visitable` are skipped.
|
|
38
|
+
def walk_nodes(node, path = [], &block)
|
|
39
|
+
return unless node
|
|
40
|
+
return unless visitable?(node)
|
|
41
|
+
|
|
42
|
+
yield(node, path)
|
|
43
|
+
node.wire_children.each do |child|
|
|
44
|
+
walk_nodes(child, path + [describe(child)], &block)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def visitable?(node)
|
|
49
|
+
node.is_a?(Chemicalml::Cml::Visitable)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def describe(node)
|
|
53
|
+
id_value = node.node_id
|
|
54
|
+
id_value ? "#{node.element_name}[#{id_value}]" : node.element_name
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Constraint that walks the document tree once per node and calls
|
|
58
|
+
# `check_node(node, path)`. Subclasses get traversal for free.
|
|
59
|
+
class NodeConstraint < Constraint
|
|
60
|
+
def check(document)
|
|
61
|
+
violations = []
|
|
62
|
+
walk_nodes(document) do |node, path|
|
|
63
|
+
violations.concat(Array(check_node(node, path)))
|
|
64
|
+
end
|
|
65
|
+
violations
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def check_node(_node, _path)
|
|
69
|
+
[]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Constraint that operates on the whole document at once — used
|
|
74
|
+
# for cross-cutting rules like "atom ids unique within molecule".
|
|
75
|
+
class DocumentConstraint < Constraint
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Dictionary
|
|
6
|
+
module Constraints
|
|
7
|
+
# Entry ids MUST be unique within the parent dictionary per
|
|
8
|
+
# the dictionary convention.
|
|
9
|
+
class EntryIdsUniqueWithinDictionary < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
def check_node(node, path)
|
|
11
|
+
return [] unless dictionary?(node)
|
|
12
|
+
|
|
13
|
+
entries = node.entries || []
|
|
14
|
+
ids = entries.map { |e| e.id }.compact
|
|
15
|
+
duplicates = ids.group_by { |x| x }.select { |_, v| v.length > 1 }.keys
|
|
16
|
+
return [] if duplicates.empty?
|
|
17
|
+
|
|
18
|
+
duplicates.map do |dup|
|
|
19
|
+
violation(path: path.join("/"),
|
|
20
|
+
message: "duplicate entry id #{dup.inspect} within dictionary")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def dictionary?(node)
|
|
27
|
+
node.is_a?(Chemicalml::Cml::Role::Dictionary)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Dictionary
|
|
6
|
+
module Constraints
|
|
7
|
+
# Every entry MUST have an `id` and a `term` attribute per the
|
|
8
|
+
# dictionary convention.
|
|
9
|
+
class EntryMustHaveIdAndTerm < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
def check_node(node, path)
|
|
11
|
+
return [] unless entry?(node)
|
|
12
|
+
|
|
13
|
+
violations = []
|
|
14
|
+
if node.id.to_s.empty?
|
|
15
|
+
violations << violation(path: path.join("/"),
|
|
16
|
+
message: "entry must have an id attribute")
|
|
17
|
+
end
|
|
18
|
+
if node.term.to_s.empty?
|
|
19
|
+
violations << violation(path: path.join("/"),
|
|
20
|
+
message: "entry must have a term attribute")
|
|
21
|
+
end
|
|
22
|
+
violations
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def entry?(node)
|
|
28
|
+
node.is_a?(Chemicalml::Cml::Role::DictionaryEntry)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Dictionary
|
|
6
|
+
module Constraints
|
|
7
|
+
autoload :EntryMustHaveIdAndTerm,
|
|
8
|
+
"chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term"
|
|
9
|
+
autoload :EntryIdsUniqueWithinDictionary,
|
|
10
|
+
"chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
# The dictionary convention. Constraints based on
|
|
6
|
+
# http://www.xml-cml.org/convention/dictionary (archived in
|
|
7
|
+
# `reference-docs/conventions/dictionary.md`).
|
|
8
|
+
module Dictionary
|
|
9
|
+
extend Base
|
|
10
|
+
|
|
11
|
+
autoload :Constraints, "chemicalml/convention/dictionary/constraints"
|
|
12
|
+
|
|
13
|
+
QNAME = "convention:dictionary".freeze
|
|
14
|
+
NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}dictionary".freeze
|
|
15
|
+
|
|
16
|
+
def self.qname
|
|
17
|
+
QNAME
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.namespace_uri
|
|
21
|
+
NAMESPACE_URI
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
register Constraints::EntryMustHaveIdAndTerm
|
|
25
|
+
register Constraints::EntryIdsUniqueWithinDictionary
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# `atomArray` MUST contain at least one `atom` child per the
|
|
8
|
+
# molecular convention.
|
|
9
|
+
class AtomArrayMustContainAtoms < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
def check_node(node, path)
|
|
11
|
+
return [] unless molecule?(node)
|
|
12
|
+
return [] unless node.atom_array
|
|
13
|
+
|
|
14
|
+
atoms = node.atom_array.atoms || []
|
|
15
|
+
return [] unless atoms.empty?
|
|
16
|
+
|
|
17
|
+
[violation(path: path.empty? ? "molecule" : path.join("/"),
|
|
18
|
+
message: "atomArray must contain at least one atom")]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def molecule?(node)
|
|
24
|
+
node.is_a?(Chemicalml::Cml::Role::Molecule)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# Atom ids MUST be unique within the eldest containing molecule
|
|
8
|
+
# per the molecular convention.
|
|
9
|
+
class AtomIdsUniqueWithinMolecule < Chemicalml::Convention::Constraint::DocumentConstraint
|
|
10
|
+
def check(document)
|
|
11
|
+
violations = []
|
|
12
|
+
walk_nodes(document) do |node, path|
|
|
13
|
+
next unless molecule?(node)
|
|
14
|
+
|
|
15
|
+
ids = atom_ids_within(node)
|
|
16
|
+
duplicates = ids.group_by { |x| x }.select { |_, v| v.length > 1 }.keys
|
|
17
|
+
duplicates.each do |dup|
|
|
18
|
+
violations << violation(
|
|
19
|
+
path: path.empty? ? "molecule[#{node.id}]" : path.join("/"),
|
|
20
|
+
message: "duplicate atom id #{dup.inspect} within molecule #{node.id.inspect}"
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
violations
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def molecule?(node)
|
|
30
|
+
node.is_a?(Chemicalml::Cml::Role::Molecule)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def atom_ids_within(molecule)
|
|
34
|
+
return [] unless molecule.atom_array
|
|
35
|
+
|
|
36
|
+
(molecule.atom_array.atoms || []).map { |a| a.id }.compact
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chemicalml
|
|
4
|
+
module Convention
|
|
5
|
+
module Molecular
|
|
6
|
+
module Constraints
|
|
7
|
+
# Bonds MUST reference atoms within the same parent molecule
|
|
8
|
+
# per the molecular convention.
|
|
9
|
+
class BondMustReferenceAtomsInSameMolecule < Chemicalml::Convention::Constraint::DocumentConstraint
|
|
10
|
+
def check(document)
|
|
11
|
+
violations = []
|
|
12
|
+
walk_nodes(document) do |node, path|
|
|
13
|
+
next unless molecule?(node)
|
|
14
|
+
|
|
15
|
+
known = atom_ids_within(node).to_set
|
|
16
|
+
bonds_within(node).each do |bond|
|
|
17
|
+
missing = atom_refs_of(bond).reject { |r| known.include?(r) }
|
|
18
|
+
next if missing.empty?
|
|
19
|
+
|
|
20
|
+
violations << violation(
|
|
21
|
+
path: path.empty? ? "molecule[#{node.id}]" : path.join("/"),
|
|
22
|
+
message: "bond #{bond.id.inspect} references atoms not in the same molecule: #{missing.inspect}"
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
violations
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def molecule?(node)
|
|
32
|
+
node.is_a?(Chemicalml::Cml::Role::Molecule)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def atom_ids_within(molecule)
|
|
36
|
+
return [] unless molecule.atom_array
|
|
37
|
+
|
|
38
|
+
(molecule.atom_array.atoms || []).map { |a| a.id }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def bonds_within(molecule)
|
|
42
|
+
return [] unless molecule.bond_array
|
|
43
|
+
|
|
44
|
+
molecule.bond_array.bonds || []
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def atom_refs_of(bond)
|
|
48
|
+
bond.atom_refs2.to_s.split(" ")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|