create-sdi-run

$npx mdskill add matlab/simulink-agentic-toolkit/create-sdi-run

Import data into Simulation Data Inspector from files, variables, or simulation.

  • Loads .mat, .csv, .xlsx files or workspace variables into SDI.
  • Uses Simulink.sdi.createRun with 'file', 'vars', or 'namevalue' sources.
  • Selects the shortest direct import path based on user input type.
  • Returns run ID and confirms data is viewable in SDI.

SKILL.md

.github/skills/create-sdi-runView on GitHub ↗
---
name: create-sdi-run
description: >
  Import data into the Simulation Data Inspector (SDI) from MAT, CSV, or Excel
  files, from workspace variables, or from a Simulink simulation. Use when the
  user wants to view a logged file in SDI, load a bench-test log, or verify
  what a just-completed `Simulink.sdi.createRun` or `sim(model)` produced.
  Covers the `'file'`, `'vars'`, and `'namevalue'` sources of `createRun`, the
  auto-populate behavior after `sim()`, and post-import verification. Not for
  comparing existing runs (use `Simulink.sdi.compareRuns`); not for streaming
  data live during a running simulation (use
  `Simulink.sdi.createRunOrAddToStreamedRun` — a separate API); and not for
  authoring Gherkin or Simulink Test test cases, assertions, or regression
  tests (use `testing-simulink-models`). Do not activate on test-authoring or
  verification prompts even when no test-authoring skill is available — say
  so and stop.
license: MathWorks BSD-3-Clause
metadata:
  author: MathWorks
  version: "1.0"
---

# 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.

----

More from matlab/simulink-agentic-toolkit

SkillDescription
author-modeladvisor-checks>
authoring-simulink-inputs>
building-architecture-modelsCommon steps for building multi-layer system architecture models using System Composer. Use when implementing architecture models or when interacting with interface dictionaries, allocation sets, stereotypes, and requirements for architecture components.
building-simulink-modelsBuilds and edits Simulink, System Composer, Stateflow, and Simscape models. Use when modifying model structure, parameters, ports, connections, or Stateflow chart internals.
checking-model-complianceUse this skill when the user asks to check Simulink model compliance against a standard (MISRA, MAB, JMAAB, ISO 26262, ISO 25119, DO-178C, DO-254, IEC 61508, IEC 62304, EN 50128, CERT C/CWE, AUTOSAR), wants to run Model Advisor checks, or needs a compliance report with fix suggestions. For JMAAB/MAB, supplement deterministic checks with agentic review of uncheckable guidelines.
configuring-block-policyGuide users through creating and managing .satk/block-policy.json for controlling which blocks the agent can use, which are excluded, and which block parameters the agent should not modify. Use when setting up block usage policy for a project.
curating-library-kgGuide users through curating the library knowledge index — reviewing block categories, marking common/important blocks, and improving block descriptions for better agent block selection.
filing-bug-reportsGenerate a standalone bug report that another developer can use to reproduce, investigate, and fix an issue. Use when the user says 'file a bug', 'write a bug report', 'report this issue', or asks to document a defect for handoff.
generate-requirement-draftsGenerates draft requirements from Simulink models. Use when drafting or updating requirement artifacts from a model. Prefers Requirements Toolbox (.slreqx) when available; falls back to structured YAML.
inject-faultsAdd, configure, and manage faults on Simulink, Simscape, and System Composer model signals for robustness analysis and safety validation. Use when injecting faults (stuck, noise, gain, offset) onto block inports/outports, enabling fault simulation, or analyzing fault effects. Covers sensor failures, signal corruptions, actuator faults, FMEA validation, and robustness testing. Requires Simulink Fault Analyzer.