Cell size is treated across the industry as a cost setting — coarser is cheaper, finer is better, pick one. It is not. The grid silently rewrites your geometry, and the rewrite lands directly in the flow areas your escape strategy depends on.
An opening is declared in metres and solved in cells. No solver can cut half a cell, so every opening is snapped — and the snapped opening is what actually gets solved. Ours had recorded both the declared and emitted area from the beginning. Nothing had ever compared them.
When we finally did, the numbers were worse than expected, and worst in exactly the place that looks cheapest. Width and height were each being rounded to the nearest cell independently, so the two errors compounded into the area — and area is what flow actually scales with.
At the resolution we validate against, the worst opening in the building went from 20.6% off declared to 4.8%. That is not a tuning improvement. It is the difference between a document that quotes the drawing and a document that quotes what was solved.
Same building, same openings, six resolutions. This is the table that decides which grid a job can honestly be run at — and it is per building, because the limit is set by the smallest opening in it.
| Cell size | Before | After | Verdict |
|---|---|---|---|
| 0.25 m | 4.8% | 4.8% | Note — clean |
| 0.40 m | 23.8% | 8.6% | Note — clean |
| 0.50 m | 20.6% | 4.8% | Note — clean |
| 0.60 m | 37.1% | 28.0% | Blocked — see below |
| 0.75 m | 33.9% | 12.5% | Warning — stated in the document |
| 1.00 m | 20.6% | 19.0% | Warning — stated in the document |
0.60 m still blocks, and it should. That building has a 1.0 m² stair-head automatic vent, which is 1.67 cells per side on a 0.6 m grid — under three cells of area in total. No snapping rule recovers that, and no honest document can be written from it. A building has a coarsest usable grid; the system's job is to find it and say so, not to accept the setting and produce a number anyway.
Large models are split across many meshes so they can be solved in parallel. Where those boundaries fall is not cosmetic. A horizontal mesh boundary across a smoke shaft seals it — the shaft stops working, the strategy it belongs to stops working, and the run completes without complaint. A vertical boundary through the same shaft is harmless, because it runs along the flow rather than across it.
Ashbeck's partitioner treats shaft columns as intervals that may only ever grow. A refinement that would cut a shaft is refused outright rather than applied with a warning, because there is no version of that trade-off worth taking: a wider column costs cells, and a sealed shaft costs the answer.
Alignment between meshes is enforced before dispatch, not discovered during it. Neighbouring meshes must share cell faces at integer ratios; a misalignment is a hard error in the solver and, on a metered cloud runner, it is an error you pay for after the instance is already running.
Refinement exists to resolve something more finely. A refinement that removes the thing it was meant to resolve has failed at its only job.
An opening cut through a wall has to overspan that wall to cut cleanly — and the overspan is a function of cell size. On a coarse grid it grows until two doors near a corner physically intersect, at which point the solver refuses the entire file. It reports that at solver start-up, which on a cloud runner is after the instance is up and billing.
That is the fortunate case, because at least it stops. The dangerous case is the resolution where the geometry does not collide and the doors are simply a third too big. Ashbeck now emits timed openings as a permanent aperture plugged by a removable leaf, so the condition that causes the refusal cannot arise at any cell size, and checks the emitted file for the invariant regardless.
Then ask what the declared door areas became at that resolution. It is a reasonable question and it is very rarely answerable — which is most of the reason we built this.