Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ Marketplace listing: <https://marketplace.visualstudio.com/items?itemName=magne-
- **File navigation** — `Ctrl+click` a quoted path on `INCLUDE`, `IMPORT`,
`RESTART`, or `GDFILE` statements to open the referenced file. `PATHS`
aliases (`$NAME` lookups) are expanded before the file is resolved.
- **Verify and run the simulation** (optional) — with a local OPM Flow binary
configured, **Verify Deck (dry run)** parses and initialises the deck without
solving (a fast check that it and its `INCLUDE`/`PATHS` files load cleanly),
and **Run Simulation** runs the full case, both in an integrated terminal. On
Windows the simulator runs through WSL, with deck paths translated to their
`/mnt/<drive>` mount automatically.
- **Generate Keyword Reference** — opens a Markdown document listing all
keywords grouped by section, useful for uploading as AI-chat context.

Expand Down
42 changes: 42 additions & 0 deletions vscode-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,28 @@ after a run. It is available from the editor right-click menu, the Command
Palette, or the `Ctrl+Alt+P` (`Cmd+Alt+P` on macOS) shortcut. If the deck has
not been run yet and no `.PRT` exists beside it, a notice is shown.

### Verify and Run the Simulation (optional)

When a local OPM Flow binary is configured, two commands let you act on the open
deck without leaving the editor — both available from the editor right-click menu
and the Command Palette:

- **OPM Flow: Verify Deck (dry run)** runs flow in dry-run mode. The deck is
fully parsed and the model is initialized (grid, properties, wells, schedule),
but the time steps are not simulated — a fast check that the deck and
everything it pulls in via `INCLUDE` / `PATHS` loads cleanly.
- **OPM Flow: Run Simulation** runs the full simulation.

