diff --git a/doc/tutorials/DC3_model/JCLs/jcl_dc3_cfd_euler.py b/doc/tutorials/DC3_model/JCLs/jcl_dc3_cfd_euler.py new file mode 100755 index 00000000..51d54414 --- /dev/null +++ b/doc/tutorials/DC3_model/JCLs/jcl_dc3_cfd_euler.py @@ -0,0 +1,307 @@ +""" +Job Control File documentation +The Job Control (jcl) is a python class which defines the model and and the simulation and is imported at +the beginning of every simulation. Unlike a conventional parameter file, this allows scripting/programming +of the input, e.g. to convert units, generate mutiple load cases, etc. +Note that this documentation of parameters is comprehensive, but a) not all parameters are necessary for +every kind of simulation and b) some parameters are for experts only --> your JCL might be much smaller. +""" +import numpy as np +import os +from loadskernel.units import ft2m, tas2Ma +from loadskernel import jcl_helper +import pathlib + + +class jcl: + + def __init__(self): + + model_root = pathlib.Path(__file__).parent.parent.resolve() + # cfd_root = '/path/to/CFD/files/' + cfd_root = '/data/test/' + + # Give your aircraft a name and set some general parameters + self.general = {'aircraft': 'DC3', + # Reference span width (from tip to tip) + 'b_ref': 29.0, + # Reference chord length + 'c_ref': 3.508, + # Reference area + 'A_ref': 91.7, + # Mean aerodynamic center, also used as moments reference point + 'MAC_ref': [8.566, 0.0, 0.0], + } + """ + The electronic flight control system (EFCS) provides the "wireing" of the pilot commands + xi, eta and zeta with the control surface deflections. This is aircraft-specific and needs + to be implemented as a python module. + """ + # Electronic flight control system + self.efcs = {'version': 'efcs_dc3', # Name of the corresponding python module + # Path where to find the EFCS module + 'path': os.path.join(model_root, 'efcs'), + } + # Read the structural geometry + self.geom = {'method': 'mona', # ModGen and/or Nastran (mona) BDFs + # bdf file(s) with GRIDs and CORDs (CORD1R and CORD2R) + 'filename_grid': [os.path.join(model_root, 'fem', 'structure_only.bdf')], + # bdf file(s) with CQUADs and CTRIAs, for visualization only, e.g. outer skin on the aircraft + # 'filename_shell': [], + # bdf file(s) with MONPNT-cards + 'filename_monpnt': os.path.join(model_root, 'fem', 'export_monitoring-stations.csv'), + # The following matrices are required for some mass methods. However, the stiffness is geometry + # and not mass dependent. Overview: + # KGG via DMAP Alter und OP4 - required for mass method = 'modalanalysis', 'guyan' or 'B2000' + # USET via DMAP Alter und OP4 - required for mass method = 'modalanalysis', 'guyan' + # matrix GM via DMAP Alter und OP4 - required for mass method = 'modalanalysis', 'guyan' + # bdf file(s) with ASET1-card - required for mass method = 'guyan' + # matrix R_trans frum B2000 - required for mass method = 'B2000' + 'filename_h5': os.path.join(model_root, 'fem', 'SOL103_structure_only.mtx.h5'), + 'filename_uset': os.path.join(model_root, 'fem', 'uset.op2'), + } + # Settings for the aerodynamic model + self.aero = {'method': 'cfd_steady', + # 'mona_steady' - steady trim and quasi-steady time domain simulations + # 'mona_unsteady' - unsteady time domain simulation based on the RFA, e.g. for gust + # 'freq_dom' - frequency domain simulations, e.g. gust, continuous turbulence, flutter, etc + # 'nonlin_steady' - steady trim and quasi-steady time domain simulations with some non-linearities + # 'cfd_steady' - steady trim + # 'cfd_unsteady' - unsteady time domain simulation, e.g. for gust + # + # True or False, aerodynamic feedback of elastic structure on aerodynamics can be deactivated. + # You will still see deformations, but there is no coupling. + 'flex': True, + # aerogrid is given by CAERO1, CAERO7 or by CQUAD4 cards + 'method_caero': 'CAERO1', + # bdf file(s) with CAERO1 or CQUAD4-cards for aerogrid. IDs in ascending order. + 'filename_caero_bdf': [os.path.join(model_root, 'aero', 'vt', 'vt.CAERO1'), + os.path.join(model_root, 'aero', 'left-ht', 'left-ht.CAERO1'), + os.path.join(model_root, 'aero', 'right-ht', 'right-ht.CAERO1'), + os.path.join(model_root, 'aero', 'left-wing', 'left-wing.CAERO1'), + os.path.join(model_root, 'aero', 'right-wing', 'right-wing.CAERO1'), + ], + # DMI Matrix for camber and twist correction. Same order as the aerogrid. + 'filename_DMI_W2GJ': [os.path.join(model_root, 'fem', 'w2gj_list.DMI_merge')], + # bdf file(s) with AESURF-cards + 'filename_aesurf': [os.path.join(model_root, 'aero', 'vt', 'vt.AESURF'), + os.path.join(model_root, 'aero', 'left-ht', 'left-ht.AESURF'), + os.path.join(model_root, 'aero', 'right-ht', 'right-ht.AESURF'), + os.path.join(model_root, 'aero', 'left-wing', 'left-wing.AESURF'), + os.path.join(model_root, 'aero', 'right-wing', 'right-wing.AESURF'), + ], + # bdf file(s) with AELIST-cards + 'filename_aelist': [os.path.join(model_root, 'aero', 'vt', 'vt.AELIST'), + os.path.join(model_root, 'aero', 'left-ht', 'left-ht.AELIST'), + os.path.join(model_root, 'aero', 'right-ht', 'right-ht.AELIST'), + os.path.join(model_root, 'aero', 'left-wing', 'left-wing.AELIST'), + os.path.join(model_root, 'aero', 'right-wing', 'right-wing.AELIST'), + ], + # The hingeline of a CS is given by a CORD. Either the y- or the z-axis is taken as hingeline. 'y', 'z' + 'hingeline': 'y', + # 'vlm' (panel-aero), 'dlm' (panel-aero) or 'nastran' (external form matrices) + 'method_AIC': 'vlm', + 'key': ['VC', 'VD'], + 'Ma': [0.27, 0.34], + # Additional parameters for CFD + 'para_path': cfd_root, + 'para_file': 'baseline_euler.cfg', + # Currently implemented interfaces: 'tau' or 'su2' + 'cfd_solver': 'su2', + } + # General CFD surface mesh information + self.meshdefo = {'surface': {'fileformat': 'su2', # implemented file formats: 'cgns', 'netcdf', 'su2' + # file name of the CFD mesh + 'filename_grid': os.path.join(cfd_root, 'CFD_mesh_440kNodes.su2'), + # list of markers [1, 2, ...] or ['upper', 'lower', ...] of surfaces to be included in + # deformation + 'markers': ['wing_upper', 'wing_lower', 'wing_te', 'wing_tips', 'ht', 'vt'], + }, + } + # Set the was in which the aerodynamic forces are applied to the structure. + self.spline = {'method': 'nearest_neighbour', # Options: 'nearest_neighbour', 'rbf' or 'nastran' + # Possibility to use only a subset of the structural grid for splining. True or False + 'splinegrid': False, + # bdf file(s) with GRIDs to ne used + 'filename_splinegrid': ['splinegrid.bdf'] + } + # Settings for the structural dynamics. + self.mass = {'method': 'modalanalysis', # Inplemented interfaces: 'f06', 'modalanalysis', 'guyan', 'CoFE', 'B2000' + 'key': ['M3'], + # MGG via DMAP Alter and OP4 - always required + 'filename_h5': [os.path.join(model_root, 'fem', 'SOL103_M3.mtx.h5')], + # True or False, omits first six modes + 'omit_rb_modes': True, + # list(s) of modes to use + 'modes': [np.arange(1, 71), np.arange(1, 71), np.arange(1, 71), np.arange(1, 71)]} + # Modal damping can be applied as a factor of the stiffness matrix. + self.damping = {'method': 'modal', + 'damping': 0.02} + # The international standard atmosphere (ISA) + self.atmo = {'method': 'ISA', + 'key': ['FL000', 'FL055', 'FL075', 'FL210'], + # Altitude in meters + 'h': ft2m([0, 5500, 7500, 21000,])} + # Setting of the rigid body equations of motion + self.eom = {'version': 'waszak'} # 'linear' or 'waszak' + + """ + This section controls the automatic plotting and selection of dimensioning load cases. + Simply put a list of names of the monitoring stations (e.g. ['MON1', 'MON2',...]) into the dictionary + of possible load plots listed below. This will generate a pdf document and nastran force and moment + cards for the dimensioning load cases. + """ + self.loadplots = {'potatos_fz_mx': [], + 'potatos_mx_my': ['WL01', 'WL03', 'WL05', 'WL07', 'WL09', 'WL11', 'WL13', 'WL15', 'WL17', + 'WL19', 'WL21', 'WL23', 'WL25', 'WL27', 'WL29', 'WL31', 'WR31', 'WR29', + 'WR27', 'WR25', 'WR23', 'WR21', 'WR19', 'WR17', 'WR15', 'WR13', 'WR11', + 'WR09', 'WR07', 'WR05', 'WR03', 'WR01'], + 'potatos_fz_my': [], + 'potatos_fy_mx': [], + 'potatos_mx_mz': [], + 'potatos_my_mz': [], + 'cuttingforces_wing': ['WL01', 'WL03', 'WL05', 'WL07', 'WL09', 'WL11', 'WL13', 'WL15', + 'WL17', 'WL19', 'WL21', 'WL23', 'WL25', 'WL27', 'WL29', 'WL31', + 'WR31', 'WR29', 'WR27', 'WR25', 'WR23', 'WR21', 'WR19', 'WR17', + 'WR15', 'WR13', 'WR11', 'WR09', 'WR07', 'WR05', 'WR03', 'WR01'], + } + """ + The trimcase defines the maneuver load case, one dictionary per load case. + There may be hundreds or thousands of load cases, so at some point it might be beneficial to script this section or + import an excel sheet. + """ + self.trimcase = [{'desc': 'CC.M3.OVCFL000.alpha0', # Descriptive string of the maneuver case + # Kind of trim condition, blank for trim about all three axes, for more trim conditions see + # trim_conditions.py + 'maneuver': 'bypass', + # Subcase ID number, for Nastran in acending order + 'subcase': 1, + # Setting of the operational point + # The flight speed is given by the Mach number + 'Ma': tas2Ma(70.0, 0.0), + # Aero key + 'aero': 'VC', + # Atmo key + 'altitude': 'FL000', + # Mass key + 'mass': 'M3', + # Load factor Nz + 'Nz': 1.0, + # Velocities and accelerations given in ISO 9300 coordinate system (right-handed, forward-right-down) + # Roll rate in rad/s + 'p': 0.0 / 180.0 * np.pi, + # Pitch rate in rad/s + 'q': 0.0 / 180.0 * np.pi, + # Yaw rate in rad/s + 'r': 0.0, + # Roll acceleration in rad/s^2 + 'pdot': 0.0, + # Pitch acceleration in rad/s^2 + 'qdot': 0.0, + # Yaw acceleration in rad/s^2 + 'rdot': 0.0, + 'theta': 0.0 / 180.0 * np.pi, + 'phi': 0.0, + 'command_xi': 0.0, + 'command_eta': 0.0, + 'command_zeta': 0.0, + }, + {'desc': 'CC.M3.OVCFL000.level', # Descriptive string of the maneuver case + # Kind of trim condition, blank for trim about all three axes, for more trim conditions see + # trim_conditions.py + 'maneuver': '', + # Subcase ID number, for Nastran in acending order + 'subcase': 2, + # Setting of the operational point + # The flight speed is given by the Mach number + 'Ma': tas2Ma(70.0, 0.0), + # Aero key + 'aero': 'VC', + # Atmo key + 'altitude': 'FL000', + # Mass key + 'mass': 'M3', + # Load factor Nz + 'Nz': 1.0, + # Velocities and accelerations given in ISO 9300 coordinate system (right-handed, forward-right-down) + # Roll rate in rad/s + 'p': 0.0 / 180.0 * np.pi, + # Pitch rate in rad/s + 'q': 0.0 / 180.0 * np.pi, + # Yaw rate in rad/s + 'r': 0.0, + # Roll acceleration in rad/s^2 + 'pdot': 0.0, + # Pitch acceleration in rad/s^2 + 'qdot': 0.0, + # Yaw acceleration in rad/s^2 + 'rdot': 0.0, + }, + {'desc': 'CC.M3.OVCFL000.pullup', # Descriptive string of the maneuver case + # Kind of trim condition, blank for trim about all three axes, for more trim conditions see + # trim_conditions.py + 'maneuver': '', + # Subcase ID number, for Nastran in acending order + 'subcase': 3, + # Setting of the operational point + # The flight speed is given by the Mach number + 'Ma': tas2Ma(70.0, 0.0), + # Aero key + 'aero': 'VC', + # Atmo key + 'altitude': 'FL000', + # Mass key + 'mass': 'M3', + # Load factor Nz + 'Nz': 2.5, + # Velocities and accelerations given in ISO 9300 coordinate system (right-handed, forward-right-down) + # Roll rate in rad/s + 'p': 0.0, + # Pitch rate in rad/s + 'q': 0.0, + # Yaw rate in rad/s + 'r': 0.0, + # Roll acceleration in rad/s^2 + 'pdot': 0.0, + # Pitch acceleration in rad/s^2 + 'qdot': 0.0, + # Yaw acceleration in rad/s^2 + 'rdot': 0.0, + }, + {'desc': 'CC.M3.OVCFL000.pushdown', # Descriptive string of the maneuver case + # Kind of trim condition, blank for trim about all three axes, for more trim conditions see + # trim_conditions.py + 'maneuver': '', + # Subcase ID number, for Nastran in acending order + 'subcase': 4, + # Setting of the operational point + # The flight speed is given by the Mach number + 'Ma': tas2Ma(70.0, 0.0), + # Aero key + 'aero': 'VC', + # Atmo key + 'altitude': 'FL000', + # Mass key + 'mass': 'M3', + # Load factor Nz + 'Nz': -1.0, + # Velocities and accelerations given in ISO 9300 coordinate system (right-handed, forward-right-down) + # Roll rate in rad/s + 'p': 0.0, + # Pitch rate in rad/s + 'q': 0.0, + # Yaw rate in rad/s + 'r': 0.0, + # Roll acceleration in rad/s^2 + 'pdot': 0.0, + # Pitch acceleration in rad/s^2 + 'qdot': 0.0, + # Yaw acceleration in rad/s^2 + 'rdot': 0.0, + }] + """ + For every trimcase, a corresponding simcase is required. For maneuvers, it may be empty self.simcase = [{}]. + A time simulation is triggered if the simcase contains at least 'dt' and 't_final' + """ + self.simcase = jcl_helper.generate_empty_listofdicts(self.trimcase) + # End diff --git a/doc/tutorials/DC3_model/cfd/baseline_euler.cfg b/doc/tutorials/DC3_model/cfd/baseline_euler.cfg new file mode 100644 index 00000000..efed2230 --- /dev/null +++ b/doc/tutorials/DC3_model/cfd/baseline_euler.cfg @@ -0,0 +1,159 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Baseline parameters for DC3 with Loads Kernel____________ % +% Author: Arne Voß___________________________________________________________ % +% Institution: DLR___________________________________________________________ % +% Date: 06/2026___ % +% File Version 8.3.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +% Solver type (EULER, NAVIER_STOKES, RANS, +% INC_EULER, INC_NAVIER_STOKES, INC_RANS, +% NEMO_EULER, NEMO_NAVIER_STOKES, +% FEM_EULER, FEM_NAVIER_STOKES, FEM_RANS, FEM_LES, +% HEAT_EQUATION_FVM, ELASTICITY) +SOLVER= EULER +% +% ------------------------------- SOLVER CONTROL ------------------------------% +% +% Maximum number of inner iterations +INNER_ITER= 300 +% +% Convergence field +CONV_FIELD= ( LIFT, DRAG, MOMENT_X, MOMENT_Y, MOMENT_Z ) +% +% Min value of the residual (log10 of the residual) +CONV_RESIDUAL_MINVAL= -6 +% +% Start convergence criteria at iteration number +CONV_STARTITER= 3 +% +% Number of elements to apply the criteria +CONV_CAUCHY_ELEMS= 3 +% +% Epsilon to control the series convergence +CONV_CAUCHY_EPS= 5E-4 +% for maneuver loads 5E-4 is okay, for GAFs the initial solution should be better +% CONV_CAUCHY_EPS= 1E-5 +% +% ---------------------- REFERENCE VALUE DEFINITION ---------------------------% +% +% Reference origin for moment computation (m or in) +REF_ORIGIN_MOMENT_X = 8.566 +REF_ORIGIN_MOMENT_Y = 0.00 +REF_ORIGIN_MOMENT_Z = 0.00 +% +% Reference length for moment non-dimensional coefficients (m or in) +REF_LENGTH= 3.508 +% +% Reference area for non-dimensional force coefficients (0 implies automatic +% calculation) (m^2 or in^2) +REF_AREA= 91.7 +% +% Aircraft semi-span (0 implies automatic calculation) (m or in) +SEMI_SPAN= 29.0 +% +% Flow non-dimensionalization (DIMENSIONAL, FREESTREAM_PRESS_EQ_ONE, +% FREESTREAM_VEL_EQ_MACH, FREESTREAM_VEL_EQ_ONE) +REF_DIMENSIONALIZATION= DIMENSIONAL + +% -------------------- BOUNDARY CONDITION DEFINITION --------------------------% +% +% Euler wall boundary marker(s) (NONE = no marker) +% Implementation identical to MARKER_SYM. +MARKER_EULER= ( wing_upper, wing_lower, wing_te, wing_tips, ht, vt ) +% +% Far-field boundary marker(s) (NONE = no marker) +MARKER_FAR= ( far_away ) +% +% ------------------------ SURFACES IDENTIFICATION ----------------------------% +% +% Marker(s) of the surface in the surface flow solution file +MARKER_PLOTTING = ( wing_upper, wing_lower, wing_te, wing_tips, ht, vt ) +% +% Marker(s) of the surface where the non-dimensional coefficients are evaluated. +MARKER_MONITORING = ( wing_upper, wing_lower, wing_te, wing_tips, ht, vt ) + +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% +% +% CFL number (initial value for the adaptive CFL number) +CFL_NUMBER= 10.0 +% +% Adaptive CFL number (NO, YES) +CFL_ADAPT= YES +CFL_ADAPT_PARAM= ( 0.9, 1.1, 4.0, 1e10, 0.001, 0) +% +% ------------------------ LINEAR SOLVER DEFINITION ---------------------------% +% +% Linear solver or smoother for implicit formulations: +% BCGSTAB, FGMRES, RESTARTED_FGMRES, CONJUGATE_GRADIENT (self-adjoint problems only), SMOOTHER. +LINEAR_SOLVER= FGMRES +% +% Preconditioner of the Krylov linear solver or type of smoother (ILU, LU_SGS, LINELET, JACOBI) +LINEAR_SOLVER_PREC= ILU +% +% Minimum error of the linear solver for implicit formulations +LINEAR_SOLVER_ERROR= 1E-6 +% +% Max number of iterations of the linear solver for the implicit formulation +LINEAR_SOLVER_ITER= 10 +% +% -------------------- FLOW NUMERICAL METHOD DEFINITION -----------------------% +% +% Convective numerical method (JST, JST_KE, JST_MAT, LAX-FRIEDRICH, CUSP, ROE, AUSM, +% AUSMPLUSUP, AUSMPLUSUP2, AUSMPWPLUS, HLLC, TURKEL_PREC, +% SW, MSW, FDS, SLAU, SLAU2, L2ROE, LMROE) +%CONV_NUM_METHOD_FLOW= JST +CONV_NUM_METHOD_FLOW= ROE +% +% Use the vectorized version of the selected numerical method (available for JST family and Roe). +% SU2 should be compiled for an AVX or AVX512 architecture for best performance. +USE_VECTORIZATION= YES +% +% Monotonic Upwind Scheme for Conservation Laws (TVD) in the flow equations. +% Required for 2nd order upwind schemes (NO, YES) +%MUSCL_FLOW= NO +MUSCL_FLOW= YES +% +% ------------------------- SCREEN/HISTORY VOLUME OUTPUT --------------------------% +% +% Screen output fields (use 'SU2_CFD -d ' to view list of available fields) +SCREEN_OUTPUT= (TIME_ITER, INNER_ITER, RMS_DENSITY, LIFT, DRAG, MOMENT_X, MOMENT_Y, MOMENT_Z, CAUCHY_LIFT, CAUCHY_DRAG, CAUCHY_MOMENT_X, CAUCHY_MOMENT_Y, CAUCHY_MOMENT_Z, LINSOL_ITER, LINSOL_RESIDUAL, AVG_CFL, WALL_TIME) +% +% History output groups (use 'SU2_CFD -d ' to view list of available fields) +HISTORY_OUTPUT= (ITER, RMS_RES, AERO_COEFF, CAUCHY, NONPHYSICAL_POINTS, LINSOL, CFL_NUMBER, WALL_TIME) +% +% list of writing frequencies corresponding to the list in OUTPUT_FILES +OUTPUT_WRT_FREQ= 999999 +% +% Volume output fields/groups (use 'SU2_CFD -d ' to view list of available fields) +VOLUME_OUTPUT= (COORDINATES, SOLUTION, PRIMITIVE, RESIDUAL) +% +% ------------------------- INPUT/OUTPUT FILE INFORMATION --------------------------% +% +% Mesh input file +MESH_FILENAME= mesh.su2 +% ------------------------ GRID DEFORMATION PARAMETERS ------------------------% +% +% Linear solver or smoother for implicit formulations (FGMRES, RESTARTED_FGMRES, BCGSTAB) +DEFORM_LINEAR_SOLVER= CONJUGATE_GRADIENT +%DEFORM_LINEAR_SOLVER= BCGSTAB +% +% Minimum residual criteria for the linear solver convergence of grid deformation +DEFORM_LINEAR_SOLVER_ERROR= 1E-10 +% +% Number of smoothing iterations for mesh deformation +DEFORM_LINEAR_SOLVER_ITER= 1000 +% +% Print the residuals during mesh deformation to the console (YES, NO) +DEFORM_CONSOLE_OUTPUT= YES +% +% Type of element stiffness imposed for FEA mesh deformation (INVERSE_VOLUME, +% WALL_DISTANCE, CONSTANT_STIFFNESS) +DEFORM_STIFFNESS_TYPE= INVERSE_VOLUME +%DEFORM_STIFFNESS_TYPE= WALL_DISTANCE +% diff --git a/doc/tutorials/cfd_steady.md b/doc/tutorials/cfd_steady.md new file mode 100644 index 00000000..0795c2bb --- /dev/null +++ b/doc/tutorials/cfd_steady.md @@ -0,0 +1,116 @@ +# Maneuver Loads with CFD +This section describes the use of the interface with the CFD solver [SU2](https://su2code.github.io/) for maneuver loads calculation. Because this is an advanced topic, please make sure that you are familiar with both Loads Kernel and SU2. + +## General Ideas +Next to the aerodynamic panel methods, CFD solvers offer an additional source for aerodynamic forces. The direct coupling of Loads Kernel with a CFD solver (steady and unsteady solutions) is not very complicated and more an implementation / interface question. The advantages are that all aerodynamic non-linearities are captured (as good as the CFD solution is) and included in the loads analysis. Areas of application include: +* Transonic operational points (most transport aircraft) +* Vortex dominated flows (many military aircraft) +* Maneuvers and gusts where flow separation occurs +* Volumetric bodies (panel methods and fuselages are no friends) + +Key requirements for a CFD solver for loads and aeroelasticity purposes are: +* Steady and unsteady solutions +* Volume mesh deformation +* 1-cosine gust, e.g. via a field velocity approach +* Python interface +* Robustness & fast time to solution + +## Interface with SU2 +The primary data exchange with SU2 (forces on the CFD surface, elastic deformations and rigid body motion) happens in-memory via the Python interface. In addition, a configuration file (.cfg) with the baseline parameters of the CFD solver is necessary. To complete the baseline parameters, Loads Kernel adds trimcase-depended parameters that describe the current operational point and then initializes SU2 with the updated parameter file (para_subcase_*). A file output of the surface or volume solution is not necessary but helps with visualization or to diagnose problems. + +The CFD surface deformations are calculated by Loads Kernel and include the elastic deformations as well as control surface deflections. They are calculated on the underlying structural grid and on the aerodynamic panel mesh respectively and interpolated on the CFD mesh using radial basis functions. SU2 is then asked to perform the volume mesh deformation. For maneuver loads, the rigid body motion is imposed using a field velocity approach, meaning the Mach number of the farfield is set to zero. + +> Next to maneuver loads, Loads Kernel's current SU2 interface also allows for a gust encounter and the computation of linearized CFD forces (experimental), which can be used for frequency domain analyses. + +Remember that the CFD solution is an iterative process and a compromise between numerical accuracy and speed. In the context of a trim solution, CFD is the most inner loop as illustrated below. The cauchy convergence criterion has proven to be a good means to establish a converged CFD solution. + +DC3 + +*Sketch of iterative trim solution scheme.* + +## Prerequisites +SU2 needs to be compiled with the Python wrapper and with MPI. If you can import SU2 in Python, this is a good sign. +``` +import SU2 +import pysu2 +``` +Also make sure that the correct MPI is used, all paths are set, etc. + +## Tutorial +A light-weight, inviscid Euler mesh with 440k nodes is available on request (due to file size) for the purpose of this tutorial. Like the aerodynamic panel model, it comprises the main wings and the empennage, as shown below. + +DC3 + +*Euler CFD mesh with 440k nodes for the DC3.* + +A file with some baseline parameters for SU2 can be found in the folder tutorials -> DC3_model -> cfd. Note that those parameters are tuned for demonstration + speed and might need to be changed for different models and/or aircraft. In addition to the Euler solution, SU2 has been tested by the author with RANS, requiring no changes on the Loads Kernel side and only a different set of baseline parameters plus a different mesh. Because the CFD-based solution sequences are implemented on top of the classical, panel-based solutions, only the following items are added to an existing JCL. As an example, a JCL named ‘jcl_dc3_cfd_euler.py’ and can be found in the folder tutorials -> DC3_model -> JCLs. + +First, the CFD solution is selected in the 'aero' section. As CFD solvers typically create a large number of files, it is convenient to use in a dedicated folder 'cfd_root' where all CFD-related files are kept. +``` +cfd_root = '/path/to/CFD/files/' + +# Settings for the aerodynamic model +self.aero = {'method': 'cfd_steady', + # Additional parameters for CFD + 'para_path': cfd_root, + 'para_file': 'baseline_euler.cfg', + # Currently implemented interfaces: 'tau' or 'su2' + 'cfd_solver': 'su2', + } +``` +Next, Loads Kernel needs to be aware of the CFD mesh and the surface markers used for surface deformation. +``` +# General CFD surface mesh information +self.meshdefo = {'surface': {'fileformat': 'su2', # implemented file formats: 'cgns', 'netcdf', 'su2' + # file name of the CFD mesh + 'filename_grid': os.path.join(cfd_root, 'CFD_mesh_440kNodes.su2'), + # list of markers [1, 2, ...] or ['upper', 'lower', ...] of surfaces to be included in + # deformation + 'markers': ['wing_upper', 'wing_lower', 'wing_te', 'wing_tips', 'ht', 'vt'], + }, + } +``` + +Start by running the preprocessing single-threaded and check/inspect the model. +``` +from loadskernel import program_flow + +# Here you launch the Loads Kernel with your job +k = program_flow.Kernel('jcl_dc3_cfd_euler', pre=True, main=False, post=False, + path_input='/path/to/JCLs', + path_output='/path/to/output') +k.run() +``` +If the model looks fine, proceed with running the mainprocessing in parallel using MPI. Modify the launch.py to use the ClusterMode and with i being the trim case, e.g. i=0 for the first case. This facilitates operation on a high performance cluster with a job scheduler, leverage the job queening system to handle the trimcases (one job = one trimcase). Working only on your local system, you might use 'for i in range(0, n)' or similar to run all trimcases sequentially. + +``` +from loadskernel import program_flow + +# Here you launch the Loads Kernel with your job +c = program_flow.ClusterMode('jcl_dc3_cfd_euler', pre=False, main=True, post=False, + path_input='/path/to/JCLs', + path_output='/path/to/output') +c.run_cluster(i=0) + +#for i in range(0, n): +# c.run_cluster(i) +``` +Then use mpiexec to essentially launch Loads Kernel four times (or more) with MPI. Both Loads Kernel and SU2, launched from within Loads Kernel, should detect MPI and behave accordingly. +``` +mpiexec -np 4 python launch.py +``` + +The cluster mode creates one response per job/trimcase and allows to gather all responses, creating one unified HDF5 file, so that the postprocessing and the GUIs should work as normal. +``` +c.gather_responses() +``` + +The pictures below show the deformed CFD surface (blue) and the surface pressure coefficient for a pullup maneuver with Nz = 2.5, corresponding to subcase 3 in the example. + +DC3 + +*CFD surface deformation, pullup maneuver with Nz = 2.5.* + +DC3 + +*Surface pressure coefficient cp, pullup maneuver with Nz = 2.5.* diff --git a/doc/tutorials/images/cfd_mesh.png b/doc/tutorials/images/cfd_mesh.png new file mode 100644 index 00000000..ec0d2cf1 Binary files /dev/null and b/doc/tutorials/images/cfd_mesh.png differ diff --git a/doc/tutorials/images/cfd_pullup_cp.png b/doc/tutorials/images/cfd_pullup_cp.png new file mode 100644 index 00000000..0b391b26 Binary files /dev/null and b/doc/tutorials/images/cfd_pullup_cp.png differ diff --git a/doc/tutorials/images/cfd_pullup_defo.png b/doc/tutorials/images/cfd_pullup_defo.png new file mode 100644 index 00000000..30ec9e1b Binary files /dev/null and b/doc/tutorials/images/cfd_pullup_defo.png differ diff --git a/doc/tutorials/images/iterative_solution_scheme.png b/doc/tutorials/images/iterative_solution_scheme.png new file mode 100755 index 00000000..f5f42515 Binary files /dev/null and b/doc/tutorials/images/iterative_solution_scheme.png differ diff --git a/doc/tutorials/myst.yml b/doc/tutorials/myst.yml index fd83e34d..22bbacc7 100644 --- a/doc/tutorials/myst.yml +++ b/doc/tutorials/myst.yml @@ -16,6 +16,7 @@ project: - file: maneuver_loads.ipynb - file: gust_time_domain.ipynb - file: flutter.ipynb + - file: cfd_steady.md site: options: logo: ../../graphics/LK_logo2.png