The Wake class manages the trailing vortex wake system behind wing panels. It handles the creation, update, and configuration of semi-infinite vortex filaments that extend from the wing trailing edge into the wake region, representing the shed vorticity from bound circulation.
Important: The Wake class uses a static factory pattern and should not be instantiated directly.
# Correct usage
panels = Wake.frozen_wake(va_distribution, panels)
# Incorrect usage - will raise RuntimeError
wake = Wake() # RuntimeError: Use static methodsUpdates panel filament systems with semi-infinite wake elements based on the local flow conditions.
Parameters:
va_distribution(np.ndarray): Array of velocity vectors at each panelpanels(List[Panel]): List of panel objects to update
Returns:
List[Panel]: Updated panel list with wake filaments
The frozen wake assumption treats the wake as:
- Straight lines: Wake filaments aligned with local velocity
- Semi-infinite: Extending to infinity downstream
- Fixed geometry: Wake shape doesn't change during iteration
For each panel, wake filaments are created at trailing edge points:
for i, panel in enumerate(panels):
va_i = va_distribution[i]
vel_mag = ||va_i||
direction = va_i / ||va_i||
# Create wake filaments
wake_fil_1 = SemiInfiniteFilament(TE_point_1, direction, vel_mag, +1)
wake_fil_2 = SemiInfiniteFilament(TE_point_2, direction, vel_mag, -1)When panels are first created, they contain:
- Bound filament: Quarter-chord bound vortex
- Trailing filament 1: From bound to TE_point_1
- Trailing filament 2: From bound to TE_point_2
After frozen_wake() is called:
- Bound filament: (unchanged)
- Trailing filament 1: (unchanged)
- Trailing filament 2: (unchanged)
- Semi-infinite wake 1: From TE_point_1 to infinity
- Semi-infinite wake 2: From TE_point_2 to infinity
Filament Direction = +1 (Forward wake)
- Extends in direction of local velocity
- Represents vorticity convected downstream
Filament Direction = -1 (Backward wake)
- Extends opposite to local velocity direction
- Maintains circulation conservation around panel
# Forward wake filament
SemiInfiniteFilament(TE_point_1, direction, vel_mag, filament_direction=+1)
# Backward wake filament
SemiInfiniteFilament(TE_point_2, direction, vel_mag, filament_direction=-1)The method handles both initial creation and subsequent updates:
if len(panel.filaments) == 3:
# Initial wake creation
panel.filaments.append(
SemiInfiniteFilament(TE_point_1, direction, vel_mag, +1)
)
panel.filaments.append(
SemiInfiniteFilament(TE_point_2, direction, vel_mag, -1)
)
elif len(panel.filaments) == 5:
# Update existing wake filaments
panel.filaments[3] = SemiInfiniteFilament(TE_point_1, direction, vel_mag, +1)
panel.filaments[4] = SemiInfiniteFilament(TE_point_2, direction, vel_mag, -1)
else:
raise ValueError("Unexpected number of filaments")- Preserves bound circulation: Doesn't modify core vortex system
- Efficient memory usage: Reuses filament objects when possible
- Consistent indexing: Maintains predictable filament order
- Error detection: Catches unexpected filament configurations
- Steady flow: Wake geometry doesn't change with time
- Inviscid flow: No viscous diffusion of wake vortices
- Straight wake: Local velocity determines wake direction
- Semi-infinite extent: Wake extends infinitely downstream
- Low angles of attack: Wake stays attached and straight
- Moderate sweep: Local flow approximation remains valid
- Steady conditions: No significant unsteady effects
- High angles of attack: Wake may separate and roll up
- Highly swept wings: 3D effects become important
- Unsteady maneuvers: Wake history effects neglected
The wake system ensures that circulation is conserved around each panel:
Γ_bound + Γ_trailing_1 + Γ_trailing_2 + Γ_wake_1 + Γ_wake_2 = 0
Wake filaments enforce the Kutta condition at the trailing edge by:
- Extending circulation smoothly into wake
- Preventing flow around sharp trailing edge
- Maintaining finite velocities at TE
Wake filaments contribute to induced velocities throughout the flow field:
- Near field: Significant influence on wing panels
- Far field: Determines downwash and induced drag
- Ground effect: Modified by ground proximity
# In BodyAerodynamics.va setter
def va(self, va_value, yaw_rate=0.0):
# Update panel velocities
for i, panel in enumerate(self.panels):
panel.va = va_distribution[i]
# Update wake filaments based on new velocities
self.panels = Wake.frozen_wake(va_distribution, self.panels)# In Solver.solve()
# AIC matrices include wake filament contributions
AIC_x, AIC_y, AIC_z = body_aero.compute_AIC_matrices(
aerodynamic_model_type,
core_radius_fraction,
va_norm_array,
va_unit_array
)
# Wake filaments affect induced velocity calculations
for panel in panels:
v_induced = panel.compute_velocity_induced_single_ring_semiinfinite(...)Wake filaments are included in plotting routines:
# From Panel.compute_filaments_for_plotting()
for i, filament in enumerate(self.filaments):
if i >= 3: # Semi-infinite wake filaments
x2 = x1 + chord_length * (va / ||va||)
color = "orange" if filament_direction == 1 else "red"
filaments.append([x1, x2, color])- O(N) update complexity: Linear in number of panels
- Minimal memory allocation: Reuses existing filament objects
- JIT compatibility: Uses optimized vector operations
- In-place updates: Modifies existing panel list
- Object reuse: Avoids creating new filament objects when possible
- Garbage collection: Old filaments automatically cleaned up
- Direction normalization: Ensures unit wake direction vectors
- Velocity magnitude: Handles zero and very small velocities
- Filament ordering: Maintains consistent indexing scheme
# Velocity distribution validation
if len(va_distribution) != len(panels):
raise ValueError("Velocity distribution length mismatch")
# Panel filament count validation
if len(panel.filaments) not in [3, 5]:
raise ValueError("Unexpected number of filaments")- Zero velocity handling: Graceful degradation for zero flow
- Direction computation: Robust normalization for small velocities
- State consistency: Maintains valid filament configurations
- Time-dependent wake evolution
- Wake rollup and contraction effects
- History-dependent wake shapes
- Viscous diffusion of wake vortices
- Core radius growth with downstream distance
- Turbulent mixing effects
- Wake interaction with ground plane
- Image vortex systems
- Modified induced velocity field
- Computational cost: More complex models require additional computation
- Memory requirements: Time-dependent wakes need history storage
- Numerical stability: Advanced models may need special treatment
# Create velocity distribution
va_distribution = np.array([
[10.0, 0.0, 1.0], # Panel 1 velocity
[10.0, 0.0, 1.2], # Panel 2 velocity
[10.0, 0.0, 1.4], # Panel 3 velocity
])
# Update wake system
panels = Wake.frozen_wake(va_distribution, panels)
# Verify wake filament creation
for panel in panels:
assert len(panel.filaments) == 5
assert panel.filaments[3].filament_direction == 1
assert panel.filaments[4].filament_direction == -1# In iterative solver loop
for iteration in range(max_iterations):
# Solve for new circulation distribution
gamma_new = solve_circulation(panels)
# Update induced velocities (includes wake effects)
for i, panel in enumerate(panels):
v_induced[i] = compute_induced_velocity(panel, gamma_new)
# Update apparent velocities
va_new = va_freestream + v_induced
# Update wake filaments based on new flow field
panels = Wake.frozen_wake(va_new, panels)