Both run in an integrated terminal, started in the deck's own directory so its
relative paths resolve. The output appears live, and afterwards
[Open PRT File](#open-prt-file) shows the print file.

This is opt-in: nothing runs until you set `opm-flow.simulator.executablePath`
(see [Simulator](#simulator) under Settings). On **Windows**, OPM Flow is
typically installed inside WSL — enable `opm-flow.simulator.useWsl` and point the
executable path at the Linux binary (e.g. `/usr/bin/flow`); the deck's Windows
path is translated to its `/mnt/<drive>` mount automatically.

### Generate Keyword Reference

**OPM Flow: Generate Keyword Reference** (Command Palette `Ctrl+Shift+P`) opens a
Expand All @@ -330,6 +352,19 @@ you can override them per-workspace or per-folder.
| --- | --- | --- |
| `opm-flow.additionalFileExtensions` | `[]` | Extra file extensions (with or without a leading `.`) to open as OPM Flow on top of the built-in list. Useful for project-specific include-file conventions. Matched case-insensitively. Example: `[".myinc", "wellconv"]`. For one-off cases the VS Code-native `files.associations` setting still works too. |

### Simulator

Optional integration for the [Verify and Run](#verify-and-run-the-simulation-optional)
commands. Leave `executablePath` unset to keep the feature dormant.

| Setting | Default | Description |
| --- | --- | --- |
| `opm-flow.simulator.executablePath` | `"flow"` | Path to the OPM Flow executable. With `useWsl` enabled this is a path *inside* WSL (e.g. `/usr/bin/flow`); otherwise a native path or a name on `PATH`. |
| `opm-flow.simulator.useWsl` | `false` | Run flow through WSL (`wsl.exe`). Required on Windows. The deck's Windows path is translated to its `/mnt/<drive>` mount automatically. |
| `opm-flow.simulator.wslDistribution` | `""` | WSL distribution to use (e.g. `ubuntu-26.04`); empty uses the default. List installed distributions with `wsl -l -v`. Only used when `useWsl` is enabled. |
| `opm-flow.simulator.verifyArgs` | `["--enable-dry-run=true"]` | Arguments for **Verify Deck**. The default parses and initializes the model without simulating time steps. Add `--parsing-strictness=high` to also fail on unsupported keywords. |
| `opm-flow.simulator.runArgs` | `[]` | Extra arguments for **Run Simulation** (e.g. `--output-dir=...`, `--threads-per-process=4`). The deck file is supplied automatically. |

### Completion

| Setting | Default | Description |
Expand Down Expand Up @@ -389,6 +424,13 @@ The language is registered as `opm-flow`.

### Unreleased

- **Verify and run the simulation (optional)** — new **OPM Flow: Verify Deck
(dry run)** and **OPM Flow: Run Simulation** commands launch a locally
installed `flow` binary on the open deck in an integrated terminal. Verify uses
flow's dry-run mode to confirm the deck (and its `INCLUDE` / `PATHS` files)
loads without solving. Configure via the new `opm-flow.simulator.*` settings;
Windows users can target a WSL distribution, with deck paths translated to
`/mnt/<drive>` automatically.
- **UDQ and ACTIONX support** — the `UDQ` expression sub-language and `ACTIONX`
action blocks are now recognised. Syntax highlighting scopes UDQ control
words, UDQ functions, and expression operators, plus the `ACTIONX` /
Expand Down
72 changes: 64 additions & 8 deletions vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,29 +145,47 @@
{
"command": "opm-flow.openPrtFile",
"title": "OPM Flow: Open PRT File"
},
{
"command": "opm-flow.verifyDeck",
"title": "OPM Flow: Verify Deck (dry run)"
},
{
"command": "opm-flow.runSimulation",
"title": "OPM Flow: Run Simulation"
}
],
"menus": {
"editor/context": [
{
"command": "opm-flow.openPrtFile",
"when": "resourceLangId == opm-flow && resourceExtname =~ /^\\.data$/i",
"group": "opm-flow"
},
{
"command": "opm-flow.alignRecordColumns",
"when": "resourceLangId == opm-flow",
"group": "opm-flow"
"group": "opmflow_1_format@1"
},
{
"command": "opm-flow.addColumnHeaders",
"when": "resourceLangId == opm-flow",
"group": "opm-flow"
"group": "opmflow_1_format@2"
},
{
"command": "opm-flow.toggleLineComment",
"when": "resourceLangId == opm-flow",
"group": "opm-flow"
"group": "opmflow_1_format@3"
},
{
"command": "opm-flow.openPrtFile",
"when": "resourceLangId == opm-flow && resourceExtname =~ /^\\.data$/i",
"group": "opmflow_2_navigation@1"
},
{
"command": "opm-flow.verifyDeck",
"when": "resourceLangId == opm-flow && resourceExtname =~ /^\\.data$/i",
"group": "opmflow_3_run@1"
},
{
"command": "opm-flow.runSimulation",
"when": "resourceLangId == opm-flow && resourceExtname =~ /^\\.data$/i",
"group": "opmflow_3_run@2"
}
]
},
Expand Down Expand Up @@ -266,6 +284,44 @@
"default": [],
"description": "Extra file extensions (with or without a leading '.') that should open as OPM Flow. Useful for project-specific include-file conventions not covered by the built-in extension list. Matched case-insensitively. Example: [\".myinc\", \"props2\"]. For one-off cases the VS Code-native `files.associations` setting still works too.",
"scope": "resource"
},
"opm-flow.simulator.executablePath": {
"type": "string",
"default": "flow",
"markdownDescription": "Path to the OPM Flow executable used by the **Verify Deck** and **Run Simulation** commands. When `#opm-flow.simulator.useWsl#` is enabled this must be a path *inside* WSL (e.g. `/usr/bin/flow`); otherwise a native path or a name on `PATH`.",
"scope": "resource"
},
"opm-flow.simulator.useWsl": {
"type": "boolean",
"default": false,
"markdownDescription": "Run the simulator through WSL (`wsl.exe`). Required on Windows, where OPM Flow is typically installed inside a Linux distribution. The deck's Windows path is translated to its `/mnt/<drive>` mount point automatically.",
"scope": "resource"
},
"opm-flow.simulator.wslDistribution": {
"type": "string",
"default": "",
"markdownDescription": "WSL distribution to run the simulator in (e.g. `ubuntu-26.04`). Leave empty to use the default distribution. List installed distributions with `wsl -l -v`. Only used when `#opm-flow.simulator.useWsl#` is enabled.",
"scope": "resource"
},
"opm-flow.simulator.verifyArgs": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"--enable-dry-run=true"
],
"markdownDescription": "Arguments passed to flow by **Verify Deck (dry run)**. The default `--enable-dry-run=true` fully parses the deck and initializes the model (grid, properties, wells, schedule) without simulating the time steps — a fast check that the deck and everything it pulls in via `INCLUDE` / `PATHS` loads cleanly. Add `--parsing-strictness=high` to also fail on unsupported keywords.",
"scope": "resource"
},
"opm-flow.simulator.runArgs": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Extra arguments passed to flow by **Run Simulation** (e.g. `--output-dir=...`, `--threads-per-process=4`). The deck file is supplied automatically.",
"scope": "resource"
}
}
}
Expand Down
116 changes: 116 additions & 0 deletions vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ import { parsePathsAliases, resolvePathAlias, prtCandidatePaths } from './paths'
import { DEFAULT_DIAGNOSTICS_EXCLUDED_KEYWORDS } from './diagnostics-exclusions';
import { buildKeywordSnippet } from './boilerplate';
import { classifyNameParam, collectDeckNames } from './names';
import {
buildDeckCommand,
SimulatorConfig,
SimulatorMode,
} from './simulator';

