3. Local sensitivity methods
Local sensitivity analysis answers a narrow but essential question: if a single design parameter changes by a small amount, how much does a scalar response of interest change? In CAE solvers this response is typically a compliance, a peak stress, a natural frequency, a mass, or a heat flux, and the parameter might be a shell thickness, a material modulus, or a fillet radius. The gradient of that response with respect to each design variable is the raw material for optimization, for design ranking, and for uncertainty screening. Three families of methods dominate practice: finite differences, the complex-step derivative, and adjoint formulations. Each trades accuracy, cost, and implementation effort differently, and choosing among them is one of the most consequential decisions in a sensitivity workflow.
Finite Differences: The Baseline
The finite-difference (FD) approach perturbs each design variable by a step h, re-runs the solver, and forms a difference quotient. The forward difference is
∂f/∂x_i ≈ ( f(x + h·e_i) − f(x) ) / h
and the central difference is
∂f/∂x_i ≈ ( f(x + h·e_i) − f(x − h·e_i) ) / (2h)
where e_i is the unit vector for variable i. Forward differences need one extra solve per variable; central differences need two. For a model with n design variables, the total cost is roughly (n+1) or (2n+1) solver runs. That scaling is the method's central weakness: it is cheap for a handful of variables and prohibitive for hundreds.
Accuracy is governed by the step size. Too large, and truncation error from the neglected higher-order terms dominates; too small, and subtractive cancellation in floating-point arithmetic destroys the signal. For a response of order f ≈ 1 in double precision, forward differences typically bottom out around h ≈ 10⁻⁸ with relative error near 10⁻⁸, while central differences reach about 10⁻¹⁰. A practical rule is to scale the step to the variable: h_i = ε · max(|x_i|, x_ref) with ε ≈ 10⁻⁶ to 10⁻⁴, and to verify by re-running with h/2 and checking that the gradient changes by less than a few percent. If it does not, the step is in the wrong regime.
Complex-Step Differentiation: Accuracy Without Subtraction
The complex-step (CS) method evaluates the response at a complex-valued perturbation and extracts the imaginary part:
∂f/∂x_i ≈ Im( f(x + i·h·e_i) ) / h
Because there is no subtraction of two nearly equal real numbers, the cancellation error that limits FD disappears. The step can be made extremely small—h ≈ 10⁻²⁰ is common—and the result is accurate to machine precision, with truncation error of order h². For a well-implemented solver, CS gradients typically agree with analytic derivatives to 10–12 significant digits.
The catch is that the entire analysis chain must support complex arithmetic. Every operation in the solver—element stiffness assembly, material constitutive laws, boundary conditions, post-processing—must be analytic in the complex sense. Branches such as abs(), max(), and conditional switches on real comparisons break the method, because they are not complex-differentiable. In practice, CS is implemented by templating the solver on the scalar type and replacing non-analytic constructs with smooth equivalents (for example, sqrt(x*x + ε) instead of abs(x)). Many research and commercial codes now offer a complex-step mode for exactly this reason.
Cost is one complex solve per variable, comparable to forward FD but with far better accuracy. For a model with 20 variables, CS is often the pragmatic choice: it avoids the step-size tuning ritual and delivers gradients clean enough to drive a gradient-based optimizer without additional filtering.
Adjoint Formulations: Cost Independent of Variable Count
The adjoint method reverses the perspective. Instead of perturbing each input and measuring the output, it solves one additional linear system—the adjoint system—and then computes the gradient of a scalar response with respect to all design variables at once. For a discretized system K(x)·u = f(x) with response g = g(u, x), the total derivative is
dg/dx = ∂g/∂x + λᵀ ( ∂f/∂x − ∂K/∂x · u )
where the adjoint vector λ solves
Kᵀ · λ = −(∂g/∂u)ᵀ
The key observation: λ depends on the response but not on the design variable index. One adjoint solve yields the full gradient vector, so the cost is essentially one extra linear solve regardless of whether there are 10 or 10,000 design variables. This is why adjoint methods underpin large-scale shape and topology optimization.
The price is implementation complexity. The adjoint requires the transpose of the tangent stiffness matrix, the partial derivatives ∂K/∂x and ∂f/∂x, and careful handling of the response derivative ∂g/∂u. For nonlinear problems the adjoint must be formulated around the converged state, and for transient or eigenvalue problems the adjoint becomes a backward-in-time or generalized eigenvalue problem. Memory can also be a constraint: storing the factorization of Kᵀ for large models is expensive, though checkpointing and iterative adjoint solvers mitigate this.
Choosing a Method in Practice
The decision usually reduces to a simple ratio: number of responses versus number of design variables. If you have few variables and many responses, forward or central FD is adequate and trivial to implement. If you have many variables and few responses—the typical optimization setting—adjoint is the only scalable option. Complex-step sits in between: it handles moderate variable counts with excellent accuracy and modest implementation effort, provided the solver supports complex arithmetic.
A useful verification habit is to cross-check methods on a reduced problem. Compute the gradient of one response with respect to three variables using both FD and CS, and confirm agreement to at least six digits. If they diverge, the FD step is likely mistuned or the solver is noisy. For adjoint implementations, the standard check is a directional derivative test: pick a random direction d, compare (g(x + εd) − g(x))/ε against (dg/dx)ᵀ d, and require agreement to a few significant digits. This catches sign errors and missing terms in ∂K/∂x before they contaminate an optimization run.
The figure summarizes the trade-off surface. Finite differences scale linearly with variable count and are the easiest to bolt onto an existing black-box solver, but they demand step-size discipline and are vulnerable to solver noise. Complex-step removes the cancellation problem and delivers near-exact gradients at a cost similar to forward FD, contingent on complex-arithmetic support. Adjoint methods break the linear scaling entirely, making gradients affordable for thousands of variables, at the cost of intrusive implementation and the need for consistent partial derivatives. In a mature CAE sensitivity workflow, all three coexist: FD for quick sanity checks, CS for moderate-dimensional design studies, and adjoint for large-scale optimization.