---
name: matlab/create-sdi-run
source: https://app.decimal.ai/s/matlab-create-sdi-run@1/SKILL.md
source_sha256: ab54f47d8577
---

# Create SDI Run

Get data into the Simulation Data Inspector using the shortest path that works. Most agents reflexively read files into the workspace and rebuild signals by hand; this skill teaches the direct paths and when each applies.

## When to Use

- User has a `.mat`, `.csv`, or `.xlsx` file of logged data and wants to view it in SDI.
- User just ran `sim(model)` and wants to see the logged signals.
- User has `timeseries`, `timetable`, or `Simulink.SimulationData.Dataset` variables in the workspace and wants an SDI run.
- User asks about `Simulink.sdi.createRun`, `Simulink.sdi.getAllRunIDs`, or `Simulink.sdi.getRun`.

## When NOT to Use

- Comparing two existing runs — that is a distinct workflow (`Simulink.sdi.compareRuns`).
- **Streaming data live during a running simulation** — this is a separate API (`Simulink.sdi.createRunOrAddToStreamedRun`), not a mode of `createRun`. Prompts like "stream data into SDI while my sim is running" belong to that API. Do not activate on those.
- Files in MDF (`.mf4`), ULG, ROS bag, CAN log, or HDF5 formats — see [references/format-support.md](references/format-support.md) for what SDI reads natively vs. what needs a different toolbox.
- Customizing plot layout, cursors, or subplot arrangement beyond opening the SDI window.
- **Authoring Gherkin `.feature` files, Simulink Test cases, `matlab.unittest` classes, or any pass/fail assertions or regression tests** for a model or subsystem — use `testing-simulink-models` (Gherkin-based `model_test`, requires Simulink Test). Prompts like "write a Gherkin test for X", "verify abs(-5) equals 5", "create a regression test for this subsystem", or any request whose deliverable is a test artifact belong there. Do **not** activate this skill on such prompts even when `testing-simulink-models` is unavailable — in that case, tell the user which skill is missing and stop.

## Workflow

Answer these four questions **in order**. The first "yes" tells you what to do.

### 1. Is the data already in SDI?

After `sim(model)` with signal logging enabled, SDI **auto-populates** one run. **Do not call `createRun`** — that produces a duplicate.

```matlab
before = Simulink.sdi.getAllRunIDs;
simOut = sim("myModel");
after  = Simulink.sdi.getAllRunIDs;
if numel(after) > numel(before)
    Simulink.sdi.view;   % done — just open the window
end
```

If no new run appeared, logging is off. Enable it via the Simulink Agentic Toolkit `model_edit` tool with a `configure` op setting `DataLogging` on the target port, then re-simulate — or fall back to Step 3 with `simOut`. As a last resort, ask the user to right-click a signal → **Log Selected Signals** in the Simulink editor themselves; that step is for the user to perform, not the agent.

### 2. Is the data in a file?

Use the `'file'` source. One call handles MAT, CSV, and Excel natively — no `load`, no `readtable`, no `sheetnames` loop.

```matlab
runID = Simulink.sdi.createRun("bench_test","file","logs/bench_test.mat");
```

**Excel with N sheets returns a 1×N `int32` vector** — one run ID per sheet. All returned runs inherit the base `name`; if the user needs sheet-named runs, rename each run in place with `sheetnames()` — do **not** delete and re-import per sheet:

```matlab
runIDs = Simulink.sdi.createRun("cases","file","cases.xlsx");   % [id1, id2, ...]
sheets = sheetnames("cases.xlsx");
for k = 1:numel(runIDs)
    Simulink.sdi.getRun(runIDs(k)).Name = sheets(k);
end
```

**Time-column header rule (CSV and Excel).** SDI's built-in reader requires the time column to be named literally `time` (case-insensitive). Headers like `time_s`, `t`, `Time (s)`, `Timestamp` cause `SDI:sdi:ImportError`. **Try `'file'` first and catch the error** — do not pre-inspect the header row and guess, because SDI's matching rules can differ from your interpretation. On `SDI:sdi:ImportError`, fall back to the `readmatrix` + `array2timetable` + `'vars'` pattern (Step 3 / CSV pattern below). Do **not** rewrite the source file.

**Other `createRun` failures.** `createRun` can also throw for reasons this skill does not enumerate — corrupted files, unsupported data types inside a MAT/Excel, malformed structs, permission errors, unregistered custom readers. When you catch an error identifier that is not `SDI:sdi:ImportError`, do **not** invent a fix or silently retry. Surface the full error identifier and message to the user, list the file or variables involved, and suggest they inspect the source (e.g., `whos("-file",path)` for a MAT, or `sheetnames(path)` plus a per-sheet read for an Excel). Ask before attempting an alternative import path.

### 3. Is the data in workspace variables?

Two sources, different purposes:

- **`'vars'`** — pass variables whose names or `.Name` properties are already correct. This is the default.
- **`'namevalue'`** — pass explicit signal names when the variables have no `.Name` metadata (e.g., bare arrays or unnamed `timeseries`).

Timeseries whose `.Name` is already what you want:

