Skip to content

Datasets & Distributions

The core discovery object. A Dataset is the abstract metadata entity describing what the set of data is. A Distribution is a concrete physical access path describing how to retrieve it. A Dataset can exist without any distributions (metadata-only registration) and distributions can be added later via POST /datasets/{id}/distributions.

Dataset

Field Type Required Description
name string Yes Short name, unique within its scope (e.g. equilibrium)
url string No Physical location (s3://, gs://, az://), taken from the primary Distribution and absent if none exists yet
media_type string No MIME type of the primary distribution (e.g. application/x-zarr)
format string No Format label (e.g. NetCDF4)
level integer No Numeric processing level. No controlled vocabulary, so set it only where the producer has a meaning for it
quality_flag string No Free-form quality annotation (e.g. good, suspect), no controlled vocabulary
access_level enum No public, embargoed, or restricted
publisher string No Institution making the data available (dct:publisher)
creator string No Person or team who produced the dataset (dct:creator)
temporal_start datetime No Start of the measurement window (mapped to dct:temporal, a dct:PeriodOfTime)
temporal_end datetime No End of the measurement window
scientific_metadata list No Diagnostic-specific parameters, see Scientific metadata below
activity_id integer No FK to the Activity that produced this dataset (provenance)
derived_from list No Upstream entities this dataset was built from, see below
annotates string No For a feature annotation dataset: the feature it localises (see Feature annotations)
subject_dataset_id integer No For a dataset-frame annotation: the source dataset it localises a feature in

Recording a dataset's provenance

activity_id says which run produced a dataset. derived_from says which data it was built from, and the two are independent: a run may consume several inputs while any one output depends on only some of them. FDS never guesses that link, so a derivation is only ever recorded because someone asserted it.

The upstream does not have to be registered in FDS. Identify it however you can, falling back to a free-text label and/or description only in the worst cases:

Field Use
source_dataset_id the upstream is a registered FDS Dataset
source_identifier it has a DOI, a URL, or another persistent identifier
source_label, source_description free text, when it can only be named or described

Give source_dataset_id or source_identifier, not both, since a registered dataset is already referenced by its own address. Every entry needs at least one of the three. A label and a description may accompany any of them.

A DOI is understood bare (10.5281/zenodo.123), prefixed (doi:10.5281/...), or as a full URL. Handle (hdl:), Software Heritage (swh:) and URNs are understood too. Anything else is recorded exactly as given, and simply not treated as resolvable.

"derived_from": [
  {"source_dataset_id": 42},
  {"source_identifier": "10.5281/zenodo.123", "source_label": "Upstream on Zenodo"},
  {"source_label": "Legacy tape", "source_description": "Recovered from a 1998 archive"}
]

Derivations can also be maintained afterwards:

POST   /api/v1/datasets/{id}/derivations
GET    /api/v1/datasets/{id}/derivations
DELETE /api/v1/datasets/{id}/derivations/{derivation_id}

Following the chain

derivations answers one hop. To get the whole ancestry in one request, ask for the lineage:

GET /api/v1/datasets/{id}/lineage

Each upstream is nested under the dataset that asserted it. Only registered datasets have upstreams to nest, so an external or described-only upstream is always a leaf.

{
  "dataset_id": 51,
  "name": "t_e_profile",
  "derived_from": [
    {
      "dataset_id": 42,
      "name": "raw_thomson",
      "derived_from": [
        {"identifier": "10.5281/zenodo.123", "label": "Upstream on Zenodo"}
      ]
    }
  ]
}

A dataset is expanded once per response. Reach it a second time, either because two paths lead to the same ancestor or because the derivations form a loop, and it appears as a stub marked seen carrying only its id. Look it up under its first occurrence for the full record. Two other flags mark a branch that stops early: restricted, where you do not have read access to the upstream, and missing, where the upstream has since been deleted. A node with none of the three is complete.

Registering a Dataset with a url and media_type creates its primary Distribution in the same call:

curl -X POST "$API/devices/mast/shots/30421/datasets" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "equilibrium",
       "url": "s3://fds-data/shots/30421/equilibrium",
       "media_type": "application/x-zarr", "access_level": "public"}'
