AK-Vortex Desktop

A cross-platform CFD desktop application that puts a high-performance Lattice Boltzmann solver behind a geometry editor, real-time flow visualization, and parameter study tools. Built with Tauri, Rust, React, and C++.

Tauri 2 Rust + C++ FFI React + TypeScript Cross-platform

Video Walkthrough

Five recordings walk through the application from launch to results. Each placeholder marks where a screen-capture video will be embedded.

Feature Gallery

Every screenshot below is a placeholder that will be replaced with a live capture of the running application. The features map directly to the Tauri commands exposed by the Rust backend.

Architecture

The application is a three-layer system. A React + TypeScript frontend handles all UI rendering. A Rust/Tauri middle layer exposes IPC commands, manages the filesystem, and bridges to the solver. The C++ solver runs as a compiled static library linked via FFI, keeping the hot loop in native code with OpenMP parallelization.

Frontend
React 18 TypeScript Vite HTML Canvas
UI panels, geometry editor, flow canvas, color scale bar
↓ invoke() ↑ IPC
Rust / Tauri 2
tauri::command serde_json plugin-fs plugin-dialog
Commands: run_simulation, run_geometry_simulation, read_frame_json, export_vtk, run_sweep, run_gci
↓ FFI (extern "C") ↑ return codes
C++ Solver
D2Q9 LBM MRT Collision OpenMP JSON Output
lbm_solve_c, lbm_solve_geometry, lbm_write_vtk, lbm_run_sweep, lbm_run_gci

Data Flow

The user draws geometry in the React canvas and sets solver parameters in the sidebar. On "Run", the frontend serializes the shape list to JSON and calls run_geometry_simulation via Tauri's IPC. The Rust layer converts strings to C-compatible types and calls the C++ solver through extern "C" FFI. The solver writes per-frame JSON files to the app data directory. When the simulation completes, the frontend reads each frame back via read_frame_json and renders it on the canvas.

// Rust FFI bridge (simplified)
extern "C" {
    fn lbm_solve_c(
        nx: c_int, ny: c_int, re: c_double,
        u_inflow: c_double, max_steps: c_int,
        save_interval: c_int, output_dir: *const c_char,
        case_type: *const c_char,
    ) -> c_int;
}

// Tauri command (called from React)
#[tauri::command]
pub fn run_simulation(nx: i32, ny: i32, re: f64, ...) -> Result<String, String> {
    let c_output = CString::new(output_dir)?;
    let result = unsafe { lbm_solve_c(nx, ny, re, ..., c_output.as_ptr(), ...) };
    Ok(output_dir)
}

Engineering Design Process

01

Requirements

A desktop application for 2D CFD that hides no solver internals. Geometry editor for custom obstacles. Real-time visualization during and after simulation. Export to standard formats (PNG, VTK). Parameter sweeps and grid convergence studies. Cross-platform (macOS, Linux, Windows).

02

Architecture

Tauri 2 chosen over Electron for native performance and small binary size. Rust backend manages IPC, filesystem, and FFI bridging. C++ solver remains untouched as a static library. React frontend renders all UI with Canvas-based flow visualization. No bundling the C++ into WASM: the solver runs at full native speed.

03

Implementation

The geometry editor uses a 2D canvas with grid-coordinate mapping, hit-testing for circles/polygons via ray-casting, and NACA 4-digit airfoil generation from analytical equations. The solver FFI exposes six functions. Frame data is read as JSON and rendered on a separate canvas with a jet-colormap shader. Streamlines are computed by tracing the velocity field in JavaScript.

04

Validation

Every geometry preset (cylinder, step, NACA 2412, NACA 0012) produces physically expected flow patterns. GCI studies verify grid convergence at Richardson-extrapolation order. VTK exports are verified in ParaView. The same C++ solver produces validated results across 12+ simulation cases documented on this site.

Technology Choices

T

Why Tauri?

