Path: blob/main/src/callbacks_step/analysis_surface_integral_3d.jl
2055 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""8LiftCoefficientPressure3D(aoa, rho_inf, u_inf, a_inf)910Compute the lift coefficient11```math12C_{L,p} \coloneqq \frac{\oint_{\partial \Omega} p \boldsymbol n \cdot \psi_L \, \mathrm{d} S}13{0.5 \rho_{\infty} U_{\infty}^2 A_{\infty}}14```15based on the pressure distribution along a boundary.16In 3D, the freestream-normal unit vector ``\psi_L`` is given by17```math18\psi_L \coloneqq \begin{pmatrix} -\sin(\alpha) \\ \cos(\alpha) \\ 0 \end{pmatrix}19```20where ``\alpha`` is the angle of attack.21This employs the convention that the wing is oriented such that the streamwise flow is in22x-direction, the angle of attack rotates the flow into the y-direction, and that wing extends spanwise in the z-direction.2324Supposed to be used in conjunction with [`AnalysisSurfaceIntegral`](@ref)25which stores the the to-be-computed variables (for instance `LiftCoefficientPressure3D`)26and boundary information.2728- `aoa::Real`: Angle of attack in radians (for airfoils etc.)29- `rho_inf::Real`: Free-stream density30- `u_inf::Real`: Free-stream velocity31- `a_inf::Real`: Reference area of geometry (e.g. projected wing surface)32"""33function LiftCoefficientPressure3D(aoa, rho_inf, u_inf, a_inf)34# `psi_lift` is the normal unit vector to the freestream direction.35# Note: The choice of the normal vector `psi_lift = (-sin(aoa), cos(aoa), 0)`36# leads to positive lift coefficients for positive angles of attack for airfoils.37# One could also use `psi_lift = (sin(aoa), -cos(aoa), 0)` which results in the same38# value, but with the opposite sign.39psi_lift = (-sin(aoa), cos(aoa), zero(aoa))40return LiftCoefficientPressure(ForceState(psi_lift, rho_inf, u_inf, a_inf))41end4243@doc raw"""44DragCoefficientPressure3D(aoa, rho_inf, u_inf, a_inf)4546Compute the drag coefficient47```math48C_{D,p} \coloneqq \frac{\oint_{\partial \Omega} p \boldsymbol n \cdot \psi_D \, \mathrm{d} S}49{0.5 \rho_{\infty} U_{\infty}^2 A_{\infty}}50```51based on the pressure distribution along a boundary.52In 3D, the freestream-tangent unit vector ``\psi_D`` is given by53```math54\psi_D \coloneqq \begin{pmatrix} \cos(\alpha) \\ \sin(\alpha) \\ 0 \end{pmatrix}55```56where ``\alpha`` is the angle of attack.57This employs the convention that the wing is oriented such that the streamwise flow is in58x-direction, the angle of attack rotates the flow into the y-direction, and that wing extends spanwise in the z-direction.5960Supposed to be used in conjunction with [`AnalysisSurfaceIntegral`](@ref)61which stores the the to-be-computed variables (for instance `DragCoefficientPressure3D`)62and boundary information.6364- `aoa::Real`: Angle of attack in radians (for airfoils etc.)65- `rho_inf::Real`: Free-stream density66- `u_inf::Real`: Free-stream velocity67- `a_inf::Real`: Reference area of geometry (e.g. projected wing surface)68"""69function DragCoefficientPressure3D(aoa, rho_inf, u_inf, a_inf)70# `psi_drag` is the unit vector tangent to the freestream direction71psi_drag = (cos(aoa), sin(aoa), zero(aoa))72return DragCoefficientPressure(ForceState(psi_drag, rho_inf, u_inf, a_inf))73end7475# 3D version of the `analyze` function for `AnalysisSurfaceIntegral`, i.e.,76# `LiftCoefficientPressure` and `DragCoefficientPressure`.77function analyze(surface_variable::AnalysisSurfaceIntegral, du, u, t,78mesh::P4estMesh{3},79equations, dg::DGSEM, cache, semi)80@unpack boundaries = cache81@unpack surface_flux_values, node_coordinates, contravariant_vectors = cache.elements82@unpack weights = dg.basis8384@unpack variable, boundary_symbols = surface_variable85@unpack boundary_symbol_indices = semi.boundary_conditions86boundary_indices = get_boundary_indices(boundary_symbols, boundary_symbol_indices)8788surface_integral = zero(eltype(u))89index_range = eachnode(dg)90for boundary in boundary_indices91element = boundaries.neighbor_ids[boundary]92node_indices = boundaries.node_indices[boundary]93direction = indices2direction(node_indices)9495i_node_start, i_node_step = index_to_start_step_3d(node_indices[1], index_range)96j_node_start, j_node_step = index_to_start_step_3d(node_indices[2], index_range)97k_node_start, k_node_step = index_to_start_step_3d(node_indices[3], index_range)9899# In 3D, boundaries are surfaces => `node_index1`, `node_index2`100for node_index1 in index_range101# Reset node indices102i_node = i_node_start103j_node = j_node_start104k_node = k_node_start105for node_index2 in index_range106u_node = Trixi.get_node_vars(cache.boundaries.u, equations, dg,107node_index1, node_index2, boundary)108# Extract normal direction at nodes which points from the elements outwards,109# i.e., *into* the structure.110normal_direction = get_normal_direction(direction,111contravariant_vectors,112i_node, j_node, k_node, element)113114# Coordinates at a boundary node115x = get_node_coords(node_coordinates, equations, dg,116i_node, j_node, k_node, element)117118# L2 norm of normal direction (contravariant_vector) is the surface element119dS = weights[node_index1] * weights[node_index2] *120norm(normal_direction)121122# Integral over entire boundary surface. Note, it is assumed that the123# `normal_direction` is normalized to be a normal vector within the124# function `variable` and the division of the normal scaling factor125# `norm(normal_direction)` is then accounted for with the `dS` quantity.126surface_integral += variable(u_node, normal_direction, x, t,127equations) * dS128129i_node += i_node_step130j_node += j_node_step131k_node += k_node_step132end133end134end135return surface_integral136end137end # muladd138139140