requests.post(
    f"{API}/devices/mast/shots/30421/datasets",
    headers=headers,
    json={
        "name": "equilibrium",
        "url": "s3://fds-data/shots/30421/equilibrium",
        "media_type": "application/x-zarr",
        "access_level": "public",
    },
).raise_for_status()
httpx.post(
    f"{API}/devices/mast/shots/30421/datasets",
    headers=headers,
    json={
        "name": "equilibrium",
        "url": "s3://fds-data/shots/30421/equilibrium",
        "media_type": "application/x-zarr",
        "access_level": "public",
    },
).raise_for_status()
await fetch(`${API}/devices/mast/shots/30421/datasets`, {
  method: "POST",
  headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    name: "equilibrium",
    url: "s3://fds-data/shots/30421/equilibrium",
    media_type: "application/x-zarr",
    access_level: "public",
  }),
});

Listing datasets

Route Returns
GET /datasets Every dataset in the catalogue
GET /devices/{device}/datasets Every dataset the device hosts, shot-level and device-level
GET /devices/{device}/shots/{shot}/datasets The datasets of one shot

The device listing covers the whole device by default. Narrow it with scope:

scope Returns
all (default) Every dataset for the device
device Datasets belonging to the device as a whole rather than to any one shot
shot Datasets attached to one of the device's shots
curl "$API/devices/mast/datasets"                # everything mast hosts
curl "$API/devices/mast/datasets?scope=device"   # general device-level Datasets

A device-level Dataset describes the machine rather than a single experiment. Geometry and calibration data are examples of this.

Listings are paged with offset and limit (default 100) and returned in a stable order, so paging through a device covers it exactly once. A page can contain fewer than limit entries when some datasets are not readable by the caller; an empty page does not mean the end of the results.

A Dataset can also link to reference geometry. A Dataset declares the geometry it needs via geometry_references, or a Device-level Dataset provides geometry via geometry_roles and applies_to. The same machinery carries reference calibration: a Dataset names the calibration it needs via calibration_references, and a Device-level Dataset provides it via calibration_roles, calibration_stage, and applies_to. Unlike geometry, calibration can resolve to an ordered chain of stages. See Reference Datasets.

Listing and filtering

The dataset lists accept a name filter, an annotation filter on the dataset's own scientific_metadata, and a shot_annotation filter on an annotation carried by its parent shot:

GET /api/v1/devices/mastu/datasets?name=equilibrium
GET /api/v1/devices/mastu/datasets?name=equilibrium&shot_annotation=elm

The second form answers a question spanning both levels ("equilibrium datasets from shots that had an ELM train") in one request. Datasets that don't belong to a shot never match a shot_annotation filter. Shot- and device-scoped dataset lists accept annotation on the same terms. See Finding annotated records.

Distribution

A Distribution is a physical access path for a Dataset, describing how to retrieve the data.

Field Type Required Description
url string Yes Download or access URL (e.g. s3://fds-data/shots/30421/equilibrium)
media_type string No IANA media type (e.g. application/x-zarr, application/x-hdf5)
format string No Human-readable format label (e.g. NetCDF4, HDF5)
endpoint_url string No Storage endpoint, used for credential vending
access_level enum No Override access policy for this distribution

In most cases a Dataset will have exactly one Distribution, and you won't need to think about the distinction. The Dataset endpoints return the primary distribution's url, media_type, and format inlined directly on the Dataset response, so there is nothing extra to fetch.

Multiple distributions are supported when the same underlying data is available in more than one form, for example as both Zarr and HDF5, or through multiple access endpoints. All distributions of a given Dataset must be scientifically interchangeable; different data belongs in a separate Dataset. Additional distributions can be registered via POST /datasets/{id}/distributions.