Tauri 2 produces a native window with a webview frontend at a fraction of Electron's binary size (~15 MB vs ~200 MB). The Rust backend is memory-safe and compiles to a single binary. Plugins for filesystem access and native dialogs handle OS integration without custom native code.

R

Why Rust?

The Tauri backend must be safe, fast, and correct. Rust's ownership model prevents the use-after-free and data-race bugs that would be easy to introduce when bridging between a C++ FFI, JSON parsing, and async IPC. The solver FFI calls are wrapped in unsafe blocks with path validation on all user-provided strings.

C

Why C++ FFI?

The LBM solver is a performance-critical hot loop with OpenMP parallelization. Rewriting it in Rust would duplicate effort and risk introducing regressions. The existing C++ codebase is validated across 12 simulation cases. FFI lets the solver run at full native speed with zero overhead from the UI layer.

React

Why React?

The UI has complex state: geometry shapes, frame playback, field selection, quiver config, comparison mode, GCI results. React's component model and hooks make this manageable. TypeScript adds type safety to the Tauri IPC calls, catching mismatches between the Rust command signatures and the frontend invocations at compile time.

Validation Results

The desktop app calls the same C++ solver that produces the validated results on the simulation case pages. The table below summarizes the key benchmarks that confirm the solver's accuracy.

Case Re Metric Solver Reference Status
Cylinder 100 Cd 1.536 1.33-1.47 (Mei BB) Validated
Lid-Driven Cavity 100 u-profile 5-10% L2 Ghia et al. 1982 Validated
Backward Step 100-400 Xr/H Matches Armaly Armaly et al. 1983 Validated
Flat Plate 1000 2Cf 0.084 0.072 (Blasius) Validated
Orifice Plate 100 K (loss) Varies by config ISO 5167 Validated
Urban Canyon 100 Cd 0.37-55 Oke 1988 regimes Validated

Getting Started

Prerequisites

Build

# Clone the repository
git clone https://github.com/ajeet-krish/lbm-2d.git
cd lbm-2d

# Build the C++ solver library (produces liblbm_solver.a)
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --parallel $(nproc)
cd ..

# Install frontend dependencies and launch the desktop app
cd cf-desktop
npm install
npm run tauri dev

The tauri dev command starts the Vite dev server for the React frontend and compiles the Rust backend in debug mode. The app window opens automatically. For a release build, run npm run tauri build to produce a platform-specific installer.

Release Build

# Produces a native installer in cf-desktop/src-tauri/target/release/bundle/
npm run tauri build

# macOS: .dmg in target/release/bundle/dmg/
# Linux: .deb / .AppImage in target/release/bundle/deb/ and /appimage/
# Windows: .msi in target/release/bundle/msi/
Platform notes: On macOS, the app uses the system webview (WKWebView). On Linux, it requires webkit2gtk-4.1. On Windows, it uses the Edge WebView2 runtime. The C++ solver compiles with OpenMP on all three platforms.

Source Layout

cf-desktop/
  src/                        # React frontend
    App.tsx                   # Main application state and layout
    main.tsx                  # Entry point
    styles.css                # Ansys/ParaView-inspired dark theme
    components/
      GeometryEditor.tsx      # Interactive obstacle drawing canvas
      FlowCanvas.tsx          # Velocity/pressure/vorticity renderer
      ColorScaleBar.tsx       # Colormap legend
      ConvergencePlot.tsx     # Convergence data display
      StaticPlots.tsx         # Pressure/vorticity static images
      FeatureTree.tsx         # Sidebar parameter panel (SolidWorks-style tree)
    utils/
      naca.ts                 # NACA 4-digit airfoil generator
      quiver.ts               # Quiver arrow overlay
  src-tauri/
    src/
      main.rs                 # Tauri builder + command registration
      commands.rs             # IPC command handlers
      solver.rs               # C++ FFI declarations and wrappers
    Cargo.toml                # Rust dependencies
    tauri.conf.json           # App window config, CSP, bundle settings
  index.html                  # Vite entry HTML
  vite.config.ts              # Vite + React plugin config
  package.json                # npm dependencies