```matlab
% TEMPLATE — not executable
runID = Simulink.sdi.createRun("baseline","vars", speedTS, torqueTS);
```

Timetable — signals inherit the column names:

```matlab
% TEMPLATE — not executable
runID = Simulink.sdi.createRun("baseline","vars", benchTT);
```

Bare arrays needing explicit names:

```matlab
% TEMPLATE — not executable
runID = Simulink.sdi.createRun("baseline","namevalue", ...
    {"velocity","load"}, {velocityData, loadData});
```

**Gotcha (Gap E):** if a `timeseries` passed to `'namevalue'` has its own `.Name` set, `.Name` **wins** over the caller-supplied name. To force the name you want, either set `ts.Name` first and use `'vars'`, or clear it before `'namevalue'`.

### 4. Verify the run

After every `createRun` or auto-populate:

```matlab
ids = Simulink.sdi.getAllRunIDs;
run = Simulink.sdi.getRun(ids(end));
fprintf("Run %d: %s (%d signals)\n", ids(end), run.Name, run.SignalCount);
for k = 1:run.SignalCount
    fprintf("  %s\n", run.getSignalByIndex(k).Name);
end
Simulink.sdi.view;
```

Verify against MATLAB via the MATLAB MCP server's `evaluate_matlab_code` tool. If `SignalCount == 0`, the import silently produced nothing — investigate the source, do not report success.

## Key Functions

All available from R2023a with base Simulink; no additional toolbox required.

| Function | Purpose | Available From |
|---|---|---|
| `Simulink.sdi.createRun(name)` | Empty run — used with `Run.add` for a per-signal build; rarely the right first choice. | R2011b |
| `Simulink.sdi.createRun(name,"file",path)` | Import a `.mat`, `.csv`, or `.xlsx` file. Returns scalar `int32` for MAT/CSV, `1×N int32` vector for N-sheet Excel. | R2011b |
| `Simulink.sdi.createRun(name,"vars",v1,v2,...)` | Import workspace variables whose names/`.Name` are correct. | R2011b |
| `Simulink.sdi.createRun(name,"namevalue",names,values)` | Import with explicit names. See Gap E caveat. | R2011b |
| `Simulink.sdi.view` | Open the SDI window. Safe to call repeatedly; opens only if not visible. | R2011b |
| `Simulink.sdi.getAllRunIDs` | Return `int32` vector of every run's ID. Use before/after `sim` to detect auto-populate. | R2017a |
| `Simulink.sdi.getRun(runID)` | Return the `Simulink.sdi.Run` object for a run ID. | R2011b |
| `Simulink.sdi.clear` | Delete all SDI runs. **Ask the user first** — destructive. | R2011b |

Four `createRun` sources exist: **empty**, **`'vars'`**, **`'namevalue'`**, **`'file'`**. When unsure, run `help Simulink.sdi.createRun`.

## Patterns

### Pattern: MAT file → SDI (one line)

```matlab
runID = Simulink.sdi.createRun("sensor_log","file","data/sensor_log.mat");
Simulink.sdi.view;
```

The variables inside the MAT file may be `timeseries`, `timetable`, `Simulink.SimulationData.Dataset`, or legacy struct-with-time — SDI's reader picks them up automatically.

### Pattern: CSV file → SDI (try `'file'`, catch, fall back)

Attempt the one-liner first. On `SDI:sdi:ImportError`, fall back to the timetable path — do not rewrite the file:

```matlab
try
    runID = Simulink.sdi.createRun("bench_run","file","logs/bench_run.csv");
catch ME
    if strcmp(ME.identifier,"SDI:sdi:ImportError")
        raw     = readmatrix("logs/bench_run.csv");
        headers = string(readcell("logs/bench_run.csv","Range","1:1"));
        tt = array2timetable(raw(:,2:end), ...
                "RowTimes", seconds(raw(:,1)), ...
                "VariableNames", headers(2:end));
        runID = Simulink.sdi.createRun("bench_run","vars", tt);
    else
        rethrow(ME);
    end
end
```

Assumes the time column is column 1 and expressed in seconds — the common case for bench logs. If the time column sits elsewhere or uses different units, adjust the index or wrap it with the appropriate `duration` constructor (`milliseconds`, `minutes`, …) before passing to `RowTimes`.

### Pattern: Excel with multiple sheets → one run per sheet

One `createRun` call per file. Every returned run initially inherits the **base name** you supplied — SDI does not auto-apply sheet names. Rename in place with `sheetnames()`; do **not** delete and re-import per sheet. The same time-column-header rule applies as for CSV — if a sheet's time column is not named `time`, `createRun` throws `SDI:sdi:ImportError`; wrap this call in the same try-then-fallback shown in the CSV pattern, using `readmatrix(...,"Sheet",sheets(k))` and `readcell(...,"Sheet",sheets(k),"Range","1:1")` per sheet.

```matlab
runIDs = Simulink.sdi.createRun("test_matrix","file","data/two_runs.xlsx");
% runIDs is a 1xN int32 vector — one ID per sheet, all named "test_matrix".
sheets = sheetnames("data/two_runs.xlsx");
for k = 1:numel(runIDs)
    run = Simulink.sdi.getRun(runIDs(k));
    run.Name = sheets(k);   % rename in place to the sheet name
    fprintf("Sheet run: %s (%d signals)\n", run.Name, run.SignalCount);
end
```

