Time to Complete: ~60 minutes
Your CFD skill level after: Bonkers
In this Tutorial
Welcome to this tutorial on how to setup an Adjoint sensitivity analysis within Luminary! On this page you'll learn a few important aspects related to Adjoint sensitivity and how it is exposed within Luminary:
What is an Adjoint sensitivity, and why would I want to run it?
Pre-requisites to run an Adjoint simulation
How to run an Adjoint simulation through the Luminary browser UI
How to post-process and interpret the results of an adjoint simulation
How to improve your design using this information
By the end of this Tutorial you'll understand what this screen is showing, how to get here yourself, and how it can be used to improve your vehicle design:
Additional resources and information about this capability can be found on the Adjoint documentation page.
Do this first!
This content is based on the Piper Cherokee aircraft tutorial, and we highly recommend that you have completed that tutorial before attempting this one. A successful simulation of the Piper is a pre-requisite for this tutorial, and it will not cover the foundational elements of the UI or workflow.
If you are already familiar with Luminary and/or CFD tools and simply want to get started, you can use the Piper Sample project as a starting point, as described in the pre-requisites section below.
Note: as we continuously and frequently improve the product, UI elements and locations of certain menus may shift from what is captured in this Tutorial. If you spot something is a bit off, please let us know via the Bug Report in the Help assistant!
Background
What is Adjoint sensitivity?
Adjoint is a computational technique to determine sensitivity of output values to input values of a simulation, also often referred to as derivatives of the output with respect to inputs. For example, in this aircraft tutorial an airframe designer may be interested in understanding the sensitivity of lift and drag (simulation outputs) to the flow velocity and angle-of-attack specified at the domain boundary condition (simulation inputs). The magnitude and sign of these sensitivities can guide engineers on how and what to change in their vehicle design to influence the outputs they care about.
Availability of Adjoint sensitivity analysis is not common amongst CFD codes, as these methods are complicated to implement (chain rule differentiation in the solver) and the computational resources required to perform the Adjoint solver are often larger than the standard simulation, also called the "primal" solve. At Luminary we have focused development of the Adjoint sensitivity due to the immense value it can provide customers, described in the following section.
Why run an Adjoint sensitivity?
A single simulation can provide a single value as an output, for example: Lift coefficient equals 0.34, or drag on the fuselage was 2.45N. Unfortunately it cannot inform designers how to improve on these outputs of interest, because it does not contain sensitivity information.
In order to produce sensitivities from these "primal" solutions, users often have to run many additional simulations to asses the discrete sensitivity. For example, run several simulations at nearby angles-of-attack to see how drag changes in relation to this particular input variable. As the number of inputs of interest increase, i.e., the dimensionality of the design space increases, assessing sensitivities in this manner can require an expensive database of simulations. The amazing value of an Adjoint is that sensitivity of an output to all of the inputs can be computed in only one additional simulation!
A common example of the "many inputs" you can compute sensitivity to is the discrete geometry (computational mesh) used to describe your vehicle. This technique can inform how to adjust the shape of your object (position of the nodes describing your geometry) to improve an output of interest. Because of this, Adjoint sensitivities are frequently leveraged in the design optimization community to improve a set of objective functions (outputs of interest).
Hopefully your interest has been piqued and now you're eager to get started on your own Adjoint runs within Luminary. Get started below!
Running An Adjoint Simulation
Running an Adjoint simulation in Luminary boils down to a few simple steps:
Ensure a well-converged "primal" solution exists
Specify the Output of interest
Run the Adjoint simulation
Once completed, interpreting the results is described later in this section.
Pre-requisites
1. Piper Cherokee Tutorial Project
If you have completed the Piper tutorial then you should already have a Project in your workspace with the appropriate mesh and even a result. Alternatively, you can start from the Sample project that shows up when you create a new project:
2. Well-Converged Primal Solution
Adjoint solutions are only available for steady-state simulations, and to run an Adjoint simulation in Luminary you must first have a well converged steady-state "primal" (standard) solution. "Well-converged" is somewhat subjective, but for external aerodynamics problems we recommend relative residual convergence of ~1e-5 for all flow variables.
Tip: Adjoint simulations are known to be sensitive to the convergence of the primal solution. When the primal solve has not converged as much as desired you can augment the Adjoint solve. This is discussed later in this tutorial and in the Adjoint documentation. In general, use whatever techniques you have at your disposal to improve the primal convergence - this will support more robust and stable Adjoint simulations.
In the Piper sample project there is a mesh loaded and a simulation result already available, however if you look at the residuals of this result you will see the convergence history is not "well converged" according to the criterion above:
To improve this we need to:
Remove any stopping conditions other than the maximum number of iterations
Increase the Maximum Iterations to a large number (6000), to allow for strong convergence
Run this simulation (~45 credits)
With no other changes, this already improves the residual convergence to ~1e-4 for the flow variables:
We can use this result as the "primal" solution for the Adjoint solve in the next step.
Running Adjoints in the browser UI
Changing Simulation Modes
This is a straightforward step where we will indicate that we want to perform an Adjoint simulation instead of the standard or primal simulation. In the General node in the Simulation Type dropdown, toggle from Primal to Adjoint:
Specify the Output objective
Once you select Adjoint, a second dropdown will appear that lets you choose the Adjoint Output quantity for which to assess the sensitivity. These are related to the Outputs you have already setup in the Settings tree. For this first Adjoint simulation let's use Lift:
Adjust Stopping Conditions
We then need to reduce the total number of iterations performed (20) as well as the threshold for the residual stopping conditions to the recommended value (1e-5). We also need to remove any additional Output-based stopping conditions, as these primal Outputs are effectively frozen for the Adjoint solver. Best practices for stopping conditions are further outlined in the Adjoint documentation.
Advanced Settings
Advanced settings of the Adjoint solver can be leveraged when the primal convergence may not be sufficient. As we saw above, the flow variables have not converged to the recommended ~1e-5, so we will leverage the advanced parameters to enhance the convergence of the Adjoint solver.
The advanced adjoint parameters can be accessed by clicking on the Physics node in the settings tree:
As recommended in the Advanced Settings section of the Adjoint documentation, we will use the Frozen Turbulence toggle for the Lift Adjoint, and adjust the Second-Order Damping Factor to 0.03.
Note: Because of the primal solution's lack of complete convergence, without adjusting these solver settings the Adjoint solver may stall, and you will receive an error message suggesting the above changes to your Adjoint settings.
Specifying Which Primal to Use
Before running the adjoint simulation, we need to ensure we are using the settings and initialization that are associated with the primal solution.
Note: If the last simulation you ran in the Setup tab was used to produce the primal, then you have no actions to take for this step. If not, then you should copy the settings from your primal result and apply it in the Setup tab.
Consistent Settings
First, the settings (boundary conditions, solver settings) must be consistent with the primal of interest:
Navigate (via the Results tab) to the simulation result of interest
Toggle the panel dropdown from Post-processing to Setup Details in the upper left of the tree.
Click the Copy to Setup button in the top right of the tree to apply the settings to the Setup tab.
Initialization
The domain initialization for the Adjoint simulation should use the solution of the primal. This is specified in the Physics -> Fluid Physics -> Initialization properties panel, where you point to the relevant primal solution. For the Adjoint Simulation Type, the only iteration allowed for selection is the final solution, which is selected by default.
Run the Adjoint Sensitivity Analysis
Now we can click Run Simulation to perform our first Adjoint simulation (~60 credits). You can repeat these steps to initiate Adjoint runs for different Outputs of interest as required.
Post-processing An Adjoint Simulation
You've made it through running the Adjoint, now welcome to the fun part - interpreting the results! If you've never worked with Adjoints before, this should be a great primer and will give you the tools to extract value from these simulations.
Adjoint-specific UI Behavior
For ongoing or completed Adjoint simulations, the name of the Simulation has the Output of interest automatically appended:
Note: To identify the Primal Solution for these runs, you currently need to navigate to the Setup Details view and look at the Initialization panel in the Settings tree.
The Simulation tab looks very similar to the traditional (primal) simulation results you have explored previously:
The major differences are:
The variables available for visualization in the 3D window are different. We will discuss this in more detail below.
The residuals displayed are for the Adjoint solve (not the primal solution)
Note, however, that the Outputs are those computed by the primal solution, since these don't change during the Adjoint run.
Interpreting the Adjoint Variables
The purpose of the Adjoint solver is to help you understand how to adjust your design to influence the Output objective, in this case Lift. This can be done by interpreting the adjoint variables:
Normal Sensitivity
Normal Sensitivity is the sensitivity of output to displacement of the geometry surface in the surface normal direction (normals point into the fluid volume)
Use
Normal Sensitivity informs how you can modify the shape of a vehicle to impact the Output of interest. For example, in the above image, where we are looking up at the airplane from underneath, deflecting the wing trailing edges "into" the fluid (or down) will increase the Lift.
Interpretation
If the normal sensitivity is positive, moving the surface into the fluid domain will increase the Output. If it is negative, moving the surface into the fluid domain will decrease the Output.
Smoothed Normal Sensitivity
Due to numerical properties of the mesh and convergence of the primal and adjoint, the Normal Sensitivity field is often un-smooth. Smoothed Normal Sensitivity is a variant of the Normal Sensitivity in which a Gaussian filter has been applied to the field to make it easier to visually interpret. Smoothed Normal Sensitivity is typically the most commonly used adjoint variable.
Use
Same as with Normal Sensitivity.
Interpretation
Same as with Normal Sensitivity.
Example: this is an important lesson in interpreting the magnitude of the sensitivity. In the image above we scaled the Smoothed Sensitivity to [-200, 200], and at first glance the result is counter-intuitive. The blue values (negative sensitivity) seem to indicate that the wing near the trailing edge should be deflected "up". Upon closer inspection, there is a much stronger sensitivity to downwards deflection of the trailing edge if we adjust the contour range to [-750, 750]:
Keep in mind that sensitivities are computed with respect to localized surface deformations, the movement of a control surface would cause many surface points to move. If the normal sensitivities on that surface or region do not have the same sign, it becomes important to consider their magnitude. As this example shows, there may be small regions of large positive sensitivity within large regions of moderate negative sensitivity. It is important not to let confirmation bias lead you to interpret the results incorrectly - make sure to investigate thoroughly!
Sensitivity
Sensitivity is the raw surface displacement sensitivity as a vector, with global coordinate system aligned components. This is the Normal Sensitivity without performing the dot-product with the local surface normal vector, such that it provides sensitivities in each coordinate direction.
Use
Can be used to assess sensitivity to surface deformation in a Cartesian direction, as opposed to the surface normal direction.
Interpretation
Per component, if positive, moving the surface along this direction will increase the Output; if negative, vice versa.
Adjoint Momentum
Adjoint Momentum is the sensitivity of the output to a sink (negative source term) in one of the momentum equations. This is a vector field with one component for the X, Y, and Z momentum components.
Use
Adjoint Momentum can be used to assess how active or passive flow control devices might impact a vehicle's performance, e.g., blowing or suction within the boundary layer of a wing.
Interpretation
Per component, if positive, introducing a momentum sink in this location will increase the Output; if negative, vice versa.
Example 1: below we show a span-wise slice of the domain displaying Adjoint Momentum-Z contours. This contour shows that adding a sink in Z-momentum, increasing momentum in the down (-Z) direction, would decrease Lift if performed over the majority of the wing. However, when applied at the trailing edges, this will actually increase lift, which is consistent with the concept of blown flaps for high-lift devices.
Example 2: below we show a span-wise slice of the domain displaying Adjoint Momentum-X contours. Here we see that a sink in x-momentum (slowing down the fluid in the x-direction) will decrease lift if performed on the top side of the main or tail wings, but will increase lift in performed underneath the wings.
Adjoint Energy
Adjoint Energy is the sensitivity of the output to an energy sink (negative source term) in the energy equation. This is a scalar field.
Use
Adjoint Energy can be used to assess the viability of thermal flow control devices. For example, where to place a heater to maximize the average temperature in a room. There is little sensitivity to temperature in this flow, so we can't provide a fun example in this case.
Interpretation
If positive, introducing an energy sink in this location will increase the Output; if negative, vice versa.
How to Improve my Design
Using the sensitivity information provided by these Adjoint simulations, you can iterate on your geometry and then run the modified geometry through Luminary. You can iterate on your designs in an informed manner by leveraging the Adjoint results.
Note: Currently this requires you to adjust your geometry upstream of Luminary, but keep an eye out for developments that augment and automate design optimization loops.
Bonus Content
Specifying Surfaces for Sensitivity Analysis
In the General panel, when you select Adjoint as your Simulation Type you will see an optional input area "Sensitivity Surfaces" to specify the surfaces to compute the sensitivities on:
This will restrict the Normal Sensitivity computations to just these surfaces, instead of all wall surfaces (the default). This may mildly speed up the simulation. Below in the right image we restricted the sensitivity to the main wings and nearby portions of the fuselage, whereas on the left the sensitivity was assessed over all walls:
This is why the tail surfaces are blue on the left. This is useful if your ability to adjust the vehicle design is constrained to certain regions - the sensitivity information elsewhere is unnecessary.
What's Next?
First, congratulations for getting all the way here, that is a lot of content to get through!
If you've simply been reading along, go ahead and try the Tutorial out for yourself. Even better, see if you can apply the approach to a problem of relevance to you or your organization.
If you are able to modify the geometry based on the Adjoint results, assess if the design changes the Adjoint suggested improve the Output of interest in your subsequent simulation(s).
Let us know if this in-depth tutorial was helpful in learning to use the Adjoint capabilities within Luminary, and how we can augment the learning process. Simply send us a message via Lumi Support chat!