# param_io { #tvbo.data.param_io }

`data.param_io`

Resolve a :class:`Parameter`'s value from its declared provenance.

A parameter says where its value comes from in exactly one of three ways, and the choice is by provenance, never by size:

``value:``
    A YAML literal. Returned as-is; codegen inlines it.

``source:`` (+ ``measure:``)
    WHERE existing bytes live — a curated entity's IRI, or a path resolved against
    the declaring spec's directory. ``measure`` selects one array out of a source
    holding several; its address space is the source's own (an HDF5/Zarr dataset
    key, a Network's per-node measure name).

``producer:``
    HOW derived bytes are made — a :class:`FunctionCall` naming the callable, its
    arguments, and (via ``output``) which entry to take from a callable returning
    several named arrays.

Sourced and produced values are **never materialised into ``Parameter.value``**, so loading a spec stays cheap no matter how large the array is, and a dumper never sees bytes it would try to serialise back into YAML. Resolution happens here, on demand, and the result is cached in this module — the ``Parameter`` object is never mutated.

That is deliberate: ``Parameter`` appears at 16 nesting sites in the schema, and LinkML always constructs the *declared range* class, so a tvbo subclass carrying lazy behaviour would need a hand-written re-wrap at every one of them (and would diverge between the dataclass and pydantic flavours). Keeping resolution outside the class means the generated datamodel is untouched and both flavours behave identically.

## Functions

| Name | Description |
| --- | --- |
| [clear_cache](#tvbo.data.param_io.clear_cache) | Drop every resolved array. Mainly for tests and long-lived processes. |
| [is_lazy](#tvbo.data.param_io.is_lazy) | True when this parameter's value is resolved rather than inlined. |
| [is_reference](#tvbo.data.param_io.is_reference) | True when an argument value points at an entity rather than being a literal. |
| [live_artifacts](#tvbo.data.param_io.live_artifacts) | ``(paths, producers)`` this study still reaches in the produced-constant store. |
| [materialise](#tvbo.data.param_io.materialise) | The ``(file, key)`` a backend reads this parameter's array from. |
| [read_artifact](#tvbo.data.param_io.read_artifact) | Load a materialised ``(path, key)`` artifact back to its array — for codegen probes that need a produced/sourced constant's shape (e.g. a partition's group count) without re-running the producer. |
| [resolve](#tvbo.data.param_io.resolve) | This parameter's value, resolving ``source``/``producer`` on demand. |
| [resolve_network_node](#tvbo.data.param_io.resolve_network_node) | Per-node vector for a ``network.<measure>`` reference. |
| [superseded_artifacts](#tvbo.data.param_io.superseded_artifacts) | Artifacts of THIS study's producers that it no longer reaches, newest first. |

### clear_cache { #tvbo.data.param_io.clear_cache }

```python
data.param_io.clear_cache()
```

Drop every resolved array. Mainly for tests and long-lived processes.

### is_lazy { #tvbo.data.param_io.is_lazy }

```python
data.param_io.is_lazy(param)
```

True when this parameter's value is resolved rather than inlined.

The one rule codegen branches on: a literal is materialised and inlined; anything obtained (``source``) or derived (``producer``) is read at run time and must never be embedded in generated source.

### is_reference { #tvbo.data.param_io.is_reference }

```python
data.param_io.is_reference(value)
```

True when an argument value points at an entity rather than being a literal.

### live_artifacts { #tvbo.data.param_io.live_artifacts }

```python
data.param_io.live_artifacts(root, cache_dir=None)
```

``(paths, producers)`` this study still reaches in the produced-constant store.

*producers* is every ``module.function`` whose liveness could be decided; a producer
whose arguments cannot be resolved is left out of BOTH sets, so its artifacts are never judged dead on the strength of a failure to look at them.

### materialise { #tvbo.data.param_io.materialise }

```python
data.param_io.materialise(param, source_dir=None, context=None, cache_dir=None)
```

The ``(file, key)`` a backend reads this parameter's array from.

Codegen emits this pair rather than the bytes, so a large constant never enters the spec or the generated source and is read at run time instead. Generated modules are ``exec``'d in memory as often as they are written to disk, so the path is absolute (resolved against the declaring spec's directory) — the same way ``bids_dir`` is emitted. A kit stays correct without rewriting code: its emitter rewrites the *spec* to point at the companion it staged and re-renders, exactly as it already does for ``network.h5``.

A ``source:`` parameter already lives in a file, so nothing is written. A ``producer:`` parameter is computed once and cached content-addressed under ``~/.tvbo/constants``, keyed by the producing call — so it survives across runs and every parameter naming that producer shares the one artifact.

Raises for a literal (there is nothing to read; it inlines) or for a parameter with no declared value.

### read_artifact { #tvbo.data.param_io.read_artifact }

```python
data.param_io.read_artifact(path, key=None)
```

Load a materialised ``(path, key)`` artifact back to its array — for codegen probes that need a produced/sourced constant's shape (e.g. a partition's group count) without re-running the producer.

### resolve { #tvbo.data.param_io.resolve }

```python
data.param_io.resolve(param, source_dir=None, context=None)
```

This parameter's value, resolving ``source``/``producer`` on demand.

Returns the literal ``value`` untouched when there is one, so a scalar costs nothing. Returns ``None`` for a parameter that declares no value at all (a free parameter, say) rather than raising — the caller decides whether that is an error.

``context`` is the owning experiment (or a network); a producer argument naming an entity — ``positions: network.nodes.position`` — resolves against it. Resolved arrays are cached and returned **read-only**: they are shared, and a resolved constant is not the caller's to modify.

### resolve_network_node { #tvbo.data.param_io.resolve_network_node }

```python
data.param_io.resolve_network_node(net, measure)
```

Per-node vector for a ``network.<measure>`` reference.

The single definition shared by the producer-argument path (``_resolve_ref``) and the observation-embedding path (``utils.collect_network_node_arrays``), so both resolve ``network.positions`` / ``network.instrength`` / ``network.labels`` identically. ``positions`` → region centroids ``(n_nodes, 3)``; ``instrength`` → weighted in-degree ``matrix('weight').sum(axis=1)`` (row sum = incoming, the TVB/Koller convention); ``labels`` → the region-label string array (``Network.node_labels``).

Anything else is read as a **named per-node attribute**, the node-side twin of ``network.edges.<attr>``: first from the nodes' own ``parameters`` (in node order), then from a ``nodes/<attr>`` dataset in the companion store. That is what lets a study carry a measured per-region array — a per-node current range, a region size — in the network file and reference it from a spec instead of pasting it into code. Returns None when the measure is unknown or unbuildable.

### superseded_artifacts { #tvbo.data.param_io.superseded_artifacts }

```python
data.param_io.superseded_artifacts(root, cache_dir=None)
```

Artifacts of THIS study's producers that it no longer reaches, newest first.

Superseded, not merely old: the content address keys on the producing call *and* on its module's source, so an artifact of a producer this study uses, at a digest this study no longer computes, can only be a version left behind by an edit or by a changed argument. Files belonging to producers not seen here are never listed — they may well be another study's, and this reads one study.