To restrict to specific sheets, use the `sheets` name-value — the returned vector is trimmed accordingly:

```matlab
runIDs = Simulink.sdi.createRun("cases","file","cases.xlsx", ...
    sheets=["baseline","variant"]);
```

### Pattern: Post-sim — just open SDI

```matlab
before = Simulink.sdi.getAllRunIDs;
simOut = sim("controller_test");
after  = Simulink.sdi.getAllRunIDs;

if numel(after) > numel(before)
    Simulink.sdi.view;             % logged signals already imported
else
    % No auto-populate — logging is off. Enable it and re-simulate, or
    % pass simOut through createRun as a fallback:
    Simulink.sdi.createRun("run_from_simout","vars", simOut);
    Simulink.sdi.view;
end
```

### Pattern: Rename workspace signals

Set `.Name` before `'vars'`. This avoids the `'namevalue'` override trap.

```matlab
data = load("data/speed_torque.mat");
speed  = data.speed;
torque = data.torque;
speed.Name  = "velocity";
torque.Name = "torqueNm";
runID = Simulink.sdi.createRun("baseline","vars", speed, torque);
```

## Conventions

**Always**

- **Check whether SDI already has the data before calling `createRun`.** Use `Simulink.sdi.getAllRunIDs` before and after `sim(model)`. If a new run appeared, `sim` already imported the logged signals — call `Simulink.sdi.view` and stop.
- **Prefer the `'file'` source** for `.mat`, `.csv`, `.xlsx`. It handles the read and the signal-naming for you. Reserve `load`/`readtable`/`sheetnames` for the CSV time-column fallback and for genuinely workspace-first workflows.
- **Verify with `SignalCount`.** After every import, confirm at least one signal exists. Zero signals means the import failed silently.
- **Read `help Simulink.sdi.createRun`** if you are unsure which source applies. The four sources are the entire API surface for import.

**Ask First**

- `Simulink.sdi.clear` — deletes every run. Confirm with the user before running.
- Modifying the source file (e.g., renaming a CSV column to `time`) — prefer the timetable fallback pattern instead of altering user data.
- Attempting an alternative import path after a non-`SDI:sdi:ImportError` failure — surface the error to the user first, then ask which path they want.

**Never**

- Never invent function names. `Simulink.sdi.createRunFromFile` does not exist. Use `Simulink.sdi.createRun(name,"file",path)`.
- Never rebuild a file-side workflow through `load` / `readtable` / manual `timeseries` construction / `Run.add` when the `'file'` source handles the same file natively.
- Never assume `namevalue` names take precedence over `timeseries.Name` — `.Name` wins. See Gap E in Common Mistakes.
- Never claim success without inspecting `SignalCount` and the signal names.

## Common Mistakes

| Mistake | Why It's Wrong | Correct Approach |
|---|---|---|
| Reading MAT/CSV/Excel yourself and calling `createRun(...,"vars",...)` | Duplicates the `'file'` source's work; loses SDI's automatic signal-name handling; scales badly for multi-sheet Excel. | `Simulink.sdi.createRun(name,"file",path)`. |
| Deleting the multi-sheet import and re-calling `createRun` in a per-sheet loop because both runs share the base name | Discards the correct one-call `'file'` import and violates the "single call handles sheet expansion" convention. All runs from one Excel file inherit the base name — they are **not** auto-renamed to sheet names. | Keep the one `createRun(...,"file",xlsx)` call and rename each run in place: `Simulink.sdi.getRun(runIDs(k)).Name = sheets(k)`. |
| Calling `createRun(...,"namevalue",{"SimOut"},{simOut})` after `sim(model)` | With signal logging on, `sim` already imported the run. This adds a duplicate. | `sim(model); Simulink.sdi.view;` (verify with `getAllRunIDs` before/after). |
| Using `Simulink.sdi.createRunFromFile` | Function does not exist. Hallucination. | `Simulink.sdi.createRun(name,"file",path)`. |
| Passing `namevalue` names when the timeseries has its own `.Name` | `.Name` overrides the caller-supplied name silently — Gap E. | Set `ts.Name` first, then use `'vars'`. Or clear `.Name` before `'namevalue'`. |
| Pre-inspecting the header and skipping `'file'` because you *think* the time column is misnamed | Your guess can differ from SDI's actual matching rules; skipping `'file'` costs the automatic signal handling. | Try `'file'` first, catch `SDI:sdi:ImportError`, then use the timetable fallback (Step 3 / CSV pattern). |

## References

- [references/format-support.md](references/format-support.md) — which file formats SDI reads natively, which need a toolbox, which need conversion. Consult when the user has a file extension other than `.mat`, `.csv`, or `.xlsx`.
- [references/createrun-signatures.md](references/createrun-signatures.md) — the four `createRun` sources side-by-side with return-value shapes and name-value options. Consult when picking a source or debugging return values.

----

Copyright 2026 The MathWorks, Inc.

----