Collection
A named, citable group of Datasets.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Short name, unique within its scope (e.g. analysed-data, jintrac-30420-56) |
title |
string | No | Human-readable title |
access_level |
enum | No | Effective access level (inherited if not set) |
root_url |
string | No | Access root for all physical data in this collection (dcat:accessURL) |
activity_id |
integer | No | FK to the Activity that produced this collection |
scientific_metadata |
list | No | What this collection is about, see Scientific metadata |
Making a run discoverable
A Collection carries scientific_metadata like a Shot or a Dataset, and its list endpoints take the same annotation filter. That is how a simulation run becomes something you can search for.
A run's outputs are always reachable as the datasets carrying its activity_id, so a bundle is never required. It is what you create when you want the run itself to be findable and citable. Set activity_id to the run and record what the run was about:
{
"name": "jintrac-30420-56",
"activity_id": 12,
"scientific_metadata": [
{"name": "confinement_mode", "value": "H-mode"},
{"name": "plasma_current", "value": 0.4, "unit": "MA"}
]
}
Then the run answers a catalogue question:
GET /api/v1/devices/mast/shots/30420/collections?annotation=confinement_mode:H-mode
If you do not bundle a run, its claims belong on its output datasets instead, and the run is not searchable as a run. That is a choice, not a gap: FDS records what a producer asserts and never infers claims they did not make.
The same field on a hand-curated collection describes what the selection was chosen for rather than what a run produced. The shape and the query are identical; activity_id tells the two apart.
Collections support nesting (a Collection can contain other Collections) and a Dataset can belong to multiple Collections. The Dataset's URI is independent of its collection membership, so adding or moving a dataset never changes its URL.
Create a Collection, then add its member Datasets by id, one bodyless POST per dataset:
COLLECTION_ID=$(curl -s -X POST "$API/devices/mast/shots/30421/collections" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name": "experiment-data", "title": "Experiment Data", "access_level": "public"}' \
| jq -r .id)
# 12 15 18 are the ids of the datasets to group
for DATASET_ID in 12 15 18; do
curl -X POST "$API/collections/$COLLECTION_ID/datasets/$DATASET_ID" \
-H "Authorization: Bearer $TOKEN"
done
col = requests.post(
f"{API}/devices/mast/shots/30421/collections",
headers=headers,
json={
"name": "experiment-data",
"title": "Experiment Data",
"access_level": "public",
},
).json()
# 12, 15, 18 are the ids of the datasets to group
for dataset_id in [12, 15, 18]:
requests.post(
f"{API}/collections/{col['id']}/datasets/{dataset_id}", headers=headers
)
col = httpx.post(
f"{API}/devices/mast/shots/30421/collections",
headers=headers,
json={
"name": "experiment-data",
"title": "Experiment Data",
"access_level": "public",
},
).json()
# 12, 15, 18 are the ids of the datasets to group
for dataset_id in [12, 15, 18]:
httpx.post(f"{API}/collections/{col['id']}/datasets/{dataset_id}", headers=headers)
const col = await (
await fetch(`${API}/devices/mast/shots/30421/collections`, {
method: "POST",
headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({
name: "experiment-data",
title: "Experiment Data",
access_level: "public",
}),
})
).json();
// 12, 15, 18 are the ids of the datasets to group
for (const datasetId of [12, 15, 18]) {
await fetch(`${API}/collections/${col.id}/datasets/${datasetId}`, {
method: "POST",
headers: { Authorization: `Bearer ${TOKEN}` },
});
}