Skip to content

Storing Data

A Store keeps retrieved source files, intermediate data created by SOPRAN, and analysis results you adopt under one root, separated into layers. It is separate from the Project workspace, so several Projects and machines can share one Store. Choosing its location is described in Configuration, and directories and file formats in Store Layout.

store = spn.Store("/path/to/sopran_data")

What goes in each layer

Layer Contents Created by
raw Source files from providers, with provider names and directory structure unchanged Downloads
normalized Standard instrument quantities (counts, energy flux, magnetic field, …) as Parquet Pipeline, write_parquet
features Quantities derived by SOPRAN (orbit geometry, pitch-angle spectra, data availability summaries, …) Caches, write_parquet
models Calibration and model-fit results Experimental APIs, write_parquet
databases Tables adopted per user or research topic (event lists, …) store.database, store.event_catalog
cache Temporary files that can be regenerated Internal
registry Search indexes Automatic

Each file in raw/ has a sidecar JSON (<filename>.sopran.json) with its source URL, checksum, and retrieval time.

Caching avoids recomputation

Orbit geometry, field-line connection, LRS spectra, pitch-angle spectra, and similar products can be kept in the Store with cache=.

cache Behavior
"use" (default) Read a result made with the same arguments if present; otherwise compute and save
"refresh" Recompute and overwrite
"never" Compute on the spot without saving

Results go under the dataset in the features layer (or similar), in variants/<variant_id>/ per set of conditions. variant_id is derived from the arguments and the content of the input files, so results made under different conditions never get mixed up. Results loaded with missing="empty" or "warn" while some source files were missing are incomplete and are not cached.

Summarize data availability

Each endpoint's coverage() returns a daily or monthly table of observed sample counts and retrieved files.

daily = spn.kaguya.esa1.counts.coverage(spn.month("2008-02"), freq="day")

The columns are sample_count, finite_sample_count, sample_time_count, expected_remote_files, and available_source_files. The result is saved in the features layer as <dataset>.coverage and reused. It summarizes availability only; it is not an interpreted event list.

Browse the Store

store.datasets(refresh=True)                       # every dataset (Polars)
store.datasets(layer="features", mission="kaguya")
store.raw_files(refresh=True, mission="kaguya")    # source files

lazy = store.scan_dataset("kaguya.esa1.counts", layer="normalized")   # Polars LazyFrame
frame = lazy.filter(...).collect()

store.dataset(dataset_id, layer=...) returns the dataset record (DatasetRecord).

Task Call
Read completed shards record.scan()
Inspect shard status record.shards(status="failed"), record.failed_shards()
Verify shard checksums record.verify_checksums(), record.verify_shards()
Verify including input source files store.verify_dataset(dataset_id, layer=...)
Rebuild the catalog from sidecars record.rebuild_catalog(), store.rebuild()

Research-topic databases

Group adopted results per topic in the databases layer.

db = store.database("lunar_wake", create=True)
db.register_product(name="event_table", schema=schema, description="hand-curated lunar wake events")
db.adopt_dataset(record, description="aligned context features")   # register an existing dataset without moving it
db.products()
db.product("event_table").scan()

adopt_dataset adds a dataset that lives in features (or another layer) to the database list without moving it.

Event catalogs

Hand-picked events and detector output can be saved as validated tables with event_catalog.

catalog = store.event_catalog("lunar_wake", create=True)
catalog.write_events(events, time_coverage=spn.month("2008-02"), overwrite=True)

monthly = catalog.counts(freq="month", by=("instrument",))
rates = catalog.rates(exposure, freq="day", by=("phenomenon",))

events is a table with time_start, time_stop, mission, instrument, phenomenon, confidence, detector, and detector_version columns. rates divides counts by the time the detector could actually decide (exposure_seconds) and keeps periods with zero events as zero. To keep unobserved periods distinct from zero events, pass the decidable time aggregated separately as exposure.

Moving to another machine

The Store stays in a form that rsync can copy as plain files. See Moving A Store.