Public API

The supported public API is stoms.atomsledger. SQLite and PostgreSQL are supported backends. Import public names from this module rather than implementation modules.

from stoms.atomsledger import AtomsLedger, SQLiteRepository

ledger = AtomsLedger("study.atomsledger")

AtomsLedger() uses atomsledger.sqlite. AtomsLedger.open(path) is a path constructor. Passing a Repository implementation is supported at the service boundary, but SQLiteRepository is the only supported concrete backend.

AtomsLedger.open(path, read_only=True) (or SQLiteRepository(path, read_only=True)) opens an existing ledger so that SQLite refuses every write; a missing file or a SQLite database that is not a ledger raises IntegrityError. stoms serve and the stoms aggregations commands open ledgers this way.

Domain values

Public value Purpose
Provenance Required created_by and purpose; optional immutable tags, notes, and strict JSON metadata.
StructureSourceSpec Immutable description of a structure-producing process.
MethodSpec Description of an evaluation method; method_identifier is its ordinary matching identity.
StructureInput, EvaluationInput, TrajectoryFrame Batch ingestion and trajectory inputs.
StructureRecord, EvaluationRecord, TrajectoryRecord Immutable persisted records.
StoredQuery, StoredQuerySummary Immutable query definitions and listing views.
Aggregation, AggregationMember, AggregationMemberInput, AggregationSummary Immutable ordered memberships and views.

StructureID, StoredQueryID, and AggregationID are string aliases. The library creates type-prefixed IDs such as str_, eval_, traj_, query_, and agg_.

Public imports

stoms.atomsledger.__all__ exports the following names:

Group Names
Service and storage AtomsLedger, SQLiteRepository, SQLiteCatalog
Records and views StructureRecord, EvaluationRecord, TrajectoryRecord, StoredQuery, StoredQuerySummary, Aggregation, AggregationSummary, AggregationMember, StructureContentMatch, StructureContentConflict, StructureCatalogEntry, EvaluatorCatalogEntry, SelectedRecord, NumericalProjection, StrictSelection
Inputs and query values StructureInput, EvaluationInput, TrajectoryFrame, AggregationMemberInput, Provenance, StructureSourceSpec, MethodSpec, StructureQuery, EvaluationQuery, DatasetQuery, EvaluatorSelector
Type aliases StructureID, StoredQueryID, AggregationID, JSONValue
Transfer TransferReport, transfer_repository, verify_repository_subset, export_sqlite, import_sqlite
Errors and warning AtomsLedgerError, RecordNotFoundError, DuplicateRecordError, IntegrityError, StrictSelectionError, StructureContentConflictError, StructureContentMismatchError, AggregationConflictError, AggregationNotFoundError, AggregationValidationError, LedgerOverwriteError, StoredQueryInUseError, SerializationError, EquivalentAtomsWarning

Ingest and read

ledger.add_structure(atoms, source_spec, provenance, *, allow_equivalent_atoms=False)
ledger.add_structures(items, *, source_spec, provenance=None, allow_equivalent_atoms=False)
ledger.add_evaluation(structure_id, *, method, properties, units=None, provenance)
ledger.add_evaluations(items)
ledger.add_trajectory(*, kind, frames, provenance)

ledger.load_atoms(structure_id)
ledger.load_evaluation(evaluation_id)
ledger.validate_structure_content(structure_id, atoms)
ledger.find_structures(query=None)
ledger.find_evaluations(query=None)
ledger.list_method_identifiers()

add_structure returns a record ID or StructureContentMatch values. add_structures returns IDs or raises StructureContentConflictError atomically. Evaluation ingestion requires one or more of energy, forces, or node_energy. Their accepted shapes are ()/(1,), (n, 3), and (n,); their default units are eV, eV/angstrom, and eV.

Query, catalog, and tags

StructureQuery(...)
EvaluationQuery(...)
DatasetQuery(structures=..., evaluations=..., require_evaluation=False)
EvaluatorSelector(method, exact_match=False)

ledger.catalog.search(query=None)
ledger.catalog.iter_search(query=None)
ledger.add_tags(record_id, tags)
ledger.add_tags_many(records)
ledger.get_tags(record_id)

StructureQuery filters by ID, content hash, formula, effective tags, purpose, and time. EvaluationQuery filters by ID, structure ID, method family/name/ version/code/identifier, property presence, effective tags, and time. DatasetQuery.require_evaluation does not select an evaluator for strict selection.

The catalog returns StructureCatalogEntry values with evaluator metadata, property names, and units without loading structure or numerical payloads. Tags are append-only and supported for structures and evaluations.

Strict selection and extxyz

selection = ledger.select(query=None, *, evaluator=None, exact_match=False)
selection.project(property_name)
selection.project_energy()

ledger.to_extxyz(destination, *, dataset=None, evaluator=None, selection=None,
                 aggregation_id=None)
ledger.export_extxyz(destination, *, query=None, template=None, dataset=None,
                     evaluator=None, selection=None, aggregation_id=None,
                     include_node_energy=False)

select accepts a DatasetQuery, StructureQuery, StoredQuery, stored-query ID, or None. A string is an ID, not a stored-query label. Evaluators can be a method name, MethodSpec, EvaluationQuery, or EvaluatorSelector. Selection raises StrictSelectionError unless every selected structure has exactly one matching evaluation.

Pass aggregation_id to export its members in their immutable stored order. Each member's attached evaluation ID is used directly; it cannot be combined with a query, template, dataset, evaluator, or explicit selection.

