Under-Relaxation

Under-relaxation stabilises iterative solvers by dampening updates between outer iterations. NeoN provides two complementary forms: matrix (equation) relaxation and field (explicit) relaxation.

Matrix Under-Relaxation

Matrix relaxation modifies the assembled linear system in-place before solving so that the solution is pulled only partially toward the new iterate. Given an under-relaxation factor alpha (0, 1), it strengthens the diagonal for numerical stability and adds a source correction so the fixed point of the relaxed system equals the fixed point of the unrelaxed one.

API: dsl::applyMatrixRelaxation(ls, solution, alpha)

The function operates on the augmented diagonal — boundary contributions are permanently baked into matrix.values[diagIdx(cell)] at assembly time. Per cell the kernel computes:

D_aug     = matrix.values[diagIdx(cell)]           (boundary already included)
D_dom     = max(|D_aug|, sumMagOffDiag[cell])       (dominance clamp)
D_relaxed = copySign(D_dom / alpha, D_aug)          (scale by 1/alpha, keep sign)
matrix.values[diagIdx(cell)] = D_relaxed
rhs[cell] += (D_relaxed - D_aug) * psi_prev[cell]  (source correction)

The dominance clamp ensures diagonal dominance is not reduced below the off-diagonal magnitude sum. componentCopySign preserves the negative-diagonal sign convention so downstream operations such as rAU = V / diag remain correct for alpha < 1.

The source correction (D_relaxed - D_aug) * psi_prev cancels exactly at the converged solution, so the fixed point is unchanged.

alpha <= 0 or alpha == 1 is a bitwise no-op.

The implementation uses a single fused kernel: the off-diagonal sum, dominance clamp, diagonal update, and source correction are computed in one pass over each cell’s CSR row. There is no scratch allocation.

Field (Explicit) Under-Relaxation

Field relaxation blends the current field value toward the previous outer iteration, independently of the linear system. It is typically applied after the momentum equation is solved and before extracting rAU/HbyA (SIMPLE/PISO outer loop).

API: dsl::applyFieldRelaxation(solution, previous, alpha)

psi[c] = prev[c] + alpha * (psi[c] - prev[c])

Only the internal vector is blended; boundary conditions must be re-evaluated by the caller via solution.correctBoundaryConditions().

alpha <= 0 or alpha == 1 is a bitwise no-op.

Snapshot helper: dsl::fieldRelaxationSnapshot(field)

Returns an executor-correct deep copy of field.internalVector() to be stored as the start-of-iteration snapshot and passed to applyFieldRelaxation. Subsequent writes to field do not affect the snapshot.

Typical usage pattern:

// At the start of each outer iteration, snapshot the current field.
auto prevU = dsl::fieldRelaxationSnapshot(U);

// ... solve momentum equation ...

// Apply field relaxation.
dsl::applyFieldRelaxation(U, prevU, alphaU);
U.correctBoundaryConditions();