Skip to content
Agentmart
Submit a tool

writing-models

How to specify a Dynamics in TVBO — the YAML and Python forms, parameter / state-variable / equation conventions, and common pitfalls.

Skill by virtual-twin

IndexedEUPL-1.2★ 5
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/virtual-twin-writing-models

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

How to specify a Dynamics in TVBO — the YAML and Python forms, parameter / state-variable / equation conventions, and common pitfalls.

Use cases: writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/writing-models

What's inside

Skill name
writing-models
Description
How to specify a Dynamics in TVBO — the YAML and Python forms, parameter / state-variable / equation conventions, and common pitfalls.
License
EUPL-1.2
Instructions
99 lines in SKILL.md · view on GitHub

Files (1)

  • SKILL.md

SKILL.md (first lines)

# Writing Models in TVBO

A **Dynamics** is the smallest building block: a set of named parameters and state variables governed by ODE equations. It can be written as YAML or constructed directly in Python.

## YAML form

```yaml
name: LorenzAttractor
parameters:
    sigma:
        value: 10
        label: Prandtl number
    rho:
        value: 28
        label: Rayleigh number
    beta:
        value: 2.6666666666666665
state_variables:
    X:
        equation:
            lhs: \dot{X}
            rhs: sigma * (Y - X)
    Y:
        equation:
            lhs: \dot{Y}
            rhs: X * (rho - Z) - Y
    Z:
        equation:
            lhs: \dot{Z}
            rhs: X * Y - beta * Z
```

## Python form

```python
from tvbo import Dynamics

lorenz = Dynamics(
    parameters={
        "sigma": {"value": 10.0},
        "rho": {"value": 28.0},
        "beta": {"value": 8 / 3},
    },
    state_variables={
        "X": {"equation": {"rhs": "sigma * (Y - X)"}},
        "Y": {"equation": {"rhs": "X * (rho - Z) - Y"}},
        "Z": {"equation": {"rhs": "X * Y - beta * Z"}},
    },
)
```

## Loading a curated model from a source

TVBO components are declarative: a `Dynamics` is either specified inline (as
above), loaded from YAML, or **pointed at semantically via an `iri`**. The
IRI's prefix names the source — `tvbo:` for the built-in ontology, but the
same mechanism is intended to dispatch to other prefixes (e.g. `neuroml:`)
that resolve from other ontologies / data sources.

```python
# Direct construction (Python API)
dyn = Dynamics.from_db("ReducedWongWangExcInh")

# As a semantic pointer inside a SimulationExperiment dict
dynamics = {"name": "ReducedWongWang", "iri": "tvbo:ReducedWongWangExcInh"}
```

A bare name string (`dynamics="ReducedWongWangExcInh"`) is **not** a semantic
pointer — there's no prefix, so the resolver cannot tell which source to
query. Always include the `iri`.

## Conventions and pitfalls

- **`lhs` is LaTeX**, not Python. `\dot{X}` for a time-derivative. Omit `lhs` to default to `\dot{<state>}`.
- **`rhs` is a SymPy-parseable expression**. Names must match parameters and state variables. Greek letters as full words: `sigma`, not `σ`.
- **`label`** on a parameter is human-readable metadata (renders in the platform browser). Keep `name` machine-friendly.
- Parameters carry **values**; state variables carry **equations**. Don't put a `value` on a state variable.
- **Don't hand-edit `tvbo/datamodel/**`** — that's generated from `schema/*.yaml`. Use the `Dynamics` class.
- **Trust the slots. Never assign a raw `{}` / `[]` to a LinkML slot — a `JsonObj` in your data is the symptom of mis-configured YAML / a Python object leaking into the schema.** The active datamodel (`tvbo/datamodel/schema.py`) is the jsonasobj2 / `YAMLRoot` dataclass form. Inlined dicts/lists are coerced into typed classes **only in `__post_init__` (i.e. at construction)**. Mutating a slot afterwards does **not** re-run that coercion:
    - **Multivalued slots** (dict- or list-backed: `parameters`, `dynamics`, `coupling`, `nodes`, `edges`, `transforms`, …) → **mutate the existing container in place**: `.update(...)` / `container["X"] = ...` for dict slots, `.append(...)` for list slots. To get a *typed* entry, insert a constructed class: `net.dynamics["X"] = Dynamics(...)`.
        - ❌ `net.dynamics = {...}` — a raw dict goes through jsonasobj2's `JsonObj.__setattr__`, which wraps it in a bare `JsonObj` (it later breaks `.items()` / `.values()` on round-trip).
        - ❌ `net.nodes = [{...}]` — a raw list isn't wrapped in `JsonObj`, but its entries stay **uncoerced plain dicts**, never `Node` instances.
        - ⚠️ `.update({...})` / `["X"] = {...}` avoids the `JsonObj`, but a raw dict still isn't normalized — prefer inserting a constructed class instance.
    - **Single-valued slots** → plain `=` is correct. Assign a scalar directly (`net.number_of_nodes = 76`); for a nested-object slot assign a **constructed LinkML class instance** (`net.coordinate_space = CommonCoordinateSpace(...)`), never a raw `{}` (which becomes a bare `JsonObj`).
    - **Best of all:** pass everything at construction (`Network(dynamics={...}, nodes=[...])`) so `__post_init__` normalizes it into typed classes for you.
- **Never change a datatype; only assign valid LinkML class instances.** If a slot expects a class, construct that class — don't pass a bare dict or an ad-hoc object.
    > This holds as long as the active datamodel is the dataclass/`YAMLRoot` one. If TVBO switches to the pydantic datamodel (`tvbo/datamodel/pydantic.py`), dict-assignment may become valid (pydantic validates/coerces) — unverified.

## Beyond ODEs

- **Stochastic** terms via `tvbo.classes.noise`.
- **Discrete events** via `tvbo.classes.event`.
- **Perturbations / stimulation** via `tvbo.classes.perturbation`.
- **Network coupling** via `tvbo.classes.coupling` + `tvbo.classes.network`.
- **Continuation** (bifurcation analysis) via `tvbo.classes.continuation`.

See the `running-simulations` skill for what to do once you have a `Dynamics`.

From virtual-twin/tvbo, EUPL-1.2.

More from virtual-twin/tvbo

Signals

Stars
5
Forks
1
Open issues
13
Last commit (this folder)
Jun 25, 2026 (4 months ago)
Last release
Jul 9, 2026 (3 months ago)

From GitHub (fetched Oct 24, 2026).

Details

License
EUPL-1.2
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from virtual-twin

Cite this page

writing-models skill. Agent Mart. https://agentmart.dev/skills/virtual-twin-writing-models (last updated 2026-10-24)