# Navier-Stokes solver

*The Navier-Stokes solver controls the compressible flow equations in Flow360. It determines how the simulation solves for fluid velocity, pressure, and density throughout the domain. Proper configuration of these parameters is essential for achieving accurate and converged solutions.*

---

## Available Parameters

| *Parameter* | *Description* | *Applicable* |
|-------------|---------------|--------------|
| [**Absolute tolerance**](#absolute-tolerance) | Convergence criteria for NS residual | always |
| [**Relative tolerance**](#relative-tolerance) | Relative residual reduction for unsteady cases | always |
| [**Kappa MUSCL**](#kappa-muscl) | Parameter controlling the upwind/downwind/central scheme influence | always |
| [**Order of accuracy**](#order-of-accuracy) | Spatial discretization order (1st or 2nd) | `Advanced` |
| [**Equation evaluation frequency**](#equation-evaluation-frequency) | Frequency of solving equations | `Advanced` |
| [**Linear solver**](#linear-solver) | Linear solver selection: Normal (default) or Krylov | `Advanced` |
| [**Linear solver - max iterations**](#linear-solver-max-iterations) | Maximum number of linear solver iterations or Krylov iterations | `Advanced` |
| [**Linear solver - max preconditioner iterations**](#linear-solver-max-preconditioner-iterations) | Number of preconditioner sweeps per Krylov iteration | `Advanced`, **Linear solver** is `Krylov` |
| [**Linear solver - relative tolerance**](#linear-solver-relative-tolerance) | Relative tolerance for the Krylov linear solver convergence | `Advanced`, **Linear solver** is `Krylov` |
| [**CFL multiplier**](#cfl-multiplier) | Factor applied to CFL specified in Time Stepping | `Advanced` |
| [**Numerical dissipation factor**](#numerical-dissipation-factor) | Controls dissipation | `Advanced` |
| [**Limit velocity**](#limit-velocity) | Whether to apply limiters to velocity | `Advanced`, **Linear solver** is `Normal` |
| [**Limit pressure density**](#limit-pressure-density) | Whether to apply limiters to pressure/density | `Advanced`, **Linear solver** is `Normal` |
| [**Solve type**](#solve-type) | Compressible or incompressible formulation | `Advanced` |
| [**Low Mach preconditioner**](#low-mach-preconditioner) | Acceleration technique for low Mach flows | `Advanced` |
| [**Low Mach preconditioner threshold**](#low-mach-preconditioner-threshold) | Minimum Mach number for preconditioner scaling | `Advanced`, **Low Mach preconditioner** is `On` |
| [**Update Jacobian frequency**](#update-jacobian-frequency) | How often to update the Jacobian matrix | `Advanced` |
| [**Max force jacobian update physical steps**](#max-force-jacobian-update-physical-steps) | Controls Jacobian matrix update criteria | `Advanced` |
| [**Line search - residual growth threshold**](#line-search-residual-growth-threshold) | Pseudotime nonlinear residual norm convergence ratio above which residual norm increase is allowed | `Advanced`, **Linear solver** is `Krylov` |
| [**Line search - max residual growth**](#line-search-max-residual-growth) | Hard cap on the residual norm ratio — never allow the residual norm to grow beyond this factor over a single pseudotime step | `Advanced`, **Linear solver** is `Krylov` |
| [**Line search - activation step**](#line-search-activation-step) | Pseudotime step threshold before the max residual growth limit is activated | `Advanced`, **Linear solver** is `Krylov` |

---

## Detailed Descriptions

### General

#### Absolute tolerance

*The primary convergence metric for how far to reduce the Navier-Stokes residuals.*

- **Default:** `1.0e-10`
- **Example:** `1.0e-8`
>**Notes:** 
>- **Steady state**: the simulation runs pseudo-steps until the residual falls below this threshold (or the maximum number of steps is reached).
>- **Unsteady**: within each physical step, pseudo-steps are run until the residual falls below this threshold.
>- For steady cases, at least 5 orders of magnitude reduction is recommended.
>- Smaller values result in more accurate solutions but may require more iterations.

#### Relative tolerance

*Relative residual reduction target for the Navier-Stokes equations per physical step.*

- **Default:** `0.0`
- **Example:** `1e-2`
>**Notes:** 
>- Applicable only to **unsteady** simulations.
>- Specifies the required reduction relative to the initial residual of each physical step (e.g., `1e-2` means the residual must drop by two orders of magnitude).
>- Within each physical step, pseudo-steps are run until this relative reduction is achieved, the absolute tolerance is met, or `max_pseudo_steps` is reached — whichever comes first. The solver then advances to the next physical step.

#### Kappa MUSCL

*Controls the blending between upwind and central schemes in the spatial discretization.*

- **Default:** `-1` (second-order upwind scheme)
- **Example:** `0.33` (blended scheme for low dissipation)
>**Notes:** 
>- Range: [`-1`, `1`] (values above `0.33` not recommended)
>- `-1`: Pure second-order upwind (most stable)
>- `0.33`: Blended upwind/central scheme (less dissipative, good for low subsonic flows)
>- `1.0`: Unstable full downwind scheme (avoid)

### Advanced

#### Order of accuracy

*Determines whether the solver uses 1st or 2nd order spatial discretization.*

- **Default:** `2` (second order)
- **Example:** `1` (first order for initialization)
>**Notes:** 
>- 1st order: More stable but more dissipative, less accurate
>- 2nd order: More accurate but less stable
>- For initial flow field development in unsteady cases with rotating components, running 1st order for 1-2 revolutions can help establish the flow before switching to 2nd order
>- Should match the turbulence model's order of accuracy

#### Equation evaluation frequency

*Controls how often (in pseudo steps) the Navier–Stokes equations are solved.* 

- **Default:** `1`
- **Example:** `2`–`4`
>**Notes:** 
>- `1` provides the most robust convergence
>- Higher values can reduce time cost but may slow down convergence

#### Linear solver

*Selects the linear solver used at each pseudo-step of the Navier-Stokes equations.*

**Possible selections:**
- `Normal` *(default)* — Standard iterative linear solver. Suitable for all simulation types including unsteady.
- `Krylov` — Krylov iterative solver. Offers improved convergence speed and reduced iteration counts for steady-state simulations. 

> ⚠️ **Krylov solver restrictions:**
> - **Steady simulations only.** The Krylov solver cannot be used with Unsteady time stepping.
> - **Incompatible with velocity and pressure/density limiters.** Do not enable `Limit velocity` or `Limit pressure density` when using the Krylov solver.
> - **Kappa MUSCL = 0.33 may cause convergence stalling.** If residuals stall at final convergence levels, try `Kappa MUSCL` values of `0` or `-0.33` instead.

#### Linear solver - max iterations

*Maximum number of linear solver iterations per pseudo-step.*

- **Default:** `30` (Normal solver) / `15` (Krylov solver)
- **Range:** `1`–`50` (Krylov solver); no upper bound for Normal solver
- **Example:** `50` (Normal) / `20` (Krylov)
>**Notes:**
>- **Normal solver:** Typical range is 25–35 for the NS solver. Increase to 50–55 if the linear residual reduction ratio is insufficient.
>- **Krylov solver:** Controls the number of the Krylov iterations used per pseudo-step. Start with the default of `15`. If convergence is stalled, try increasing this value. If the case converges well, try reducing it for better performance.

#### Linear solver - max preconditioner iterations

*Number of preconditioner iterations applied during each Krylov iteration.*

*Applicable when **Linear solver** is `Krylov`.*

- **Default:** `25`
- **Range:** `10`–`35` (typical)
- **Example:** `30`
>**Notes:**
>- More iterations improve preconditioner quality but increase cost per iteration.
>- Try increasing this value before increasing **max iterations** if convergence is insufficient.

#### Linear solver - relative tolerance

*Target relative convergence tolerance for the Krylov linear solver. The solver considers the linear system converged when the residual drops below this fraction of the initial residual.*

*Applicable when **Linear solver** is `Krylov`.*

- **Default:** `0.05`
- **Example:** `0.01`
>**Notes:**
>- Tighter tolerances (smaller values) improve accuracy but require more iterations.
>- The adaptive CFL strategy uses this tolerance to decide when to increase or limit the CFL. Setting this value too low may prevent CFL from ramping up effectively.
>- For low Mach number cases, values as low as `1e-3` may be needed for good convergence.

#### CFL multiplier

*Scaling factor applied to the CFL number defined in Time Stepping.*

- **Default:** `1.0`
- **Example:** `0.5` (for better stability)
>**Notes:** 
>  - Reducing this value can help stability for challenging cases
>  - Effective CFL = Time Stepping CFL × CFL Multiplier

#### Numerical dissipation factor

*Factor that controls the reduction in numerical flux dissipation for the low-dissipation Roe scheme.*

- **Default:** `1.0` (standard scheme)
- **Example:** `0.2` (low dissipation)
>**Notes:** 
>- Range: [`0.01`, `1.0`]
>- Recommended value: `0.2` for low-dissipation

#### Limit velocity

*Applies limiters to velocity to enhance stability in transonic/supersonic flows.*

- **Default:** `False`
- **Example:** `True` (for transonic flows)
- **Applicable:** when **Linear solver** is `Normal`
>**Notes:** 
>- Enable for transonic or supersonic cases
>- Can help prevent divergence in regions with strong shocks

#### Limit pressure density

*Applies limiters to pressure and density to enhance stability in transonic/supersonic flows.*

- **Default:** `False`
- **Example:** `True` (for transonic flows)
- **Applicable:** when **Linear solver** is `Normal`
>**Notes:** 
>- Enable for transonic or supersonic cases
>- Can help prevent negative pressures or densities near shocks

#### Solve type

*Selects the formulation used for the Navier-Stokes equations.*

**Possible selections:**
- `Compressible` *(default)* — Standard compressible Navier-Stokes formulation. Suitable for all Mach number regimes.
- `CompressibleIsentropic` — Applies mass and momentum conservation with the isentropic assumption for low-speed flow. Recommended for low Mach number applications to speed up the solver.

>**Notes:**
>  - For most simulations, the default `Compressible` setting is appropriate.
>  - `CompressibleIsentropic` is automatically applied for `LiquidOperatingCondition` regardless of the value of this field.
>  - `CompressibleIsentropic` is not compatible with temperature-dependent gas properties; only constant specific heat ratio (CPG) models are supported.

#### Low Mach preconditioner

*Improves solver convergence for low Mach number flows by addressing the stiffness in the equation system.*

- **Default:** `False`
- **Example:** `True` (for flows with $M <$ `0.3`)
>**Notes:** 
>- Recommended for simulations with Mach number below `0.3`
>- Particularly beneficial for nearly incompressible flows
>- Can significantly accelerate convergence for low-speed aerodynamics

#### Low Mach preconditioner threshold

*Minimum Mach number used in the preconditioner scaling.*

*Applicable when **Low Mach preconditioner** is `On`.*

- **Default:** Freestream Mach number (if not specified)
- **Example:** `0.1`
>**Notes:** 
>- For flow regions with Mach numbers smaller than this threshold, the threshold value is used
>- Only relevant when low Mach preconditioner is enabled

#### Update Jacobian frequency

*Controls how often the Jacobian matrix is updated during the solution process.*

- **Default:** `4` (every 4 pseudo-steps)
- **Example:** `1` (every pseudo-step for challenging cases)
>**Notes:** 
>- Smaller values improve robustness but increase computational cost
>- Reducing to `1` may help convergence for difficult cases
>- May slow the solver by up to approximately 30% when set to `1`

#### Max force jacobian update physical steps

*If the current physical step is lower than this value, jacobian matrix will be updated at every pseudo step.*

- **Default:** `0` (disabled)
- **Example:** `10`
>**Note:** Useful for cases where more stability is needed at the beginning

#### Line search - residual growth threshold

*Pseudotime nonlinear residual norm convergence ratio above which residual norm increase is allowed. This ensures the residual norm only increases when the pseudotime residual has decreased by at least the specified ratio, preventing non-physical steps from degrading convergence.*

- **Default:** `0.85`
- **Range:** `0.5`–`1.0`
- **Applicable:** when **Linear solver** is `Krylov`
>**Note:** The line search works together with the adaptive CFL strategy — CFL is only increased when full steps are accepted and is reduced when steps need to be scaled back.

#### Line search - max residual growth

*Hard cap on the allowed residual growth ratio per step. The line search ensures the residual does not grow by more than this factor in a single step, rejecting or scaling back updates that exceed it.*

- **Default:** `1.1`
- **Range:** ≥ `1.0`
- **Applicable:** when **Linear solver** is `Krylov`

#### Line search - activation step

*Pseudo-step number after which the max residual growth limit is enforced.*

- **Default:** `100`
- **Example:** `200`
- **Applicable:** when **Linear solver** is `Krylov`
>**Notes:**
>- Delays enforcement of the max residual growth limit during early pseudo-steps.
>- Particularly useful for angle-of-attack sweeps with restart, where residuals typically climb at the beginning of the simulation before settling down.

---

<details>
<summary><h3 style="display:inline-block"> 💡 Tips</h3></summary>

- For steady-state simulations:
  - Focus on absolute tolerance for convergence criteria
  - Consider using low Mach preconditioner for low-speed flows
  - Use 2nd order accuracy for final results

- For unsteady simulations:
  - Use relative tolerance (typically 1e-2 or 1e-3)
  - For cases with rotating components, consider starting with 1st order for initialization
  - Use AdaptiveCFL instead of ramp CFL strategy to automatically balance stability and convergence speed

- For low-dissipation schemes:
  - Start from a converged steady or unsteady solution
  - Aim for at least a two-order-of-magnitude reduction in nonlinear residuals
  - Consider halving the time step size
  - Use Kappa MUSCL = 1/3 for optimal accuracy
  - Set update Jacobian frequency = 1 for better stability

- For transonic or supersonic flows:
  - Enable limiters for velocity and pressure/density (not available with **Krylov** linear solver)
  - Use a more dissipative scheme (Kappa MUSCL = -1)

- For steady simulations where convergence is slow or stalls:
  - Consider switching the linear solver from Normal to **Krylov**
  - Start with the default Krylov settings (max iterations = 15, max preconditioner iterations = 25, relative tolerance = 0.05)
  - If residuals stall when using Kappa MUSCL = 0.33 with the Krylov solver, try values of 0 or -0.33
  - For low Mach number cases with the Krylov solver, try lowering the relative tolerance (e.g., to 1e-3) if convergence is difficult

- For Krylov cases that struggle to converge, adjust the line search parameters:
  - If the CFL is being cut too aggressively, increase the residual growth threshold (e.g., from 0.85 toward 1.0) to allow more residual growth
  - If residuals stall or oscillate excessively, lower the residual growth threshold to enforce stricter convergence per step
</details>

---

<details>
<summary><h3 style="display:inline-block"> ❓ Frequently Asked Questions</h3></summary>

- **When should I use 1st order vs. 2nd order accuracy?**  
  > Use 1st order for initial flow field development or for very challenging cases with strong discontinuities. Use 2nd order for final production runs to achieve accurate results. For unsteady simulations with rotating components, consider starting with 1st order for 1-2 revolutions before switching to 2nd order.

- **How do I know if my simulation has converged properly?**  
  > For steady simulations, look for at least 5 orders of magnitude reduction in residuals and stable force coefficients. For unsteady simulations, ensure residuals drop by 2-3 orders of magnitude within each physical time step.

- **What can I do if my simulation is diverging?**  
  > Try the following: (1) Use AdaptiveCFL instead of manual CFL settings to automatically adjust for stability, (2) Reduce the convergence limiting factor for complex cases with strong gradients or discontinuities, (3) Reduce the CFL number if using ramp CFL settings, (4) Switch to 1st order accuracy temporarily, (5) Enable limiters for transonic/supersonic flows, (6) Update the Jacobian more frequently (set update_jacobian_frequency = 1), (7) Check the mesh quality, particularly in high-gradient regions.

- **When should I use the low-dissipation scheme?**  
  > Use it for unsteady simulations where capturing fine flow structures is important, especially for aeroacoustics, turbulent flows, and vortex-dominated flows. It works best for low Mach number flows and should be started from a well-developed solution.

- **How does the Kappa MUSCL parameter affect my solution?**  
  > Kappa_MUSCL = -1 (default) gives a stable upwind scheme with higher numerical dissipation. Kappa MUSCL = 0.33 gives a less dissipative scheme that preserves flow features better but may be less stable. For low-speed flows without strong shocks, Kappa MUSCL = 0.33 often provides better results.

- **What's the difference between absolute and relative tolerance?**  
  > **Absolute tolerance** sets a fixed residual threshold and applies to both steady and unsteady simulations. For steady simulations it is the sole convergence driver: pseudo-steps run until the residual drops below this value. **Relative tolerance** applies only to unsteady simulations and specifies a required reduction relative to the initial residual of each physical step (e.g., `1e-2` → two orders of magnitude drop). Within each physical step, pseudo-steps run until either tolerance is met or `max_pseudo_steps` is reached, then the solver advances to the next physical step.

- **When should I use the Krylov linear solver instead of the Normal solver?**
  > The Krylov solver offers improved convergence speed and reduced iteration counts for steady-state simulations. It automatically enables an enhanced adaptive CFL strategy and adjusts equation evaluation frequencies for optimal performance. It is **only available for Steady simulations** and cannot be combined with `Limit velocity` or `Limit pressure density`. For unsteady cases, continue using the Normal solver.

</details>

---

<details>
<summary><h3 style="display:inline-block"> 🐍 Python Example Usage</h3></summary>

Below is a Python code example showing how to configure the Navier-Stokes solver:

```python
import flow360 as fl

# Basic configuration for the Navier-Stokes solver
ns_solver = fl.NavierStokesSolver(
    absolute_tolerance=1e-10,
    order_of_accuracy=2
)

# Configuration for a low-speed simulation
ns_solver_low_mach = fl.NavierStokesSolver(
    absolute_tolerance=1e-10,
    low_mach_preconditioner=True,
    low_mach_preconditioner_threshold=0.1,
    kappa_MUSCL=0.33
)

# Configuration for an unsteady simulation with low dissipation
ns_solver_unsteady = fl.NavierStokesSolver(
    absolute_tolerance=1e-10,
    relative_tolerance=1e-3,
    numerical_dissipation_factor=0.2,
    update_jacobian_frequency=1,
    linear_solver=fl.LinearSolver(max_iterations=50)
)

# Configuration for a transonic/supersonic flow
ns_solver_transonic = fl.NavierStokesSolver(
    absolute_tolerance=1e-10,
    limit_velocity=True,
    limit_pressure_density=True,
    CFL_multiplier=0.5
)

# Configuration using the Krylov linear solver for steady simulations
# NOTE: Krylov solver is only valid with Steady time stepping
# NOTE: Do NOT combine with limit_velocity=True or limit_pressure_density=True
ns_solver_krylov = fl.NavierStokesSolver(
    absolute_tolerance=1e-10,
    linear_solver=fl.KrylovLinearSolver(
        max_iterations=15,
        max_preconditioner_iterations=25,
        relative_tolerance=0.05,
    ),
)

# Krylov solver with optional line search for additional stability during early pseudo-steps
ns_solver_krylov_with_line_search = fl.NavierStokesSolver(
    absolute_tolerance=1e-10,
    linear_solver=fl.KrylovLinearSolver(
        max_iterations=15,
        max_preconditioner_iterations=25,
        relative_tolerance=0.05,
    ),
    line_search=fl.LineSearch(
        residual_growth_threshold=0.85,
        max_residual_growth=1.1,
        activation_step=100,
    ),
)
```

</details>
