import sharpy.aero.utils.uvlmlib as uvlmlib
import sharpy.utils.settings as settings_utils
from sharpy.utils.solver_interface import solver, BaseSolver
import sharpy.utils.generator_interface as gen_interface
from sharpy.utils.constants import vortex_radius_def
import sharpy.aero.utils.mapping as mapping
[docs]@solver
class StaticUvlm(BaseSolver):
"""
``StaticUvlm`` solver class, inherited from ``BaseSolver``
Aerodynamic solver that runs a UVLM routine to solve the steady or unsteady aerodynamic problem.
The aerodynamic problem is posed in the form of an ``Aerogrid`` object.
Args:
data (PreSharpy): object with problem data
custom_settings (dict): custom settings that override the settings in the solver ``.txt`` file. None by default
Attributes:
settings (dict): Name-value pair of settings employed by solver. See Notes for valid combinations
settings_types (dict): Acceptable data types for entries in ``settings``
settings_default (dict): Default values for the available ``settings``
data (PreSharpy): object containing the information of the problem
velocity_generator(object): object containing the flow conditions information
"""
solver_id = 'StaticUvlm'
solver_classification = 'aero'
settings_types = dict()
settings_default = dict()
settings_description = dict()
settings_types['print_info'] = 'bool'
settings_default['print_info'] = True
settings_description['print_info'] = 'Print info to screen'
settings_types['horseshoe'] = 'bool'
settings_default['horseshoe'] = False
settings_description['horseshoe'] = 'Horseshoe wake modelling for steady simulations.'
settings_types['nonlifting_body_interactions'] = 'bool'
settings_default['nonlifting_body_interactions'] = False
settings_description['nonlifting_body_interactions'] = 'Consider nonlifting body interactions'
settings_types['only_nonlifting'] = 'bool'
settings_default['only_nonlifting'] = False
settings_description['only_nonlifting'] = 'Consider only nonlifting bodies'
settings_types['phantom_wing_test'] = 'bool'
settings_default['phantom_wing_test'] = False
settings_description['phantom_wing_test'] = 'Debug option'
settings_types['num_cores'] = 'int'
settings_default['num_cores'] = 0
settings_description['num_cores'] = 'Number of cores to use in the VLM lib'
settings_types['n_rollup'] = 'int'
settings_default['n_rollup'] = 0
settings_description['n_rollup'] = 'Number of rollup iterations for free wake. Use at least ``n_rollup > 1.1*m_star``'
settings_types['rollup_dt'] = 'float'
settings_default['rollup_dt'] = 0.1
settings_description['rollup_dt'] = 'Pseudo time step for wake convection. Chose it so that it is similar to the unsteady time step'
settings_types['rollup_aic_refresh'] = 'int'
settings_default['rollup_aic_refresh'] = 1
settings_description['rollup_dt'] = 'Controls when the AIC matrix is refreshed during the wake rollup'
settings_types['rollup_tolerance'] = 'float'
settings_default['rollup_tolerance'] = 1e-4
settings_description['rollup_tolerance'] = 'Convergence criterium for rollup wake'
settings_types['iterative_solver'] = 'bool'
settings_default['iterative_solver'] = False
settings_description['iterative_solver'] = 'Not in use'
settings_types['iterative_tol'] = 'float'
settings_default['iterative_tol'] = 1e-4
settings_description['iterative_tol'] = 'Not in use'
settings_types['iterative_precond'] = 'bool'
settings_default['iterative_precond'] = False
settings_description['iterative_precond'] = 'Not in use'
settings_types['velocity_field_generator'] = 'str'
settings_default['velocity_field_generator'] = 'SteadyVelocityField'
settings_description['velocity_field_generator'] = 'Name of the velocity field generator to be used in the simulation'
settings_types['velocity_field_input'] = 'dict'
settings_default['velocity_field_input'] = {}
settings_description['velocity_field_input'] = 'Dictionary of settings for the velocity field generator'
settings_types['rho'] = 'float'
settings_default['rho'] = 1.225
settings_description['rho'] = 'Air density'
settings_types['cfl1'] = 'bool'
settings_default['cfl1'] = True
settings_description['cfl1'] = 'If it is ``True``, it assumes that the discretisation complies with CFL=1'
settings_types['vortex_radius'] = 'float'
settings_default['vortex_radius'] = vortex_radius_def
settings_description['vortex_radius'] = 'Distance between points below which induction is not computed'
settings_types['vortex_radius_wake_ind'] = 'float'
settings_default['vortex_radius_wake_ind'] = vortex_radius_def
settings_description['vortex_radius_wake_ind'] = 'Distance between points below which induction is not computed in the wake convection'
settings_types['rbm_vel_g'] = 'list(float)'
settings_default['rbm_vel_g'] = [0., 0., 0., 0., 0., 0.]
settings_description['rbm_vel_g'] = 'Rigid body velocity in G FoR'
settings_types['centre_rot_g'] = 'list(float)'
settings_default['centre_rot_g'] = [0., 0., 0.]
settings_description['centre_rot_g'] = 'Centre of rotation in G FoR around which ``rbm_vel_g`` is applied'
settings_types['map_forces_on_struct'] = 'bool'
settings_default['map_forces_on_struct'] = False
settings_description['map_forces_on_struct'] = 'Maps the forces on the structure at the end of the timestep. Only usefull if the solver is used outside StaticCoupled'
settings_types['ignore_first_x_nodes_in_force_calculation'] = 'int'
settings_default['ignore_first_x_nodes_in_force_calculation'] = 0
settings_description['ignore_first_x_nodes_in_force_calculation'] = 'Ignores the forces on the first user-specified number of nodes of all surfaces.'
settings_table = settings_utils.SettingsTable()
__doc__ += settings_table.generate(settings_types, settings_default, settings_description)
def __init__(self):
# settings list
self.data = None
self.settings = None
self.velocity_generator = None
def initialise(self, data, custom_settings=None, restart=False):
self.data = data
if custom_settings is None:
self.settings = data.settings[self.solver_id]
else:
self.settings = custom_settings
settings_utils.to_custom_types(self.settings, self.settings_types, self.settings_default, no_ctype=True)
self.update_step()
# init velocity generator
velocity_generator_type = gen_interface.generator_from_string(
self.settings['velocity_field_generator'])
self.velocity_generator = velocity_generator_type()
self.velocity_generator.initialise(self.settings['velocity_field_input'], restart=restart)
def add_step(self):
self.data.aero.add_timestep()
if self.settings['nonlifting_body_interactions']:
self.data.nonlifting_body.add_timestep()
def update_grid(self, beam):
if not self.settings['only_nonlifting']:
self.data.aero.generate_zeta(beam,
self.data.aero.aero_settings,
-1,
beam_ts=-1)
if self.settings['nonlifting_body_interactions'] or self.settings['only_nonlifting']:
self.data.nonlifting_body.generate_zeta(beam,
self.data.nonlifting_body.aero_settings,
-1,
beam_ts=-1)
def update_custom_grid(self, structure_tstep, aero_tstep, nonlifting_tstep=None):
self.data.aero.generate_zeta_timestep_info(structure_tstep,
aero_tstep,
self.data.structure,
self.data.aero.aero_settings,
dt=self.settings['rollup_dt'])
if self.settings['nonlifting_body_interactions']:
self.data.nonlifting_body.generate_zeta_timestep_info(structure_tstep,
nonlifting_tstep,
self.data.structure,
self.data.nonlifting_body.aero_settings)
def run(self, **kwargs):
structure_tstep = settings_utils.set_value_or_default(kwargs, 'structural_step', self.data.structure.timestep_info[self.data.ts])
if not self.settings['only_nonlifting']:
aero_tstep = settings_utils.set_value_or_default(kwargs, 'aero_step', self.data.aero.timestep_info[self.data.ts])
if not self.data.aero.timestep_info[self.data.ts].zeta:
return self.data
# generate the wake because the solid shape might change
self.data.aero.wake_shape_generator.generate({'zeta': aero_tstep.zeta,
'zeta_star': aero_tstep.zeta_star,
'gamma': aero_tstep.gamma,
'gamma_star': aero_tstep.gamma_star,
'dist_to_orig': aero_tstep.dist_to_orig})
if self.settings['nonlifting_body_interactions']:
# generate uext
self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta,
'override': True,
'for_pos': structure_tstep.for_pos[0:3]},
self.data.nonlifting_body.timestep_info[self.data.ts].u_ext)
# generate uext
self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta,
'override': True,
'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]},
self.data.aero.timestep_info[self.data.ts].u_ext)
# grid orientation
uvlmlib.vlm_solver_lifting_and_nonlifting_bodies(self.data.aero.timestep_info[self.data.ts],
self.data.nonlifting_body.timestep_info[self.data.ts],
self.settings)
else:
# generate uext
self.velocity_generator.generate({'zeta': self.data.aero.timestep_info[self.data.ts].zeta,
'override': True,
'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]},
self.data.aero.timestep_info[self.data.ts].u_ext)
# grid orientation
uvlmlib.vlm_solver(self.data.aero.timestep_info[self.data.ts],
self.settings)
else:
self.velocity_generator.generate({'zeta': self.data.nonlifting_body.timestep_info[self.data.ts].zeta,
'override': True,
'for_pos': self.data.structure.timestep_info[self.data.ts].for_pos[0:3]},
self.data.nonlifting_body.timestep_info[self.data.ts].u_ext)
uvlmlib.vlm_solver_nonlifting_body(self.data.nonlifting_body.timestep_info[self.data.ts],
self.settings)
return self.data
[docs] def next_step(self):
""" Updates de aerogrid based on the info of the step, and increases
the self.ts counter """
self.data.aero.add_timestep()
if self.settings['nonlifting_body_interactions']:
self.data.nonlifting_body.add_timestep()
self.update_step()
def update_step(self):
if not self.settings['only_nonlifting']:
self.data.aero.generate_zeta(self.data.structure,
self.data.aero.aero_settings,
self.data.ts)
if self.settings['nonlifting_body_interactions'] or self.settings['only_nonlifting']:
self.data.nonlifting_body.generate_zeta(self.data.structure,
self.data.nonlifting_body.aero_settings,
self.data.ts)