StrictSelection.project returns NumericalProjection with aligned IDs and a read-only NumPy array. Values must be numeric, have one declared unit, and share a shape.

Stored queries and aggregations

ledger.create_stored_query(*, label, query, provenance, description="", hidden_at=None)
ledger.list_stored_queries()
ledger.get_stored_query(query_id)
ledger.hide_stored_query(query_id)
ledger.delete_stored_query(query_id, *, force=False)

ledger.create_aggregation(*, name, members, description=None, metadata=None,
                          external_key=None, supersedes_id=None)
ledger.materialize_stored_query(*, stored_query_id, name, description=None,
                                metadata=None, external_key=None)
ledger.get_aggregation(aggregation_id, *, include_members=True)
ledger.get_aggregation_by_external_key(external_key, *, include_members=True)
ledger.list_aggregations(*, name=None, source_stored_query_id=None,
                         metadata_filter=None)
ledger.get_aggregation_structure_ids(aggregation_id)
ledger.get_aggregation_structures(aggregation_id)

Stored-query labels are nonunique. Hiding changes visibility but not the query. Deleting a query referenced by an aggregation raises StoredQueryInUseError unless force=True. Aggregation members contain a required structure ID and an optional evaluation ID. Membership is immutable. external_key permits idempotent recreation only with matching membership and compatible fields.

Transfer

ledger.transfer_to(target, *, conflict="verify", batch_size=256)
ledger.export_database(destination, *, overwrite=False, batch_size=256)
ledger.export_aggregation(aggregation_id, destination, *, overwrite=False,
                          batch_size=256)
ledger.import_database(source, *, conflict="verify", batch_size=256)

transfer_repository(source, target, *, conflict="verify", batch_size=256)
verify_repository_subset(source, target)
export_sqlite(source, destination, *, overwrite=False, batch_size=256)
export_aggregation_sqlite(source, aggregation_id, destination, *,
                          overwrite=False, batch_size=256)
import_sqlite(source, target, *, conflict="verify", batch_size=256)

Conflict policy is error, skip, or verify. verify accepts an existing ID only if its immutable fingerprint matches. Transfer is additive and never rewrites or deletes destination records.

The exports never write to the source ledger's own file, even with overwrite=True (also through a symbolic or hard link), and an extxyz export (to_extxyz, export_extxyz) never replaces any ledger or other SQLite database: both raise LedgerOverwriteError. Other existing files are replaced by an extxyz export, and by a SQLite export only with overwrite=True.

export_aggregation writes a new SQLite ledger with one aggregation, its member structures, and the evaluation paired with each member; other evaluations of those structures are left out. IDs, timestamps, provenance, and tags are preserved, and a source stored query is copied as a definition. An aggregation with supersedes_id raises AggregationValidationError.

Errors and warnings

Public errors derive from AtomsLedgerError: RecordNotFoundError, DuplicateRecordError, IntegrityError, StrictSelectionError, StructureContentConflictError, StructureContentMismatchError, AggregationConflictError, AggregationNotFoundError, AggregationValidationError, LedgerOverwriteError, StoredQueryInUseError, and SerializationError. Reused or equivalent structure IDs issue EquivalentAtomsWarning.

Ring analysis

stoms.analysis.find_rings finds the rings of an atomic network in any ASE Atoms; the structure viewer's Rings tab uses it.

from stoms.analysis import find_rings

result = find_rings(
    atoms,
    heights={"Cu": (10.0, 11.5), "O": (10.0, 11.5)},  # along a x b, Angstrom
    cutoffs={"Cu-O": 2.3},  # others: 1.15 x covalent radii, none metal-metal
    count="auto",  # "auto", "pairs", "all", or an element symbol
    method="planar",  # or "shortest-path" for 3D networks
)
result.signature  # "4,4,4,4,8,8,8,8+1Cu"
result.counts()  # {4: 4, 8: 4}

elements limits the network to some elements. alternating=True bonds only atoms of different elements. The planar method traces the faces of the network drawn in the a-b plane, for surfaces, layers and 2D materials. It reports faces that wrap around the cell as open_faces, and the atoms that each ring encloses. When network atoms lie on top of each other in the a-b plane, its rings may be wrong, and result.warnings says so. The shortest-path method finds Guttman rings up to max_size atoms in any network, including bulk and amorphous 3D structures. "auto" counts atom pairs when every bond joins two different elements, and atoms otherwise; a ring with an odd number of atoms counts half of them, rounded up, as pairs. Each Ring has its size, label, atoms and periodic images, its corner points moved into the cell, and, for the planar method, its area and the atoms inside it.

Notebook viewer

stoms.viewer shows ASE structures in the viewer, in Jupyter or a browser, without a ledger. The link to Python needs the optional jupyter dependency (anywidget); see ASE structures in Jupyter.

from stoms.viewer import view, view_phonons

viewer = view(images)                # Atoms, a list, a Trajectory or a file name
viewer.frame, viewer.settings, viewer.camera   # synced with the browser
viewer.colour_by("charges")          # also show_cell, show_bonds, show_forces, ...
viewer.add_plane(1, 1, 1)            # also slice, hidden_elements, element_colours, bond_cutoffs, ...
viewer.save("figure.svg")            # the SVG the browser drew; or ".html"
viewer.get_settings()                # settings, view and measurements, for view(..., settings=...)
viewer.measurements, viewer.selected_atoms
view_phonons("phonopy_params.yaml")  # with the phonopy dependency

Public names: view, view_phonons, Viewer (the widget), StaticView (HTML output without a link to Python) and StaleViewError, raised by save() when the browser has not drawn the current state.