interface Parameter {
index: number | string;
Expand Down Expand Up @@ -1153,6 +1158,94 @@ async function retagDocumentIfMatches(
}
}

// ---------------------------------------------------------------------------
// Simulator integration — optional verify / run via a local `flow` binary
// ---------------------------------------------------------------------------

/** Read the `opm-flow.simulator.*` settings for the given resource. */
function getSimulatorConfig(resource?: vscode.Uri): SimulatorConfig {
const c = vscode.workspace.getConfiguration('opm-flow.simulator', resource ?? null);
return {
executablePath: c.get<string>('executablePath', 'flow'),
useWsl: c.get<boolean>('useWsl', false),
wslDistribution: c.get<string>('wslDistribution', '').trim(),
runArgs: c.get<string[]>('runArgs', []),
verifyArgs: c.get<string[]>('verifyArgs', ['--enable-dry-run=true']),
};
}

// A single reusable terminal, recreated when the required shell changes
// (e.g. the WSL distribution setting was edited).
let simulatorTerminal: vscode.Terminal | undefined;
let simulatorTerminalSignature: string | undefined;

/** Resolve the deck file to act on, or report why none is available. */
function resolveDeckTarget(resource?: vscode.Uri): vscode.Uri | undefined {
const target = resource ?? vscode.window.activeTextEditor?.document.uri;
if (!target || target.scheme !== 'file') {
vscode.window.showInformationMessage(
'OPM Flow: open a deck (.DATA) file to run or verify it.',
);
return undefined;
}
return target;
}

/** Launch flow on `target` in mode, in a (reused) integrated terminal. */
async function runSimulatorOnDeck(
mode: SimulatorMode,
target: vscode.Uri,
): Promise<void> {
const cfg = getSimulatorConfig(target);
const isWindows = process.platform === 'win32';

// Running a native Windows flow from the integrated terminal would route the
// POSIX command line through PowerShell/cmd. Steer the user to WSL instead.
if (isWindows && !cfg.useWsl) {
const pick = await vscode.window.showWarningMessage(
'OPM Flow: running the simulator on Windows requires WSL. Enable '
+ '"opm-flow.simulator.useWsl" and set the executable path (e.g. /usr/bin/flow).',
'Open Settings',
);
if (pick === 'Open Settings') {
await vscode.commands.executeCommand(
'workbench.action.openSettings', 'opm-flow.simulator',
);
}
return;
}

// WSL only exists on Windows; on Linux/macOS run flow natively even if the
// (portable) setting happens to be on.
const effectiveCfg: SimulatorConfig = { ...cfg, useWsl: cfg.useWsl && isWindows };
const cmd = buildDeckCommand(target.fsPath, mode, effectiveCfg);

// Reuse the terminal unless its shell no longer matches what we need.
if (simulatorTerminal && simulatorTerminal.exitStatus !== undefined) {
simulatorTerminal = undefined; // user closed it
}
if (simulatorTerminal && simulatorTerminalSignature !== cmd.shellSignature) {
simulatorTerminal.dispose();
simulatorTerminal = undefined;
}
if (!simulatorTerminal) {
simulatorTerminal = vscode.window.createTerminal({
name: 'OPM Flow',
shellPath: cmd.shellPath,
shellArgs: cmd.shellArgs,
});
simulatorTerminalSignature = cmd.shellSignature;
}

simulatorTerminal.show(true);
if (mode === 'verify') {
vscode.window.setStatusBarMessage(
`OPM Flow: verifying ${path.basename(target.fsPath)}…`, 4000,
);
}
simulatorTerminal.sendText(cmd.commandLine);
}

export function activate(context: vscode.ExtensionContext): void {
const index = loadKeywordIndex(context);
const keywords = Object.keys(index);
Expand Down Expand Up @@ -1677,6 +1770,29 @@ export function activate(context: vscode.ExtensionContext): void {
},
);

// --- Commands: verify (dry-run load check) / run the deck via `flow` ---
const verifyDeckCommand = vscode.commands.registerCommand(
'opm-flow.verifyDeck',
async (resource?: vscode.Uri) => {
const target = resolveDeckTarget(resource);
if (target) await runSimulatorOnDeck('verify', target);
},
);
const runSimulationCommand = vscode.commands.registerCommand(
'opm-flow.runSimulation',
async (resource?: vscode.Uri) => {
const target = resolveDeckTarget(resource);
if (target) await runSimulatorOnDeck('run', target);
},
);
context.subscriptions.push(
verifyDeckCommand,
runSimulationCommand,
vscode.window.onDidCloseTerminal(t => {
if (t === simulatorTerminal) simulatorTerminal = undefined;
}),
);

// --- File-reference link provider (INCLUDE / IMPORT / RESTART / GDFILE) ---
const fileLinkProvider = vscode.languages.registerDocumentLinkProvider(
'opm-flow',
Expand Down
Loading