Multi-GPU CFD · measured on EuroHPC

For the meshes that
don’t fit

One GPU holds about 20 million cells. Your production mesh is 150 million. CudaFlow keeps the entire solve resident across every GPU in the cluster — so the limit is the fleet, not the card.

147,449,586 cells on 16 A100s. Verified against the single-GPU reference to 2.9e-10.

Surface pressure on 147,449,586 cells, solved across 16 A100s. Spalart-Allmaras, rendered from the written OpenFOAM fields.

The ceiling nobody talks about

GPU CFD is usually benchmarked at a few million cells, because that is what fits on one card. Production external aerodynamics does not fit on one card — and a solver that cannot load the mesh has no speed at all.

Typical single-GPU CFD benchmark1 GPU
5 M cells

Comfortably resident. This is where single-card solvers are measured.

Practical ceiling of one A100-40GB1 GPU
20 M cells

Past roughly this point the device-resident field pool fails to allocate.

Capability study mesh2–16 GPUs
40 M cells

Out of memory on one A100. Runs on two, and 2.1× faster on sixteen.

DrivAerML — production automotive external aero16 GPUs
147.4 M cells

20 SIMPLE iterations in 744 s across 16 A100s. Job 5031974.

fits one GPUrequires multiple GPUs — where CudaFlow operates

147 M

cells solved across 16 GPUs

2.9e-10

agreement with single-GPU reference

29.9 s

to partition a 148 M-cell mesh

What running at this size requires

Scale is not one feature. It is the mesh reader, the partitioner, the transport and the control plane all holding at 150 million cells.

🧠

Resident across every GPU

Mesh, fields, matrix assembly, turbulence and the pressure multigrid stay in device memory across iterations — on all ranks. Halos move over NVLink and InfiniBand via NVSHMEM; nothing round-trips through the host between iterations.

🧩

No decomposePar step

The domain is partitioned in core at startup with distributed graph partitioning. A 148 M-cell mesh partitions in 29.9 s across 16 GPUs, and the result is cached per mesh and GPU count, so repeat runs skip it entirely.

🛡️

Reads the meshes your preprocessor writes

Commercial meshers do not always follow the reference conventions. We found and fixed two cases where an ANSA-written polyMesh broke a reader that synthetic test meshes sail straight through. Malformed addressing is now rejected at load with counts, not three layers down inside the partitioner.

Correctness gated before timing

No performance number is reported from a build that has not first reproduced the single-GPU reference field by field. Distributed velocity agrees to 2.9e-10 on two GPUs and 4.0e-10 on four.

📐

Refuses rather than approximates

An unsupported scheme, boundary condition or fvOption stops the run with the reason. A solver that quietly substitutes a different discretisation is worse than one that will not start.

📊

Metered per GPU-second

Nodes register over one HTTPS call, jobs are claimed straight from Postgres with SELECT FOR UPDATE SKIP LOCKED, and usage is metered per GPU-second including startup. No queue service, no background sweepers.

What we don’t sell you

You will benchmark this yourself. Better that you hear the limits from us first.

We do not sell speedup

On a mesh that already fits one GPU, sixteen GPUs return about 1.04×. Per-iteration cost is dominated by blocking global reductions that do not shrink with subdomain size. If your case fits on one card, run it on one card.

We are not the fastest single-GPU solver

That is a different problem, and it is well served by others. Our value begins exactly where a single device runs out of memory.

A demonstration run is not a validated prediction

Our 147 M-cell result is steady SIMPLE; the published setup for that case is transient SA-DDES. It shows the machinery holds at full industrial size — it is not a drag coefficient.

Bring us the mesh that won’t load

If your case is too large for a single GPU, that is the case we want to see.

Start free