use serde::{Deserialize, Serialize};
use stormath::{
spatial_vector::SpatialVector,
type_aliases::Float,
consts::{PI, MIN_POSITIVE},
};
use crate::line_force_model::LineForceModel;
use crate::lifting_line::singularity_elements::prelude::*;
use crate::lifting_line::wake::settings::ViscousCoreLength;
use super::{
DynamicWake,
settings::{
WakeSettings,
FirstWakePointsDirection,
WakeIndices,
},
};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SinIncreasedViscousCoreLength {
pub last_panel_value: ViscousCoreLength,
#[serde(default="SinIncreasedViscousCoreLength::default_evolution_length_factor")]
pub evolution_length_factor: Float,
}
impl SinIncreasedViscousCoreLength {
fn default_evolution_length_factor() -> Float { 1.0 }
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum ViscousCoreLengthEvolution {
Constant,
SinIncrease(SinIncreasedViscousCoreLength)
}
impl Default for ViscousCoreLengthEvolution {
fn default() -> Self {
Self::Constant
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DynamicWakeBuilder {
#[serde(default="DynamicWakeBuilder::default_number_of_panels_per_line_element")]
pub nr_panels_per_line_element: usize,
#[serde(default)]
pub viscous_core_length: ViscousCoreLength,
#[serde(default)]
pub viscous_core_length_evolution: ViscousCoreLengthEvolution,
#[serde(default="DynamicWakeBuilder::default_first_panel_relative_length")]
pub first_panel_relative_length: Float,
#[serde(default="DynamicWakeBuilder::default_last_panel_relative_length")]
pub last_panel_relative_length: Float,
#[serde(default)]
pub first_wake_points_direction: FirstWakePointsDirection,
#[serde(default)]
pub symmetry_condition: SymmetryCondition,
#[serde(default)]
pub ratio_of_wake_affected_by_induced_velocities: Float,
#[serde(default="PotentialTheorySettings::default_far_field_ratio")]
pub far_field_ratio: Float,
#[serde(default)]
pub shape_damping_factor: Float,
#[serde(default)]
pub neglect_self_induced_velocities: bool,
#[serde(default="DynamicWakeBuilder::default_initial_relative_wake_length")]
pub initial_relative_wake_length: Float,
#[serde(default)]
pub write_wake_data_to_file: bool,
#[serde(default)]
pub wake_files_folder_path: String,
}
impl DynamicWakeBuilder {
fn default_number_of_panels_per_line_element() -> usize {100}
fn default_initial_relative_wake_length() -> Float {50.0}
fn default_first_panel_relative_length() -> Float {0.75}
fn default_last_panel_relative_length() -> Float {0.25}
pub fn build(
&self,
line_force_model: &LineForceModel,
) -> DynamicWake {
let span_points = &line_force_model.span_points_global;
let indices = self.get_wake_indices(line_force_model);
let mut points = vec![SpatialVector::default(); indices.nr_points()];
for i_stream in 0..indices.nr_points_per_line_element {
for i_span in 0..span_points.len() {
let flat_index = indices.point_index(i_stream, i_span);
points[flat_index] = span_points[i_span];
}
}
let end_index_induced_velocities_on_wake = (
self.ratio_of_wake_affected_by_induced_velocities *
(indices.nr_points() - 1) as Float
).ceil() as usize;
let settings = WakeSettings {
first_panel_relative_length: self.first_panel_relative_length,
last_panel_relative_length: self.last_panel_relative_length,
first_wake_points_direction: self.first_wake_points_direction,
end_index_induced_velocities_on_wake,
shape_damping_factor: self.shape_damping_factor,
neglect_self_induced_velocities: self.neglect_self_induced_velocities,
write_wake_data_to_file: self.write_wake_data_to_file,
wake_files_folder_path: self.wake_files_folder_path.clone(),
};
let potential_theory_settings = PotentialTheorySettings {
symmetry_condition: self.symmetry_condition.clone(),
far_field_ratio: self.far_field_ratio,
..Default::default()
};
let nr_panels = indices.nr_panels();
let strengths: Vec<Float> = vec![0.0; nr_panels];
let panels_viscous_core_length = self.get_panels_viscous_core_length(
line_force_model,
&indices
);
let panels = vec![Panel::default(); nr_panels];
let velocity_at_points = vec![SpatialVector::default(); indices.nr_points()];
let representative_chord_length = line_force_model.chord_lengths.iter()
.map(|c| *c)
.sum::<Float>() / line_force_model.chord_lengths.len() as Float;
let mut wake = DynamicWake {
indices,
points,
velocity_at_points,
strengths,
panels_viscous_core_length,
settings,
potential_theory_settings,
wing_indices: line_force_model.wing_indices.clone(),
number_of_time_steps_completed: 0,
panels,
representative_chord_length,
};
wake.initialize_based_on_chord_length(
&line_force_model,
self.initial_relative_wake_length
);
wake
}
pub fn get_panels_viscous_core_length(
&self,
line_force_model: &LineForceModel,
wake_indices: &WakeIndices,
) -> Vec<Float> {
let mut out = vec![0.0; wake_indices.nr_panels()];
let span_lines = &line_force_model.span_lines_global;
let span_line_lengths: Vec<Float> = span_lines.iter()
.map(|line| line.length())
.collect();
for i_stream in 0..wake_indices.nr_panels_per_line_element {
for i_span in 0..wake_indices.nr_panels_along_span {
let flat_index = wake_indices.panel_index(
i_stream,
i_span
);
let viscous_core_length_raw = match self.viscous_core_length {
ViscousCoreLength::Relative(relative_length) => {
relative_length * span_line_lengths[i_span]
},
ViscousCoreLength::Absolute(length) => length,
ViscousCoreLength::NoViscousCore => MIN_POSITIVE
};
out[flat_index] = match self.viscous_core_length_evolution {
ViscousCoreLengthEvolution::Constant => viscous_core_length_raw,
ViscousCoreLengthEvolution::SinIncrease(evolution_settings) => {
let final_viscous_core_length = match evolution_settings.last_panel_value {
ViscousCoreLength::Relative(relative_length) => {
relative_length * span_line_lengths[i_span]
},
ViscousCoreLength::Absolute(length) => length,
ViscousCoreLength::NoViscousCore => MIN_POSITIVE
};
let length_before_last_value_is_used = (wake_indices.nr_panels_per_line_element - 1) as Float * evolution_settings.evolution_length_factor;
let relative_stream_value = (i_stream as Float / length_before_last_value_is_used).min(1.0).max(0.0);
let sin_input = 0.5 * relative_stream_value * PI; let sin_factor = sin_input.sin();
viscous_core_length_raw * (1.0 - sin_factor) +
final_viscous_core_length * sin_factor
}
};
}
}
out
}
pub fn get_wake_indices(
&self,
line_force_model: &LineForceModel,
) -> WakeIndices {
let span_points = &line_force_model.span_points_global;
let nr_panels_along_span = line_force_model.nr_span_lines();
let nr_points_along_span = span_points.len();
let nr_panels_per_line_element = self.nr_panels_per_line_element;
let nr_points_per_line_element = self.nr_panels_per_line_element + 1;
WakeIndices {
nr_points_along_span,
nr_panels_along_span,
nr_panels_per_line_element,
nr_points_per_line_element,
}
}
}
impl Default for DynamicWakeBuilder {
fn default() -> Self {
Self {
nr_panels_per_line_element: Self::default_number_of_panels_per_line_element(),
viscous_core_length: Default::default(),
viscous_core_length_evolution: Default::default(),
first_panel_relative_length: Self::default_first_panel_relative_length(),
last_panel_relative_length: Self::default_last_panel_relative_length(),
first_wake_points_direction: FirstWakePointsDirection::default(),
symmetry_condition: Default::default(),
ratio_of_wake_affected_by_induced_velocities: Default::default(),
far_field_ratio: PotentialTheorySettings::default_far_field_ratio(),
shape_damping_factor: 0.0,
neglect_self_induced_velocities: false,
initial_relative_wake_length: Self::default_initial_relative_wake_length(),
write_wake_data_to_file: false,
wake_files_folder_path: String::new(),
}
}
}