diff --git a/_temp/scratch.py b/_temp/scratch.py new file mode 100644 index 0000000..93047de --- /dev/null +++ b/_temp/scratch.py @@ -0,0 +1,102 @@ +import numpy as np + +from optvl import OVLSolver +import pickle +import os + + + +mesh = np.load("wing_mesh.npy") + + +with open("wing_mesh.pkl", 'wb') as f: + pickle.dump(mesh, f) + + + +test_mesh = np.zeros((2,2,3)) +test_mesh[1,:,0] = 1.0 +test_mesh[:,1,1] = 1.0 +test_mesh[:,:,2] = 1.0 + + +base_dir = os.path.dirname(os.path.abspath(__file__)) # Path to current folder +geom_dir = os.path.join(base_dir, '..', 'geom_files') + +rect_file = os.path.join(geom_dir, 'rect_with_body.avl') + + +surf = { + "Wing": { + # General + "component": np.int32(1), # logical surface component index (for grouping interacting surfaces, see AVL manual) + # "yduplicate": np.float64(0.0), # surface is duplicated over the ysymm plane + # Geometry + "scale": np.array( + [1.0, 1.0, 1.0], dtype=np.float64 + ), # scaling factors applied to all x,y,z coordinates (chords arealso scaled by Xscale) + "translate": np.array( + [0.0, 0.0, 0.0], dtype=np.float64 + ), # offset added on to all X,Y,Z values in this surface + # Geometry: Mesh + "mesh": np.float64(test_mesh), # (nx,ny,3) numpy array containing mesh coordinates + # Control Surface Specification + "control_assignments": { + "Elevator" : {"assignment":np.arange(0,test_mesh.shape[1]), + "xhinged": 0.5, # x/c location of hinge + "vhinged": np.array([0,1,0]), # vector giving hinge axis about which surface rotates + "gaind": -1.0, # control surface gain + "refld": 1.0 # control surface reflection, sign of deflection for duplicated surface + } + }, + + } +} + +fuselage = {"Fuse pod": { + # General + # 'yduplicate': np.float64(0), # body is duplicated over the ysymm plane + # Geometry + "scale": np.array( + [1.0, 1.0, 1.0] + ), # scaling factors applied to all x,y,z coordinates (chords areal so scaled by Xscale) + "translate": np.array([0.0, 0.0, 0.0]), # offset added on to all X,Y,Z values in this surface + # Discretization + "nvb": np.int32(4), # number of source-line nodes + "bspace": np.float64(2.0), # lengthwise node spacing parameter + "bfile": "../geom_files/fuseSimple.dat", # body oml file name +} +} + +input_dict = { + "title": "MACH MDAO AVL", + "mach": np.float64(0.0), + "iysym": np.int32(0), + "izsym": np.int32(0), + "zsym": np.float64(0.0), + "Sref": np.float64(1.123), + "Cref": np.float64(0.25), + "Bref": np.float64(6.01), + "XYZref": np.array([0.0, 0, 0],dtype=np.float64), + "CDp": np.float64(0.0), + "surfaces": surf, + "bodies": fuselage, + # Global Control and DV info + "dname": ["Elevator"], # Name of control input for each corresonding index +} + + +solver = OVLSolver(input_dict=input_dict,debug=True) +# solver = OVLSolver(geo_file=rect_file,debug=True) + +solver.set_variable("alpha", 25.0) +solver.set_variable("beta", 5.0) +solver.execute_run() + +# solver.plot_geom() +with open("rect_with_body.pkl", 'wb') as f: + pickle.dump(input_dict, f) + + + + diff --git a/config/defaults/config.MACOS_GFORTRAN.mk b/config/defaults/config.MACOS_GFORTRAN.mk index faa3d9e..491c6bb 100644 --- a/config/defaults/config.MACOS_GFORTRAN.mk +++ b/config/defaults/config.MACOS_GFORTRAN.mk @@ -6,13 +6,11 @@ FF90 = gfortran -FF90_FLAGS = -fdefault-real-8 -fdefault-double-8 -O2 -fPIC -Wno-align-commons -std=legacy -C -mmacosx-version-min=13.6 -# FF90_FLAGS = -fdefault-real-8 -fdefault -double-8 -O0 -fPIC -Wno-align-commons -Werror=line-truncation -std=legacy -C -mmacosx-version-min=13.6 -g -fcheck=bounds -finit-real=snan -finit-integer=-999999 -ftrapping-math -ftrapv +# FF90_FLAGS = -fdefault-real-8 -fdefault-double-8 -O2 -fPIC -Wno-align-commons -std=legacy -C -mmacosx-version-min=13.6 +FF90_FLAGS = -fdefault-real-8 -fdefault -double-8 -O0 -fPIC -Wno-align-commons -Werror=line-truncation -std=legacy -C -mmacosx-version-min=13.6 -g -fcheck=bounds -finit-real=snan -finit-integer=-999999 -ftrapping-math -ftrapv C_FLAGS = -O2 -fPIC -mmacosx-version-min=13.6 - -F2PY = f2py F2PY_FF90 = gfortran PYTHON = python diff --git a/geom_files/rect_out.avl b/geom_files/rect_out.avl new file mode 100644 index 0000000..1cbd4d4 --- /dev/null +++ b/geom_files/rect_out.avl @@ -0,0 +1,41 @@ +# generated using OptVL v1.4.2.dev0 +#=============================================================================== +#------------------------------------ Header ----------------------------------- +#=============================================================================== +MACH MDAO AVL +#Mach +0.12341234 +#IYsym IZsym Zsym +0 0 0.0 +#Sref Cref Bref +1.0 2.0 3.0 +#Xref Yref Zref +4.0 5.0 6.0 +#CD0 +0.0 +#=============================================================================== +#------------------------------------- Wing ------------------------------------ +#=============================================================================== +SURFACE +Wing +#Nchordwise Cspace [Nspanwise Sspace] +1 1.0 1 -2.0 +SCALE +1.0 1.0 1.0 +TRANSLATE +0.0 0.0 0.0 +ANGLE +0.0 +#--------------------------------------- +SECTION +#Xle Yle Zle | Chord Ainc Nspan Sspace + 0.000000 0.000000 0.000000 1.000000 0.000000 + CONTROL +#surface gain xhinge hvec SgnDup + Elevator -1.0 0.5 0.000000 1.000000 0.000000 1.0 +SECTION +#Xle Yle Zle | Chord Ainc Nspan Sspace + 0.000000 1.000000 0.000000 1.000000 0.000000 + CONTROL +#surface gain xhinge hvec SgnDup + Elevator -1.0 0.5 0.000000 1.000000 0.000000 1.0 diff --git a/geom_files/rect_with_body.pkl b/geom_files/rect_with_body.pkl new file mode 100644 index 0000000..c4bca9e Binary files /dev/null and b/geom_files/rect_with_body.pkl differ diff --git a/geom_files/wing_mesh.pkl b/geom_files/wing_mesh.pkl new file mode 100644 index 0000000..12c5464 Binary files /dev/null and b/geom_files/wing_mesh.pkl differ diff --git a/optvl/om_wrapper.py b/optvl/om_wrapper.py index baea656..9a43395 100644 --- a/optvl/om_wrapper.py +++ b/optvl/om_wrapper.py @@ -117,7 +117,16 @@ def add_ovl_geom_vars(self, ovl, add_as="inputs", include_airfoil_geom=False): elif add_as == "outputs": self.add_output(geom_key, val=surf_data[surf][key], tags="geom") +def add_ovl_mesh_out_as_output(self, ovl): + surf_data = ovl.get_surface_params() + + meshes,_ = ovl.get_cp_data() + for surf in surf_data: + idx_surf = ovl.surface_names.index(surf) + out_name = f"{surf}:mesh" + self.add_output(out_name, val=meshes[idx_surf], tags="geom_mesh") + def add_ovl_conditions_as_inputs(sys, ovl): # TODO: add all the condition constraints @@ -331,7 +340,7 @@ def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode): res_d_seeds = d_residuals["gamma_d"] res_u_seeds = d_residuals["gamma_u"] - con_seeds, geom_seeds, gamma_seeds, gamma_d_seeds, gamma_u_seeds, param_seeds, ref_seeds = ( + con_seeds, geom_seeds, mesh_seeds, gamma_seeds, gamma_d_seeds, gamma_u_seeds, param_seeds, ref_seeds = ( self.ovl._execute_jac_vec_prod_rev( res_seeds=res_seeds, res_d_seeds=res_d_seeds, res_u_seeds=res_u_seeds ) @@ -601,7 +610,7 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): # print(var_name, body_axis_seeds[func_key]) print(f" running rev mode derivs for {func_key}") - con_seeds, geom_seeds, gamma_seeds, gamma_d_seeds, gamma_u_seeds, param_seeds, ref_seeds = ( + con_seeds, geom_seeds, mesh_seeds, gamma_seeds, gamma_d_seeds, gamma_u_seeds, param_seeds, ref_seeds = ( self.ovl._execute_jac_vec_prod_rev( func_seeds=func_seeds, consurf_derivs_seeds=csd_seeds, @@ -713,14 +722,18 @@ class OVLMeshReader(om.ExplicitComponent): def initialize(self): self.options.declare("geom_file", types=str) self.options.declare("mass_file", default=None) + self.options.declare("mesh_output",default=False) def setup(self): geom_file = self.options["geom_file"] mass_file = self.options["mass_file"] + mesh_output = self.options["mesh_output"] avl = OVLSolver(geo_file=geom_file, mass_file=mass_file, debug=False) add_ovl_geom_vars(self, avl, add_as="outputs", include_airfoil_geom=True) + if mesh_output: + add_ovl_mesh_out_as_output(self,avl) class Differencer(om.ExplicitComponent): def setup(self): diff --git a/optvl/optvl_class.py b/optvl/optvl_class.py index cdcf7ae..b57dbb0 100644 --- a/optvl/optvl_class.py +++ b/optvl/optvl_class.py @@ -222,22 +222,21 @@ class OVLSolver(object): ad_suffix = "_DIFF" # Primary array limits: These also need to updated in the Fortran layer if changed - NSMAX = 400 # number of chord strips - NFMAX = 30 # number of surfaces - NLMAX = 500 # number of source/doublet line nodes + NVMAX = 5000 # number of horseshoe vortices + NSMAX = 500 # number of chord strips + NSECMAX = 301 # nuber of geometry sections + NFMAX = 100 # number of surfaces + NLMAX = 502 # number of source/doublet line nodes NBMAX = 20 # number of bodies NUMAX = 6 # number of freestream parameters (V,Omega) NDMAX = 30 # number of control deflection parameters - NGMAX = 20 # number of design variables + NGMAX = 21 # number of design variables NRMAX = 25 # number of stored run cases - NTMAX = 5000 # number of stored time levels - IBX = 300 - ICONX = 20 + NTMAX = 503 # number of stored time levels + NOBMAX=1 # max number of off body points + ICONX = 20 # + IBX = 200 # max number of airfoil coordinates - if platform.system == "Windows": - NVMAX = 5000 # number of horseshoe vortices - else: - NVMAX = 6000 # number of horseshoe vortices def __init__( self, @@ -319,11 +318,11 @@ def __init__( if mass_file is not None: self.avl.loadmass(mass_file) elif input_dict is not None: - self.load_input_dict(input_dict, postCheck=True) + self.load_input_dict(input_dict, post_check=True) self.avl.loadgeo("") else: raise ValueError("neither a geometry file nor an input options dictionary was specified") - + # todo store the default dict somewhere else # the control surface contraints get added to this array in the __init__ self.conval_idx_dict = { @@ -404,6 +403,11 @@ def __init__( deriv_key = self._get_deriv_key(var, func) self.case_body_derivs_to_fort_var[deriv_key] = ["CASE_R", f"{func_to_prefix[func]}TOT_U_BA", idx_var] + # In the case where there is no mesh then we have to initialize these before _init_map_data so ad seeds work correclty + if not input_dict or ("mesh" not in input_dict.keys()): + self.mesh_idx_first = np.zeros(self.get_num_surfaces(),dtype=np.int32) + self.y_offsets = np.zeros(self.get_num_surfaces(),dtype=np.float64) + # the case parameters are stored in a 1d array, # these indices correspond to the position of each parameter in that arra self._init_map_data() @@ -417,6 +421,7 @@ def __init__( def _init_map_data(self): """Used in the __init__ method to allocate the slice data for the surfaces""" self.surf_geom_to_fort_var = {} + self.surf_mesh_to_fort_var = {} self.surf_section_geom_to_fort_var = {} self.surf_pannel_to_fort_var = {} self.con_surf_to_fort_var = {} @@ -428,7 +433,7 @@ def _init_map_data(self): # we have to loop over the unique surfaces because those are the # only ones that have geometric data from the input file - # AVL only mirror the mesh data it doesn't infer the input data + # AVL only mirrors the mesh data it doesn't infer the input data # for the mirrored surface for surf_name in self.unique_surface_names: idx_surf = self.get_surface_index(surf_name) @@ -500,6 +505,16 @@ def _setup_surface_maps(self, surf_name: str, idx_surf: int, num_sec: int): "load": ["SURF_L", "LFLOAD", slice_idx_surf], } + # We need this map to be setup regardless of if we have a mesh so that the ad routines work correctly + # if self.get_avl_fort_arr("SURF_MESH_L", "LSURFMSH", slicer=slice_idx_surf): + nvc = self.get_avl_fort_arr("SURF_GEOM_I", "NVC", slicer=slice_idx_surf) + nvs = self.get_avl_fort_arr("SURF_GEOM_I", "NVS", slicer=slice_idx_surf) + mesh_size = ((nvc+1)*(nvs+1)) + slice_mesh_surf = (slice(self.mesh_idx_first[idx_surf],self.mesh_idx_first[idx_surf]+mesh_size),slice(None)) + self.surf_mesh_to_fort_var[surf_name] = { + "mesh": ["SURF_MESH_R", "MSHBLK", slice_mesh_surf] + } + icontd_slices = [] idestd_slices = [] xhinged_slices = [] @@ -603,14 +618,14 @@ def _setup_section_maps(self, surf_name: str, idx_surf: int, num_sec: int, nasec "zlasec": ["SURF_GEOM_R", "ZLASEC", zlasec_slices], } - def load_input_dict(self, input_dict: dict, preCheck: bool = True, postCheck: bool = False): + def load_input_dict(self, input_dict: dict, pre_check: bool = True, post_check: bool = False): """Reads and loads the input dictionary data into optvl. Equivalent to INPUT routine in AVL. Args: input_dict: input dictionary in optvl format - preCheck: perform additional verification of the user's input dictionary before loading into AVL - postCheck: verify certain inputs values are correctly reflected in the Fortran layer + pre_check: perform additional verification of the user's input dictionary before loading into AVL + post_check: verify certain inputs values are correctly reflected in the Fortran layer """ # Initialize Variables and Counters @@ -630,7 +645,7 @@ def load_input_dict(self, input_dict: dict, preCheck: bool = True, postCheck: bo self.set_avl_fort_arr("SURF_L", "LRANGE", True, slicer=slice(None, self.NFMAX)) # Perform pre-check of user's input dictionary before loading into AVL - if preCheck: + if pre_check: input_dict = pre_check_input_dict(input_dict) def get_types_from_blk(common_blk): @@ -725,6 +740,12 @@ def check_type(key, avl_vars, given_val, cast_type=True): val = optional_header_defaults[key] else: raise ValueError(f"Key {key} not found in input dictionary but is required") + elif key == "title": + # We need to apply this function to the title string so that the tecplot file writing works correctly + # val = self._str_to_fort_str(input_dict[key],num_max_char=120) + # NOTE: SAB this seems to have broken again in AVL 3.52. Need to manually set it for now + val = "dummy" + self.avl.CASE_C.TITLE = self._str_to_fort_str(input_dict[key], num_max_char=120) else: val = input_dict[key] @@ -735,30 +756,38 @@ def check_type(key, avl_vars, given_val, cast_type=True): self.set_avl_fort_arr("CASE_R", "YSYM", 0.0) # YSYM Hardcoded to 0 # set the global control variable options - ncontrol = len(input_dict.get("dname", [])) - if ncontrol > self.NDMAX: + num_controls = len(input_dict.get("dname", [])) + if num_controls > self.NDMAX: raise RuntimeError(f"Number of specified controls exceeds {self.NDMAX}. Raise NDMAX!") - self.set_avl_fort_arr("CASE_I", "NCONTROL", ncontrol) + self.set_avl_fort_arr("CASE_I", "NCONTROL", num_controls) - for k in range(ncontrol): + for k in range(num_controls): self.avl.CASE_C.DNAME[k] = input_dict["dname"][k] # set the gloabl design variable options - ndesign = len(input_dict.get("gname", [])) - self.set_avl_fort_arr("CASE_I", "NDESIGN", ncontrol) - if ndesign > self.NGMAX: + num_design = len(input_dict.get("gname", [])) + self.set_avl_fort_arr("CASE_I", "NDESIGN", num_design) + if num_design > self.NGMAX: raise RuntimeError(f"Number of specified design variables exceeds {self.NGMAX}. Raise NGMAX!") - for k in range(ncontrol): + for k in range(num_design): self.avl.CASE_C.GNAME[k] = input_dict["gname"][k] # Set total number of surfaces in one shot num_surfs = len(input_dict["surfaces"]) if num_surfs < self.NFMAX: - self.set_avl_fort_arr("CASE_I", "NSURF", num_surfs) # YSYM Hardcoded to 0 + self.set_avl_fort_arr("CASE_I", "NSURF", num_surfs) else: raise RuntimeError(f"Number of specified surfaces, {num_surfs}, exceeds {self.NFMAX}. Raise NFMAX!") + # Class variable to store the starting index of all meshes. Set to 0 for no mesh. + # We will insert entries into it for duplicate surfaces later but right now it's only for unique surfaces + self.mesh_idx_first = np.zeros(self.get_num_surfaces(),dtype=np.int32) + + # Class variable to store the yoffset for duplicated meshes. This information is needed for correcting retreiving + # a duplicated mesh as its normally only passed as a dummy argument into the SDUPL subroutine and not stored in the fortran layer. + self.y_offsets = np.zeros(self.get_num_surfaces(),dtype=np.float64) + # Load surfaces if num_surfs > 0: surf_names = list(input_dict["surfaces"].keys()) @@ -766,6 +795,7 @@ def check_type(key, avl_vars, given_val, cast_type=True): # setup surface data for initial input self.surf_geom_to_fort_var = {} self.surf_section_geom_to_fort_var = {} + self.surf_mesh_to_fort_var = {} self.surf_pannel_to_fort_var = {} self.con_surf_to_fort_var = {} self.des_var_to_fort_var = {} @@ -775,13 +805,18 @@ def check_type(key, avl_vars, given_val, cast_type=True): for surf_name in input_dict["surfaces"]: surf_dict = input_dict["surfaces"][surf_name] + # For meshes set the number of "sections" to the number of strips in the mesh so that the slice maps are setup correctly. + # This is automatically done by the pre-check routine num_secs = surf_dict["num_sections"] - # Set total number of sections in one shot - if num_secs < self.NSMAX: + + # Check how many strip/sections we have defined so far and that it doesn't exceed NSECMAX + cur_secs = np.sum(self.get_avl_fort_arr("SURF_GEOM_I","NSEC")) + if cur_secs + num_secs < self.NSECMAX: + # Set total number of sections in one shot self.set_avl_fort_arr("SURF_GEOM_I", "NSEC", num_secs, slicer=idx_surf) else: raise RuntimeError( - f"Number of specified sections for surface {surf_name} exceeds {self.NSMAX}. Raise NSMAX!" + f"Number of specified sections/strips exceeds {self.NSECMAX}. Raise NSECMAX!" ) # Set the number of control and design variables for the surface @@ -801,26 +836,41 @@ def check_type(key, avl_vars, given_val, cast_type=True): # fmt: off optional_surface_defaults = { - "nspan": 0, - "sspace": 0.0, - "use surface spacing": False, + "nspan": 0, + "sspace": 0.0, + "use surface spacing": False, "component": idx_surf+1, # +1 for 1-based indexing in fortran - "scale": np.array([1.,1.,1.]), - "translate": np.array([0.,0.,0.]), - "angle": 0.0, - "wake": True, - "albe": True, - "load": True, + "scale": np.array([1.,1.,1.]), + "translate": np.array([0.,0.,0.]), + "angle": 0.0, + "aincs": np.zeros(num_secs, dtype=np.float64), + "wake": True, + "albe": True, + "load": True, "clcd": np.zeros(6, dtype=np.float64), "nspans": np.zeros(num_secs, dtype=np.int32), "sspaces": np.zeros(num_secs, dtype=np.float64), "clcdsec": np.zeros((num_secs,6)), "claf": np.ones(num_secs), } + + + ignore_if_mesh = { + "xles", + "yles", + "zles", + "chords", + "nchordwise", + "cspace", + "sspaces" + "sspace", + "nspan", + } + # fmt: on # set some flags based on the options used for this surface - if "sspace" in surf_dict: + if ("sspace" in surf_dict) and ("mesh" not in surf_dict): self.set_avl_fort_arr("SURF_GEOM_L", "LSURFSPACING", True, slicer=idx_surf) else: self.set_avl_fort_arr("SURF_GEOM_L", "LSURFSPACING", False, slicer=idx_surf) @@ -840,7 +890,9 @@ def check_type(key, avl_vars, given_val, cast_type=True): self.surf_geom_to_fort_var[surf_name].items(), self.surf_pannel_to_fort_var[surf_name].items() ): if key not in surf_dict: - if key in optional_surface_defaults: + if (key == "yduplicate") or (("mesh" in surf_dict) and (key in ignore_if_mesh)): + continue + elif key in optional_surface_defaults: val = optional_surface_defaults[key] else: raise ValueError(f"Key {key} not found in surface dictionary, {surf_name}, but is required") @@ -948,21 +1000,25 @@ def check_type(key, avl_vars, given_val, cast_type=True): # --- setup control variables for each section --- # Load control surfaces if "icontd" in surf_dict.keys(): - for j in range(num_secs): + for idx_sec in range(num_secs): # check to make sure this section has control vars - if surf_dict["num_controls"][j] == 0: + if surf_dict["num_controls"][idx_sec] == 0: continue for key, avl_vars in self.con_surf_to_fort_var[surf_name].items(): avl_vars_secs = self.con_surf_to_fort_var[surf_name][key] - avl_vars = (avl_vars_secs[0], avl_vars_secs[1], avl_vars_secs[2][j]) + avl_vars = (avl_vars_secs[0], avl_vars_secs[1], avl_vars_secs[2][idx_sec]) if key not in surf_dict: raise ValueError( f"Key {key} not found in surf dictionary, `{surf_name}` but is required" ) else: - val = surf_dict[key][j] + # This has to be incremented by 1 for Fortran indexing + if key == "icontd": + val = np.array(surf_dict[key][idx_sec],dtype=np.int32) + 1 + else: + val = np.array(surf_dict[key][idx_sec],dtype=np.float64) val = check_type(key, avl_vars, val) self.set_avl_fort_arr(avl_vars[0], avl_vars[1], val, slicer=avl_vars[2]) @@ -970,21 +1026,25 @@ def check_type(key, avl_vars, given_val, cast_type=True): # --- setup design variables for each section --- # Load design variables if "idestd" in surf_dict.keys(): - for j in range(num_secs): + for idx_sec in range(num_secs): # check to make sure this section has control vars - if surf_dict["num_design_vars"][j] == 0: + if surf_dict["num_design_vars"][idx_sec] == 0: continue for key, avl_vars in self.des_var_to_fort_var[surf_name].items(): avl_vars_secs = self.des_var_to_fort_var[surf_name][key] - avl_vars = (avl_vars_secs[0], avl_vars_secs[1], avl_vars_secs[2][j]) + avl_vars = (avl_vars_secs[0], avl_vars_secs[1], avl_vars_secs[2][idx_sec]) if key not in surf_dict: raise ValueError( f"Key {key} not found in surf dictionary, `{surf_name}` but is required" ) else: - val = surf_dict[key][j] + # This has to be incremented by 1 for Fortran indexing + if key == "idestd": + val = np.array(surf_dict[key][idx_sec],dtype=np.int32) + 1 + else: + val = np.array(surf_dict[key][idx_sec],dtype=np.float64) val = check_type(key, avl_vars, val) self.set_avl_fort_arr(avl_vars[0], avl_vars[1], val, slicer=avl_vars[2]) @@ -992,10 +1052,21 @@ def check_type(key, avl_vars, given_val, cast_type=True): # Make the surface if self.debug: print(f"Building surface: {surf_name}") - self.avl.makesurf(idx_surf + 1) # +1 to convert to 1 based indexing + # Load the mesh and make if one is specified otherwise just make + if "mesh" in surf_dict.keys(): + if "flatten mesh" not in surf_dict.keys(): + surf_dict["flatten mesh"] = True + self.set_mesh(idx_surf, surf_dict["mesh"],flatten=surf_dict["flatten mesh"],update_nvs=True,update_nvc=True) # set_mesh handles the Fortran indexing and ordering + self.avl.makesurf_mesh(idx_surf + 1) #+1 for Fortran indexing + else: + self.avl.makesurf(idx_surf + 1) # +1 to convert to 1 based indexing if "yduplicate" in surf_dict.keys(): self.avl.sdupl(idx_surf + 1, surf_dict["yduplicate"], "YDUP") + # Insert duplicate into the mesh first index array + self.mesh_idx_first = np.insert(self.mesh_idx_first,idx_surf+1,self.mesh_idx_first[idx_surf]) + self.y_offsets[idx_surf] = surf_dict["yduplicate"] + self.y_offsets = np.insert(self.y_offsets,idx_surf+1,self.y_offsets[idx_surf]) self.avl.CASE_I.NSURF += 1 idx_surf += 1 @@ -1074,7 +1145,8 @@ def check_type(key, avl_vars, given_val, cast_type=True): idx_body += 1 - if postCheck: + + if post_check: self.post_check_input(input_dict) if self.debug: @@ -1099,6 +1171,91 @@ def check_type(key, avl_vars, given_val, cast_type=True): # Tell AVL that geometry exists now and is ready for analysis self.avl.CASE_L.LGEO = True + def set_mesh(self, idx_surf: int, mesh: np.ndarray, flatten:bool=True, update_nvs: bool=False, update_nvc: bool=False): + """Sets a mesh directly into OptVL. + + Args: + idx_surf (int): the surface to apply the mesh to + mesh (np.ndarray): XYZ mesh array (nx,ny,3) + flatten (bool): Should OptVL flatten the mesh when placing vorticies and control points + update_nvs (bool): Should OptVL update the number of spanwise elements for the given mesh + update_nvc (bool): Should OptVL update the number of chordwise elements for the given mesh + """ + nx = copy.deepcopy(mesh.shape[0]) + ny = copy.deepcopy(mesh.shape[1]) + + if update_nvs: + self.avl.SURF_GEOM_I.NVS[idx_surf] = ny-1 + + if update_nvc: + self.avl.SURF_GEOM_I.NVC[idx_surf] = nx-1 + + if flatten: + self.avl.SURF_MESH_L.LMESHFLAT[idx_surf] = True + else: + self.avl.SURF_MESH_L.LMESHFLAT[idx_surf] = False + + # Compute and set the mesh starting index + if idx_surf != 0: + self.mesh_idx_first[idx_surf] = self.mesh_idx_first[idx_surf-1] + (self.avl.SURF_GEOM_I.NVS[idx_surf-1]+1)*(self.avl.SURF_GEOM_I.NVC[idx_surf-1]+1) + + self.set_avl_fort_arr("SURF_MESH_I","MFRST",self.mesh_idx_first[idx_surf]+1,slicer=idx_surf) + + # Reshape the mesh + # mesh = mesh.ravel(order="C").reshape((3,mesh.shape[0]*mesh.shape[1]), order="F") + mesh = mesh.transpose((1,0,2)).reshape((mesh.shape[0]*mesh.shape[1],3)) + + # Set the mesh + self.set_avl_fort_arr("SURF_MESH_R","MSHBLK",mesh, slicer=(slice(self.mesh_idx_first[idx_surf],self.mesh_idx_first[idx_surf]+nx*ny),slice(0,3))) + + # Flag surface as using mesh geometry + self.avl.SURF_MESH_L.LSURFMSH[idx_surf] = True + + def get_mesh(self, idx_surf: int, concat_dup_mesh: bool = False): + """Returns the current set mesh coordinates from AVL as a numpy array. + Note this is intended for + + Args: + idx_surf (int): the surface to get the mesh for + concat_dup_mesh (bool): concatenates and returns the meshes for idx_surf and idx_surf + 1, for use with duplicated surfaces + """ + + # Check if surface is using mesh geometry + if not self.avl.SURF_MESH_L.LSURFMSH[idx_surf]: + raise RuntimeError(f"Surface {idx_surf} does not have a mesh assigned!") + + # Get the mesh size + nx = self.avl.SURF_GEOM_I.NVC[idx_surf] + 1 + ny = self.avl.SURF_GEOM_I.NVS[idx_surf] + 1 + + + # Get the mesh + mesh = self.get_avl_fort_arr("SURF_MESH_R","MSHBLK",slicer=(slice(self.mesh_idx_first[idx_surf],self.mesh_idx_first[idx_surf]+nx*ny),slice(0,3))) + mesh = copy.deepcopy(mesh.reshape((ny,nx,3)).transpose((1,0,2))) + # mesh = mesh.transpose((1,0,2)) + + # Check if duplicated + imags = self.get_avl_fort_arr("SURF_I", "IMAGS") + if imags[idx_surf] < 0: + mesh[:,:,1] = -mesh[:,:,1] + self.y_offsets[idx_surf-1] + + # Concatenate with duplicate + if concat_dup_mesh: + if imags[idx_surf] < 0: + raise RuntimeError(f"Concatenating a duplicated surface, {idx_surf}, with the next surface!") + elif imags[idx_surf+1] > 0: + raise RuntimeError(f"Concatenating with a non duplicated surface, {idx_surf+1}!") + # Get the next mesh + mesh_dup = self.get_avl_fort_arr("SURF_MESH_R","MSHBLK",slicer=(slice(self.mesh_idx_first[idx_surf+1],self.mesh_idx_first[idx_surf+1]+nx*ny),slice(0,3))) + mesh_dup = mesh_dup.reshape((ny,nx,3)).transpose((1,0,2)) + mesh_dup[:,:,1] = -mesh_dup[:,:,1] + self.y_offsets[idx_surf-1] + + # Concatenate them + mesh = np.hstack([mesh,mesh_dup]) + + return mesh + + def set_section_naca(self, isec: int, isurf: int, nasec: int, naca: str, xfminmax: np.ndarray): """Sets the airfoil oml points for the specified surface and section. Computes camber lines, thickness, and oml shape from NACA 4-digit specification. @@ -1286,23 +1443,28 @@ def post_check_input(self, inputDict: dict): # check number of sections, controls, and dvs if len(inputDict["surfaces"]) > 0: surf_names = list(inputDict["surfaces"].keys()) + idx_surf = 0 for i in range(len(inputDict["surfaces"])): surf_dict = inputDict["surfaces"][surf_names[i]] - if self.avl.SURF_GEOM_I.NSEC[i] != surf_dict["num_sections"]: + if self.avl.SURF_GEOM_I.NSEC[idx_surf] != surf_dict["num_sections"]: raise RuntimeError( - f"Mismatch: NSEC[i] = {self.avl.SURF_GEOM_I.NSEC[i]}, Dictionary: {surf_dict['num_sections']}" + f"Mismatch: NSEC[i] = {self.avl.SURF_GEOM_I.NSEC[idx_surf]}, Dictionary: {surf_dict['num_sections']}" ) # Check controls and design variables per section for j in range(surf_dict["num_sections"]): - if self.avl.SURF_GEOM_I.NSCON[i, j] != surf_dict["num_controls"][j]: + if self.avl.SURF_GEOM_I.NSCON[j, idx_surf] != surf_dict["num_controls"][j]: raise RuntimeError( - f"Mismatch: NSCON[i,j] = {self.avl.SURF_GEOM_I.NSCON[i, j]}, Dictionary: {surf_dict['num_controls'][j]}" + f"Mismatch: NSCON[i,j] = {self.avl.SURF_GEOM_I.NSCON[j, idx_surf]}, Dictionary: {surf_dict['num_controls'][j]}" ) - if self.avl.SURF_GEOM_I.NSDES[i, j] != surf_dict["num_design_vars"][j]: + if self.avl.SURF_GEOM_I.NSDES[j, idx_surf] != surf_dict["num_design_vars"][j]: raise RuntimeError( - f"Mismatch: NSDES[i,j] = {self.avl.SURF_GEOM_I.NSDES[i, j]}, Dictionary: {surf_dict['num_design_vars'][j]}" + f"Mismatch: NSDES[i,j] = {self.avl.SURF_GEOM_I.NSDES[j, idx_surf]}, Dictionary: {surf_dict['num_design_vars'][j]}" ) + + idx_surf += 1 + if "yduplicate" in surf_dict: + idx_surf += 1 # Check the global control and design var count if "dname" in inputDict.keys(): @@ -1333,7 +1495,7 @@ def execute_run(self, tol: float = 0.00002): """Run the analysis (equivalent to the AVL command `x` in the OPER menu) Args: - tol: the tolerace of the Newton solver used for timing the aircraft + tol: the tolerace of the Newton solver used for triming the aircraft """ self.set_avl_fort_arr("CASE_R", "EXEC_TOL", tol) self.avl.oper() @@ -3125,7 +3287,7 @@ def _str_list_to_fort_char_array(self, strList, num_max_char): def __fort_char_array_to_str(self, fort_string: str) -> str: # TODO: need a more general solution for |S type - # SB: This should fix it but keep things commented out in case + # SAB: This should fix it but keep things commented out in case if fort_string.dtype == np.dtype("|S0"): # there are no characters in the sting to add @@ -3329,6 +3491,35 @@ def set_geom_ad_seeds(self, geom_seeds: Dict[str, float], mode: str = "AD", scal # print(blk, var, val, slicer) self.set_avl_fort_arr(blk, var, val, slicer=slicer) + def get_mesh_ad_seeds(self) -> Dict[str, Dict[str, float]]: + mesh_seeds = {} + for surf_key in self.unique_surface_names: + mesh_seeds[surf_key] = {} + for mesh_key in self.surf_mesh_to_fort_var[surf_key]: + blk, var, slicer = self.surf_mesh_to_fort_var[surf_key][mesh_key] + + blk += self.ad_suffix + var += self.ad_suffix + + mesh_seeds[surf_key][mesh_key] = copy.deepcopy(self.get_avl_fort_arr(blk, var, slicer=slicer)) + + return mesh_seeds + + def set_mesh_ad_seeds(self, mesh_seeds: Dict[str, float], mode: str = "AD", scale=1.0) -> None: + for surf_key in mesh_seeds: + for mesh_key in mesh_seeds[surf_key]: + blk, var, slicer = self.surf_mesh_to_fort_var[surf_key][mesh_key] + + if mode == "AD": + blk += self.ad_suffix + var += self.ad_suffix + val = mesh_seeds[surf_key][mesh_key] * scale + elif mode == "FD": + val = self.get_avl_fort_arr(blk, var, slicer=slicer) + val += mesh_seeds[surf_key][mesh_key] * scale + # print(blk, var, val, slicer) + self.set_avl_fort_arr(blk, var, val, slicer=slicer) + # --- state ad seeds --- def get_gamma_ad_seeds(self) -> np.ndarray: slicer = (slice(0, self.get_mesh_size()),) @@ -3567,6 +3758,15 @@ def clear_ad_seeds_fast(self): num_airfoil_pts_max = self.get_avl_fort_arr("SURF_GEOM_R", "XLASEC").shape[-1] num_airfoil_pts = np.max(self.get_avl_fort_arr("SURF_GEOM_I", "NASEC")) + mesh_size_max = 4*num_vor_max + mesh_surf = np.trim_zeros(self.get_avl_fort_arr("SURF_MESH_L", "LSURFMSH")) + mesh_size = 0 + for i, is_mesh in enumerate(mesh_surf): + if is_mesh == 1: + nvc = self.get_avl_fort_arr("SURF_GEOM_I", "NVC", slicer=i) + nvs = self.get_avl_fort_arr("SURF_GEOM_I", "NVS", slicer=i) + mesh_size += (nvc+1)*(nvs+1) + # import psutil # process = psutil.Process() # mem_before = process.memory_info().rss @@ -3598,6 +3798,9 @@ def clear_ad_seeds_fast(self): if dim_size == num_airfoil_pts_max: dim_size = num_airfoil_pts + if dim_size == mesh_size_max: + dim_size = mesh_size + slices.append(slice(0, dim_size)) slicer = tuple(slices) val[slicer] = 0.0 @@ -3626,6 +3829,7 @@ def _execute_jac_vec_prod_fwd( self, con_seeds: Optional[Dict[str, float]] = None, geom_seeds: Optional[Dict[str, Dict[str, any]]] = None, + mesh_seeds: Optional[Dict[str, Dict[str, np.ndarray]]] = None, param_seeds: Optional[Dict[str, float]] = None, ref_seeds: Optional[Dict[str, float]] = None, gamma_seeds: Optional[np.ndarray] = None, @@ -3639,6 +3843,7 @@ def _execute_jac_vec_prod_fwd( Args: con_seeds: Case constraint AD seeds geom_seeds: Geometric AD seeds in the same format as the geometric data + mesh_seeds: Mesh geometry AD seeds in the same format as the mesh data param_seeds: Case parameter AD seeds ref_seeds: Reference condition AD seeds gamma_seeds: Circulation AD seeds @@ -3669,6 +3874,9 @@ def _execute_jac_vec_prod_fwd( if geom_seeds is None: geom_seeds = {} + if mesh_seeds is None: + mesh_seeds = {} + if gamma_seeds is None: gamma_seeds = np.zeros(mesh_size) @@ -3693,6 +3901,7 @@ def _execute_jac_vec_prod_fwd( # self.clear_ad_seeds() self.set_variable_ad_seeds(con_seeds) self.set_geom_ad_seeds(geom_seeds) + self.set_mesh_ad_seeds(mesh_seeds) self.set_gamma_ad_seeds(gamma_seeds) self.set_gamma_d_ad_seeds(gamma_d_seeds) self.set_gamma_u_ad_seeds(gamma_u_seeds) @@ -3715,6 +3924,7 @@ def _execute_jac_vec_prod_fwd( self.set_variable_ad_seeds(con_seeds, scale=0.0) self.set_geom_ad_seeds(geom_seeds, scale=0.0) + self.set_mesh_ad_seeds(mesh_seeds, scale=0.0) self.set_gamma_ad_seeds(gamma_seeds, scale=0.0) self.set_gamma_d_ad_seeds(gamma_d_seeds, scale=0.0) self.set_gamma_u_ad_seeds(gamma_u_seeds, scale=0.0) @@ -3727,6 +3937,7 @@ def _execute_jac_vec_prod_fwd( if mode == "FD": self.set_variable_ad_seeds(con_seeds, mode="FD", scale=step) self.set_geom_ad_seeds(geom_seeds, mode="FD", scale=step) + self.set_mesh_ad_seeds(mesh_seeds, mode="FD", scale=step) self.set_gamma_ad_seeds(gamma_seeds, mode="FD", scale=step) self.set_gamma_d_ad_seeds(gamma_d_seeds, mode="FD", scale=step) self.set_gamma_u_ad_seeds(gamma_u_seeds, mode="FD", scale=step) @@ -3750,6 +3961,7 @@ def _execute_jac_vec_prod_fwd( self.set_variable_ad_seeds(con_seeds, mode="FD", scale=-1 * step) self.set_geom_ad_seeds(geom_seeds, mode="FD", scale=-1 * step) + self.set_mesh_ad_seeds(mesh_seeds, mode="FD", scale=-1 * step) self.set_gamma_ad_seeds(gamma_seeds, mode="FD", scale=-1 * step) self.set_gamma_d_ad_seeds(gamma_d_seeds, mode="FD", scale=-1 * step) self.set_gamma_u_ad_seeds(gamma_u_seeds, mode="FD", scale=-1 * step) @@ -3910,6 +4122,7 @@ def _execute_jac_vec_prod_rev( # extract derivatives seeds and set the output dict of functions con_seeds = self.get_variable_ad_seeds() geom_seeds = self.get_geom_ad_seeds() + mesh_seeds = self.get_mesh_ad_seeds() gamma_seeds = self.get_gamma_ad_seeds() gamma_d_seeds = self.get_gamma_d_ad_seeds() gamma_u_seeds = self.get_gamma_u_ad_seeds() @@ -3933,7 +4146,7 @@ def _execute_jac_vec_prod_rev( if print_timings: print(f" Total Time: {time.time() - time_start}") - return con_seeds, geom_seeds, gamma_seeds, gamma_d_seeds, gamma_u_seeds, param_seeds, ref_seeds + return con_seeds, geom_seeds, mesh_seeds, gamma_seeds, gamma_d_seeds, gamma_u_seeds, param_seeds, ref_seeds def execute_run_sensitivities( self, @@ -3967,7 +4180,7 @@ def execute_run_sensitivities( # TODO: remove seeds if it doesn't effect accuracy # self.clear_ad_seeds() time_last = time.time() - _, _, pfpU, _, _, _, _ = self._execute_jac_vec_prod_rev(func_seeds={func: 1.0}) + _, _, _, pfpU, _, _, _, _ = self._execute_jac_vec_prod_rev(func_seeds={func: 1.0}) if print_timings: print(f"Time to get RHS: {time.time() - time_last}") time_last = time.time() @@ -3984,7 +4197,7 @@ def execute_run_sensitivities( # get the resulting adjoint vector (dfunc/dRes) from fortran dfdR = self.get_residual_ad_seeds() # self.clear_ad_seeds() - con_seeds, geom_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( + con_seeds, geom_seeds, mesh_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( func_seeds={func: 1.0}, res_seeds=dfdR ) if print_timings: @@ -3992,7 +4205,14 @@ def execute_run_sensitivities( time_last = time.time() sens[func].update(con_seeds) - sens[func].update(geom_seeds) + # I don't know if it's worth combining geom_seeds and mesh_seeds into one just to make this one part less nasty + for key in geom_seeds: + # if func == 'e': + # import pdb + # pdb.set_trace() + sens[func][key] = geom_seeds[key] | mesh_seeds[key] + # sens[func].update(geom_seeds) + # sens[func].update(mesh_seeds) sens[func].update(param_seeds) sens[func].update(ref_seeds) @@ -4007,7 +4227,7 @@ def execute_run_sensitivities( # get the RHS of the adjoint equation (pFpU) # TODO: remove seeds if it doesn't effect accuracy - _, _, pfpU, pf_pU_d, _, _, _ = self._execute_jac_vec_prod_rev(consurf_derivs_seeds={func_key: 1.0}) + _, _, _, pfpU, pf_pU_d, _, _, _ = self._execute_jac_vec_prod_rev(consurf_derivs_seeds={func_key: 1.0}) if print_timings: print(f"Time to get RHS: {time.time() - time_last}") time_last = time.time() @@ -4027,7 +4247,7 @@ def execute_run_sensitivities( dfdR = self.get_residual_ad_seeds() dfdR_d = self.get_residual_d_ad_seeds() # self.clear_ad_seeds() - con_seeds, geom_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( + con_seeds, geom_seeds, mesh_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( consurf_derivs_seeds={func_key: 1.0}, res_seeds=dfdR, res_d_seeds=dfdR_d ) if print_timings: @@ -4035,7 +4255,10 @@ def execute_run_sensitivities( time_last = time.time() sens[func_key].update(con_seeds) - sens[func_key].update(geom_seeds) + for key in geom_seeds: + sens[func_key][key] = geom_seeds[key] | mesh_seeds[key] + # sens[func_key].update(geom_seeds) + # sens[func_key].update(mesh_seeds) sens[func_key].update(param_seeds) sens[func_key].update(ref_seeds) @@ -4050,7 +4273,7 @@ def execute_run_sensitivities( # get the RHS of the adjoint equation (pFpU) # TODO: remove seeds if it doesn't effect accuracy - _, _, pfpU, _, pf_pU_u, _, _ = self._execute_jac_vec_prod_rev(stab_derivs_seeds={func_key: 1.0}) + _, _, _, pfpU, _, pf_pU_u, _, _ = self._execute_jac_vec_prod_rev(stab_derivs_seeds={func_key: 1.0}) if print_timings: print(f"Time to get RHS: {time.time() - time_last}") time_last = time.time() @@ -4070,7 +4293,7 @@ def execute_run_sensitivities( dfdR = self.get_residual_ad_seeds() dfdR_u = self.get_residual_u_ad_seeds() # self.clear_ad_seeds() - con_seeds, geom_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( + con_seeds, geom_seeds, mesh_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( stab_derivs_seeds={func_key: 1.0}, res_seeds=dfdR, res_u_seeds=dfdR_u ) @@ -4079,7 +4302,10 @@ def execute_run_sensitivities( time_last = time.time() sens[func_key].update(con_seeds) - sens[func_key].update(geom_seeds) + for key in geom_seeds: + sens[func_key][key] = geom_seeds[key] | mesh_seeds[key] + # sens[func_key].update(geom_seeds) + # sens[func_key].update(mesh_seeds) sens[func_key].update(param_seeds) sens[func_key].update(ref_seeds) # sd_deriv_seeds[func_key] = 0.0 @@ -4095,7 +4321,7 @@ def execute_run_sensitivities( # get the RHS of the adjoint equation (pFpU) # TODO: remove seeds if it doesn't effect accuracy - _, _, pfpU, _, pf_pU_u, _, _ = self._execute_jac_vec_prod_rev(body_axis_derivs_seeds={func_key: 1.0}) + _, _, _, pfpU, _, pf_pU_u, _, _ = self._execute_jac_vec_prod_rev(body_axis_derivs_seeds={func_key: 1.0}) if print_timings: print(f"Time to get RHS: {time.time() - time_last}") time_last = time.time() @@ -4115,7 +4341,7 @@ def execute_run_sensitivities( dfdR = self.get_residual_ad_seeds() dfdR_u = self.get_residual_u_ad_seeds() # self.clear_ad_seeds() - con_seeds, geom_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( + con_seeds, geom_seeds, mesh_seeds, _, _, _, param_seeds, ref_seeds = self._execute_jac_vec_prod_rev( body_axis_derivs_seeds={func_key: 1.0}, res_seeds=dfdR, res_u_seeds=dfdR_u ) @@ -4124,7 +4350,10 @@ def execute_run_sensitivities( time_last = time.time() sens[func_key].update(con_seeds) - sens[func_key].update(geom_seeds) + for key in geom_seeds: + sens[func_key][key] = geom_seeds[key] | mesh_seeds[key] + # sens[func_key].update(geom_seeds) + # sens[func_key].update(mesh_seeds) sens[func_key].update(param_seeds) sens[func_key].update(ref_seeds) @@ -4397,6 +4626,227 @@ def plot_geom(self, axes=None, body_color="magenta"): plt.axis("equal") plt.show() + def add_mesh_plot_3d_avl( + self, + axis, + color: str = "black", + mesh_style="--", + mesh_linewidth=0.3, + show_mesh: bool = False, + show_control_points: bool = False + ): + """Adds a 3D plot of the aircraft mesh to a 3D axis + Always plots a flattened mesh from the AVL geometry definition or + a flattened version of any directly assigned meshes. + + Args: + axis: axis to add the plot to + color: what color should the mesh be + mesh_style: line style of the interior mesh, e.g. '-' or '--' + mesh_linewidth: width of the interior mesh, 1.0 will match the surface outline + show_mesh: flag to show the interior mesh of the geometry + """ + mesh_size = self.get_mesh_size() + num_control_surfs = self.get_num_control_surfs() + num_strips = self.get_num_strips() + num_surfs = self.get_num_surfaces() + + # get the mesh points for ploting + mesh_slice = (slice(0, mesh_size),) + strip_slice = (slice(0, num_strips),) + surf_slice = (slice(0, num_surfs),) + + rv1 = self.get_avl_fort_arr("VRTX_R", "RV1", slicer=mesh_slice) # Vortex Left points + rv2 = self.get_avl_fort_arr("VRTX_R", "RV2", slicer=mesh_slice) # Vortex Right points + rc = self.get_avl_fort_arr("VRTX_R", "RC", slicer=mesh_slice) # Control Points + rle1 = self.get_avl_fort_arr("STRP_R", "RLE1", slicer=strip_slice) # Strip left end LE point + rle2 = self.get_avl_fort_arr("STRP_R", "RLE2", slicer=strip_slice) # Strip right end LE point + chord1 = self.get_avl_fort_arr("STRP_R", "CHORD1", slicer=strip_slice) # Left strip chord + chord2 = self.get_avl_fort_arr("STRP_R", "CHORD2", slicer=strip_slice) # Right strip chord + jfrst = self.get_avl_fort_arr("SURF_I", "JFRST", slicer=surf_slice) # Index of first strip in surface + + ijfrst = self.get_avl_fort_arr("STRP_I", "IJFRST", slicer=strip_slice) # Index of first element in strip + nvstrp = self.get_avl_fort_arr("STRP_I", "NVSTRP", slicer=strip_slice) # Number of elements in strip + + nj = self.get_avl_fort_arr("SURF_I", "NJ", slicer=surf_slice) # Number of elements along span in surface + imags = self.get_avl_fort_arr("SURF_I", "IMAGS") # Is surface YDUPL one? + + for idx_surf in range(num_surfs): + # get the range of the elements that belong to this surfaces + strip_st = jfrst[idx_surf] - 1 + strip_end = strip_st + nj[idx_surf] + + # inboard and outboard of outline + # get surfaces that have not been duplicated + if imags[idx_surf] > 0: + j1 = strip_st + jn = strip_end - 1 + dj = 1 + else: + # this surface is a duplicate + j1 = strip_end - 1 + jn = strip_st + dj = -1 + + pts = { + "x": [rle1[j1, 0], rle1[j1, 0] + chord1[j1]], + "y": [rle1[j1, 1], rle1[j1, 1]], + "z": [rle1[j1, 2], rle1[j1, 2]], + } + # # chord-wise grid + axis.plot(pts['x'], pts['y'],pts['z'], color=color) + + pts = { + "x": np.array([rle2[jn, 0], rle2[jn, 0] + chord2[jn]]), + "y": np.array([rle2[jn, 1], rle2[jn, 1]]), + "z": np.array([rle2[jn, 2], rle2[jn, 2]]), + } + + # # chord-wise grid + axis.plot(pts['x'], pts['y'],pts['z'], color=color) + + # # --- outline of surface --- + # front + pts = { + "x": np.append(rle1[j1:jn:dj, 0], rle2[jn, 0]), + "y": np.append(rle1[j1:jn:dj, 1], rle2[jn, 1]), + "z": np.append(rle1[j1:jn:dj, 2], rle2[jn, 2]), + } + axis.plot(pts['x'], pts['y'],pts['z'],"-", color=color) + + # aft + + pts = { + "x": np.append(rle1[j1:jn:dj, 0] + chord1[j1:jn:dj], rle2[jn, 0] + chord2[jn]), + "y": np.append(rle1[j1:jn:dj, 1], rle2[jn, 1]), + "z": np.append(rle1[j1:jn:dj, 2], rle2[jn, 2]), + } + axis.plot(pts['x'], pts['y'],pts['z'],"-", color=color) + + if show_mesh: + for idx_strip in range(strip_st, strip_end): + if ((imags[idx_surf] > 0) and (idx_strip != strip_st)) or ( + (imags[idx_surf] < 0) and (idx_strip != strip_end) + ): + pts = { + "x": [rle1[idx_strip, 0], rle1[idx_strip, 0] + chord1[idx_strip]], + "y": [rle1[idx_strip, 1], rle1[idx_strip, 1]], + "z": [rle1[idx_strip, 2], rle1[idx_strip, 2]], + } + + # # chord-wise grid + axis.plot(pts['x'], pts['y'], pts['z'], mesh_style, color=color, alpha=1.0, linewidth=mesh_linewidth) + + vor_st = ijfrst[idx_strip] - 1 + vor_end = vor_st + nvstrp[idx_strip] + + # spanwise grid + for idx_vor in range(vor_st, vor_end): + pts = { + "x": [rv1[idx_vor, 0], rv2[idx_vor, 0]], + "y": [rv1[idx_vor, 1], rv2[idx_vor, 1]], + "z": [rv1[idx_vor, 2], rv2[idx_vor, 2]], + } + axis.plot(pts['x'], pts['y'],pts['z'], mesh_style, color=color, alpha=1.0, linewidth=mesh_linewidth) + + if show_control_points: + for idx_strip in range(strip_st, strip_end): + vor_st = ijfrst[idx_strip] - 1 + vor_end = vor_st + nvstrp[idx_strip] + + # spanwise grid + for idx_vor in range(vor_st, vor_end): + pts = { + "x": [rc[idx_vor, 0]], + "y": [rc[idx_vor, 1]], + "z": [rc[idx_vor, 2]], + } + axis.scatter(pts['x'], pts['y'], pts['z'],color='r', alpha=1.0, linewidth=0.1) + + def add_mesh_plot_3d_direct( + self, + axis, + color: str = "black", + mesh_style="--", + mesh_linewidth=0.3, + show_mesh: bool = False, + ): + """Plots the mesh assigned to SURF_MESH AVL common block data on a 3D axis. + + Args: + axis: axis to add the plot to + color: what color should the mesh be + mesh_style: line style of the interior mesh, e.g. '-' or '--' + mesh_linewidth: width of the interior mesh, 1.0 will match the surface outline + show_mesh: flag to show the interior mesh of the geometry + """ + num_surfs = self.get_num_surfaces() + + for idx_surf in range(num_surfs): + # get the mesh block data + mesh = self.get_mesh(idx_surf) + mesh_x = mesh[:, :, 0] + mesh_y = mesh[:, :, 1] + mesh_z = mesh[:, :, 2] + + # Plot mesh outline + axis.plot(mesh_x[0, :], mesh_y[0, :], mesh_z[0, :], "-", color=color, lw=1) + axis.plot(mesh_x[-1, :], mesh_y[-1, :],mesh_z[-1, :], "-", color=color, lw=1) + axis.plot(mesh_x[:, 0], mesh_y[:, 0],mesh_z[:, 0], "-", color=color, lw=1) + axis.plot(mesh_x[:, -1], mesh_y[:, -1],mesh_z[:, -1], "-", color=color, lw=1) + + if show_mesh: + for i in range(mesh_x.shape[0]): + axis.plot(mesh_x[i, :], mesh_y[i, :], mesh_z[i, :], mesh_style, color=color, lw=mesh_linewidth, alpha=1.0) + for j in range(mesh_x.shape[1]): + axis.plot(mesh_x[:, j], mesh_y[:, j],mesh_z[:, j], mesh_style, color=color, lw=mesh_linewidth, alpha=1.0) + + + def plot_geom_3d(self, axes=None, plot_avl_mesh = True, plot_direct_mesh = False): + """Generates a plot of the VLM mesh on a 3d axis. + By default the flat version of the mesh that satisfies AVL's VLM assumptions is plotted. + This is either the mesh that comes as a result of AVL's standard geometry specification system + or the custom user assigned mesh after it has undergone the transformation needed to flatten it to + satify the VLM assumptions. There is also an option to overlay the directly assigned mesh. This will + plot user assigned mesh as is with no modifications. + + Args: + axes: Matplotlib axis object to add the plots too. If none are given, the axes will be generated. + plot_avl_mesh: If True the AVL flattenned mesh is plotted on the axis + plot_direct_mesh: If True the user assigned mesh will be plotted as is + """ + + if axes == None: + import matplotlib.pyplot as plt + + ax1 = plt.subplot(projection='3d') + ax1.set_ylabel("X", rotation=0) + ax1.set_aspect("equal") + ax1._axis3don = False + plt.subplots_adjust(left=0.025, right=0.925, top=0.925, bottom=0.025) + else: + ax1 = axes + + if plot_avl_mesh: + self.add_mesh_plot_3d_avl(ax1, + color = "red", + mesh_style="--", + mesh_linewidth=1.0, + show_mesh= True, + show_control_points = False) + + if plot_direct_mesh: + self.add_mesh_plot_3d_direct(ax1, + color = "black", + mesh_style="--", + mesh_linewidth=0.3, + show_mesh= True) + + if axes is None: + # assume that if we don't provide axes that we want to see the plot + plt.axis("equal") + plt.show() + def get_cp_data(self) -> Tuple[List[np.ndarray], List[np.ndarray]]: """Gets the current surface mesh and cp distribution diff --git a/optvl/utils/check_surface_dict.py b/optvl/utils/check_surface_dict.py index 6746941..89cfb0b 100755 --- a/optvl/utils/check_surface_dict.py +++ b/optvl/utils/check_surface_dict.py @@ -12,11 +12,32 @@ # ============================================================================= import numpy as np - # ============================================================================= # Extension modules # ============================================================================= +def scalar_to_strip_vec(given_val,num_secs): + """Converts a scalar into a numpy array of length matching + the number of sections. If a numpy array of length not matching + the number of sections is input then an exception is thrown. + + Args: + given_val: Input value + num_secs: Number of sections + + Returns: + np.ndarray of length number of sections + """ + # check if we input a scalar + if not isinstance(given_val, np.ndarray): + return given_val*np.ones(num_secs) + elif isinstance(given_val,np.ndarray): + if given_val.shape[0] != num_secs: + raise ValueError("The length of a given surface/body input must either be a scalar or match the number of sections!") + return given_val + else: + return given_val + def pre_check_input_dict(input_dict: dict): """ @@ -106,13 +127,18 @@ def pre_check_input_dict(input_dict: dict): "nspans", # number of spanwise elements vector, overriden by nspans "sspaces", # spanwise spacing vector (for each section), overriden by sspace "use surface spacing", # surface spacing set under the surface heeading (known as LSURFSPACING in AVL) + # Geometery: Mesh + "mesh", + "flatten mesh", # Control Surfaces - # "dname" # IMPLEMENT THIS + "control_assignments", "icontd", # control variable index "xhinged", # x/c location of hinge "vhinged", # vector giving hinge axis about which surface rotates "gaind", # control surface gain "refld", # control surface reflection, sign of deflection for duplicated surface + # Design Variables + "design_var_assignments", "idestd", # design variable index "gaing", # desgin variable gain ] @@ -133,6 +159,12 @@ def pre_check_input_dict(input_dict: dict): "sspaces", # spanwise spacing vector (for each section), overriden by sspace "clcdsec", # profile-drag CD(CL) function for each section in this surface "claf", # CL alpha (dCL/da) scaling factor per section + # Geometry + "xles", # leading edge cordinate vector(x component) + "yles", # leading edge cordinate vector(y component) + "zles", # leading edge cordinate vector(z component) + "chords", # chord length vector + "aincs", # incidence angle vector # Geometry: Cross Sections # NACA "naca", @@ -160,6 +192,11 @@ def pre_check_input_dict(input_dict: dict): "refld", # control surface reflection, sign of deflection for duplicated surface ] + design_var_keys =[ + "idestd", + "gaing", # design variable surface gain + ] + dim_2_keys = [ "clcdsec", @@ -183,7 +220,7 @@ def pre_check_input_dict(input_dict: dict): if key in ["Bref", "Sref", "Cref"]: if input_dict[key] < 0.0: raise ValueError(f"Reference value {key} cannot be negative!") - + # Correct incorrect symmetry plane defs with warning if key in ["iysym", "izsym"]: if input_dict[key] not in [-1,0,1]: @@ -193,7 +230,7 @@ def pre_check_input_dict(input_dict: dict): stacklevel=2, ) input_dict[key] = np.sign(input_dict[key]) - + # Check for keys not implemented if key not in keys_implemented_general: warnings.warn( @@ -201,31 +238,97 @@ def pre_check_input_dict(input_dict: dict): category=RuntimeWarning, stacklevel=2, ) - total_global_control = 0 - total_global_design_var = 0 + if "surfaces" in input_dict.keys(): if len(input_dict["surfaces"]) > 0: for surface in input_dict["surfaces"].keys(): + # Check if we are directly providing a mesh and set the strips as "sections" so that the maps setup correctly + if "mesh" in input_dict["surfaces"][surface].keys(): + # First check if the mesh is a valid numpy array shape + if len(input_dict["surfaces"][surface]["mesh"].shape) != 3: + raise ValueError("The provided mesh must be a numpy array of size (nx,ny,3)") + # If we are using a mesh then set number of sections equal to number of strip for the purposes of intialization + input_dict["surfaces"][surface]["num_sections"] = input_dict["surfaces"][surface]["mesh"].shape[1] + # Verify at least two section if input_dict["surfaces"][surface]["num_sections"] < 2: raise RuntimeError("Must have at least two sections per surface!") - # if no controls are specified then fill it in with 0s - if "num_controls" not in input_dict["surfaces"][surface].keys(): + # Read and process the controls dictionary + if "control_assignments" in input_dict["surfaces"][surface] and "num_controls" not in input_dict["surfaces"][surface]: + num_controls_per_sec = np.zeros(input_dict["surfaces"][surface]["num_sections"],dtype=np.int32) + + for control in input_dict["surfaces"][surface]["control_assignments"]: + if control not in input_dict["dname"]: + raise ValueError(f"Control {control}, in surface {surface} not defined in dname!") + + # built the control data lists if needed + if "icontd" not in input_dict["surfaces"][surface]: + input_dict["surfaces"][surface]["icontd"] = [[] for _ in range(input_dict["surfaces"][surface]["num_sections"])] + if "xhinged" not in input_dict["surfaces"][surface]: + input_dict["surfaces"][surface]["xhinged"] = [[] for _ in range(input_dict["surfaces"][surface]["num_sections"])] + if "vhinged" not in input_dict["surfaces"][surface]: + input_dict["surfaces"][surface]["vhinged"] = [[] for _ in range(input_dict["surfaces"][surface]["num_sections"])] + if "gaind" not in input_dict["surfaces"][surface]: + input_dict["surfaces"][surface]["gaind"] = [[] for _ in range(input_dict["surfaces"][surface]["num_sections"])] + if "refld" not in input_dict["surfaces"][surface]: + input_dict["surfaces"][surface]["refld"] = [[] for _ in range(input_dict["surfaces"][surface]["num_sections"])] + + # Add one to the number of controls defined for each section + sec_assign = input_dict["surfaces"][surface]["control_assignments"][control]["assignment"] + num_controls_per_sec[sec_assign] += 1 + # assign data to sections + for idx_sec in input_dict["surfaces"][surface]["control_assignments"][control]["assignment"]: + input_dict["surfaces"][surface]["icontd"][idx_sec].append(input_dict["dname"].index(control)) # Add control index to icontd for each section + input_dict["surfaces"][surface]["xhinged"][idx_sec].append(input_dict["surfaces"][surface]["control_assignments"][control]["xhinged"]) # Add hinge line position + input_dict["surfaces"][surface]["vhinged"][idx_sec].append(input_dict["surfaces"][surface]["control_assignments"][control]["vhinged"]) # Add hinge vector position + input_dict["surfaces"][surface]["gaind"][idx_sec].append(input_dict["surfaces"][surface]["control_assignments"][control]["gaind"]) # Add gain information + input_dict["surfaces"][surface]["refld"][idx_sec].append(input_dict["surfaces"][surface]["control_assignments"][control]["refld"]) # Add reflection information + + # set the control numbers per section + input_dict["surfaces"][surface]["num_controls"] = num_controls_per_sec + elif "num_controls" not in input_dict["surfaces"][surface]: + # Otherwise if we are not manually specifying controls then zero out the num_controls array input_dict["surfaces"][surface]["num_controls"] = np.zeros(input_dict["surfaces"][surface]["num_sections"],dtype=np.int32) - # if no dvs are specified then fill it in with 0s - if "num_design_vars" not in input_dict["surfaces"][surface].keys(): + # Read and process the design variables dictionary + if "design_var_assignments" in input_dict["surfaces"][surface] and "num_design_vars" not in input_dict["surfaces"][surface]: + num_design_vars_per_sec = np.zeros(input_dict["surfaces"][surface]["num_sections"],dtype=np.int32) + + for design_var in input_dict["surfaces"][surface]["design_var_assignments"]: + if design_var not in input_dict["gname"]: + raise ValueError(f"Design Variable {design_var}, in surface {surface} not defined in gname!") + + # built the control data lists if needed + if "idestd" not in input_dict["surfaces"][surface]: + input_dict["surfaces"][surface]["idestd"] = [[] for _ in range(input_dict["surfaces"][surface]["num_sections"])] + if "gaing" not in input_dict["surfaces"][surface]: + input_dict["surfaces"][surface]["gaing"] = [[] for _ in range(input_dict["surfaces"][surface]["num_sections"])] + + + # Add one to the number of controls defined for each section + sec_assign = input_dict["surfaces"][surface]["design_var_assignments"][design_var]["assignment"] + num_design_vars_per_sec[sec_assign] += 1 + # assign data to sections + for idx_sec in input_dict["surfaces"][surface]["design_var_assignments"][design_var]["assignment"]: + input_dict["surfaces"][surface]["idestd"][idx_sec].append(input_dict["gname"].index(design_var)) # Add design var index to idestd for each section + input_dict["surfaces"][surface]["gaing"][idx_sec].append(input_dict["surfaces"][surface]["design_var_assignments"][design_var]["gaing"]) # Add gain information + + # set the control numbers per section + input_dict["surfaces"][surface]["num_design_vars"] = num_design_vars_per_sec + elif "num_design_vars" not in input_dict["surfaces"][surface]: + # Otherwise if we are not manually specifying controls then zero out the num_design_vars array input_dict["surfaces"][surface]["num_design_vars"] = np.zeros(input_dict["surfaces"][surface]["num_sections"],dtype=np.int32) - + + #Checks to see that at most only one of the options in af_load_ops or one of the options in manual_af_override is selected if len(airfoil_spec_keys & input_dict["surfaces"][surface].keys()) > 1: raise RuntimeError( "More than one airfoil section specification detected in input dictionary!\n" "Select only a single approach for specifying airfoil sections!") - + # Process all keys for key in input_dict["surfaces"][surface].keys(): # Check to verify if redundant y-symmetry specification are not made @@ -235,22 +338,35 @@ def pre_check_input_dict(input_dict: dict): f"ERROR: Redundant y-symmetry specifications in surface {surface} \nIYSYM /= 0 \nYDUPLICATE 0.0. \nCan use one or the other, but not both!" ) - # Check the surface input size is a 2D array with second dim equal to num_sections + # Verify that keys that need items specified for every strip/section have value specified for all strip/section or have a scalar/single vector that can be duplicated if key in multi_section_keys: - if (key in dim_2_keys) and (input_dict["surfaces"][surface][key].ndim != 2): - raise ValueError( - f"Key {key} is of dimension {input_dict['surfaces'][surface][key].ndim}, expected 2!" - ) - if (key not in dim_2_keys) and input_dict["surfaces"][surface][key].ndim != 1: - raise ValueError( - f"Key {key} is of dimension {input_dict['surfaces'][surface][key].ndim}, expected 1!" - ) - if ( - input_dict["surfaces"][surface][key].shape[0] - != input_dict["surfaces"][surface]["num_sections"] - ): - raise ValueError(f"Key {key} does not have entries corresponding to each section!s") + if (key in dim_2_keys): + if not (isinstance(input_dict["surfaces"][surface][key],np.ndarray)): + raise ValueError(f"Input for {key} must be a single dim 1 numpy array or a dim 2 numpy array with each vector along axis 0 corresponding to strip/section") + + # If the user provides a single dim 1 vector stack it num_sections times + if (input_dict["surfaces"][surface][key].ndim == 1): + input_dict["surfaces"][surface][key] = np.tile(input_dict["surfaces"][surface][key],(input_dict["surfaces"][surface]["num_sections"],1)) + # Otherwise make sure we have entries for each seciton + elif (input_dict["surfaces"][surface][key].ndim == 2): + if (input_dict["surfaces"][surface][key].shape[0] != input_dict["surfaces"][surface]["num_sections"]): + raise ValueError( + f"Key {key} only has {input_dict['surfaces'][surface][key].shape[0]}, expected {input_dict['surfaces'][surface]['num_sections']}!" + ) + else: + raise ValueError( + f"Key {key} is of dimension {input_dict['surfaces'][surface][key].ndim}, expected 1 or 2!" + ) + else: + + # If the user provides a scalar or string expand it out for all sections + if isinstance(input_dict["surfaces"][surface][key],(int,float,np.int32,np.float64,str)): + input_dict["surfaces"][surface][key] = np.tile(input_dict["surfaces"][surface][key],(input_dict["surfaces"][surface]["num_sections"])) + elif input_dict["surfaces"][surface][key].ndim > 1: + raise ValueError( + f"Key {key} is of dimension {input_dict['surfaces'][surface][key].ndim}, expected 1!" + ) # Check for keys not implemented if key not in keys_implemented_surface: @@ -267,50 +383,27 @@ def pre_check_input_dict(input_dict: dict): for j in range(input_dict["surfaces"][surface]["num_sections"]): for _ in range(input_dict["surfaces"][surface]["num_controls"][j]): if ( - input_dict["surfaces"][surface][key][j].shape[0] + len(input_dict["surfaces"][surface][key][j]) != input_dict["surfaces"][surface]["num_controls"][j] ): raise ValueError( f"Key {key} does not have entries corresponding to each control for this section!" ) - # Accumulate icont max - if "icontd" in input_dict["surfaces"][surface].keys(): - arr = input_dict["surfaces"][surface]["icontd"] - vals = [a.max() + 1 for a in arr if a.size > 0] - total_global_control = max(vals) if vals else None - # total_global_control = np.max(input_dict["surfaces"][surface]["icontd"])+1 # Check if dvs defined correctly - if key in control_keys: + if key in design_var_keys: for j in range(input_dict["surfaces"][surface]["num_sections"]): for _ in range(input_dict["surfaces"][surface]["num_design_vars"][j]): if ( - input_dict["surfaces"][surface][key][j].shape[0] + len(input_dict["surfaces"][surface][key][j]) != input_dict["surfaces"][surface]["num_design_vars"][j] ): raise ValueError( f"Key {key} does not have entries corresponding to each design var for this section!" ) - # Accumulate idestd max - if "idestd" in input_dict["surfaces"][surface].keys(): - arr = input_dict["surfaces"][surface]["idestd"] - vals = [a.max() + 1 for a in arr if a.size > 0] - total_global_design_var = max(vals) if vals else None - # total_global_design_var = np.max(input_dict["surfaces"][surface]["idestd"])+1 - - if "icontd" in input_dict["surfaces"][surface].keys(): - if len(input_dict["dname"]) != (total_global_control): - raise ValueError( - "Number of unique control names does not match the number of unique controls defined!" - ) - - if "idestd" in input_dict["surfaces"][surface].keys(): - if len(input_dict["gname"]) != (total_global_design_var): - raise ValueError( - "Number of unique design vars does not match the number of unique controls defined!" - ) + else: # Add dummy entry if surfaces are not defined input_dict["surfaces"] = {} diff --git a/src/ad_src/Makefile_tapenade b/src/ad_src/Makefile_tapenade index 3d1e36f..908cfea 100644 --- a/src/ad_src/Makefile_tapenade +++ b/src/ad_src/Makefile_tapenade @@ -26,7 +26,7 @@ PP_FILES = $(addprefix $(PP_DIR)/,$(notdir $(ALL_RES_FILES))) # you also need to add any new files to `src/build/fileList` # ====================== Full List of Routines ================== fullRoutines = "\ - update_surfaces(XYZSCAL,XYZTRAN,ADDINC,XYZLES,CHORDS,AINCS,XASEC,SASEC,TASEC,CLCDSEC,CLAF)>(ENC,ENV, DXV, CHORDV,CHORD, CHORD1, CHORD2, RLE,RLE1,RLE2, WSTRIP, RV1,RV2,RV,RC,RS,RL, ENSY, ENSZ, ESS, XSREF,YSREF,ZSREF, ENC_D)\ + update_surfaces(XYZSCAL,XYZTRAN,ADDINC,XYZLES,CHORDS,MSHBLK,AINCS,XASEC,SASEC,TASEC,CLCDSEC,CLAF)>(ENC,ENV, DXV, CHORDV,CHORD, CHORD1, CHORD2, RLE,RLE1,RLE2, WSTRIP, RV1,RV2,RV,RC,RS,RL, ENSY, ENSZ, ESS, XSREF,YSREF,ZSREF, ENC_D)\ \ get_res(GAM, GAM_D, GAM_U, CONVAL, PARVAL, YSYM, ZSYM, ENC, ENV, DXV, CHORDV, RV1, RV2, RV, RC, RS, RL, ENC_D, XYZREF)>(RES, RES_D, RES_U, GAM, GAM_D, GAM_U, VINF, WROT, VINF_A, VINF_B, ALFA, BETA, DELCON, RV1,RV2, RV, RC,DXV, XYZREF, WV_GAM, CDREF, MACH, SRC, SRC_U)\ \ diff --git a/src/ad_src/forward_ad_src/amake_d.f b/src/ad_src/forward_ad_src/amake_d.f index 47eb1a6..17bf2b2 100644 --- a/src/ad_src/forward_ad_src/amake_d.f +++ b/src/ad_src/forward_ad_src/amake_d.f @@ -6,15 +6,14 @@ C chord2 wstrip ess ensy ensz xsref ysref zsref C rv1 rv2 rv rc rs dxv chordv enc env enc_d C with respect to varying inputs: xyzscal xyztran addinc xyzles -C chords aincs xasec sasec claf +C chords aincs xasec sasec claf mshblk C RW status of diff variables: xyzscal:in xyztran:in addinc:in C xyzles:in chords:in aincs:in xasec:in sasec:in -C claf:in rle:out chord:out rle1:out chord1:out +C claf:in mshblk:in rle:out chord:out rle1:out chord1:out C rle2:out chord2:out wstrip:out ess:out ensy:out C ensz:out xsref:out ysref:out zsref:out rv1:out C rv2:out rv:out rc:out rs:out dxv:out chordv:out C enc:out env:out enc_d:out -C MAKESURF SUBROUTINE UPDATE_SURFACES_D() use avl_heap_inc use avl_heap_diff_inc @@ -32,6 +31,30 @@ SUBROUTINE UPDATE_SURFACES_D() nstrip = 0 nvor = 0 isurf = 1 + nsurfdupl = 0 + DO ii=1,nsurf + IF (ldupl(ii)) nsurfdupl = nsurfdupl + 1 + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rv1msh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rv2msh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rvmsh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rcmsh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO DO ii1=1,NSTRIP DO ii2=1,3 rle_diff(ii2, ii1) = 0.D0 @@ -120,7 +143,11 @@ SUBROUTINE UPDATE_SURFACES_D() C up the size information as we make each surface DO ii=1,nsurf-nsurfdupl IF (lverbose) WRITE(*, *) 'Updating surface ', isurf - CALL MAKESURF_D(isurf) + IF (lsurfmsh(isurf)) THEN + CALL MAKESURF_MESH_D(isurf) + ELSE + CALL MAKESURF_D(isurf) + END IF IF (ldupl(isurf)) THEN IF (lverbose) WRITE(*, *) ' reduplicating ', isurf CALL SDUPL_D(isurf, ydupl(isurf), 'ydup') @@ -523,6 +550,8 @@ SUBROUTINE MAKESURF_D(isurf) iptloc(nsec(isurf)) = npt C C----- fudge spacing array to make nodes match up exactly with interior sections +C Throws an error in the case where the same node is the closest node +C to two consecutive sections DO isec=2,nsec(isurf)-1 ipt1 = iptloc(isec-1) ipt2 = iptloc(isec) @@ -1299,13 +1328,1300 @@ SUBROUTINE MAKESURF_D(isurf) + ) END +C Differentiation of makesurf_mesh in forward (tangent) mode (with options i4 dr8 r8): +C variations of useful results: rv1msh rv2msh rvmsh rcmsh rle +C chord rle1 chord1 rle2 chord2 wstrip ainc ainc_g +C rv1 rv2 rv rc rs dxv chordv slopev slopec dcontrol +C vhinge +C with respect to varying inputs: xyzscal xyztran addinc aincs +C xasec sasec claf mshblk rv1msh rv2msh rvmsh rcmsh +C rle chord rle1 chord1 rle2 chord2 wstrip ainc +C ainc_g rv1 rv2 rv rc rs dxv chordv slopev slopec +C dcontrol vhinge +C + SUBROUTINE MAKESURF_MESH_D(isurf) + INCLUDE 'AVL.INC' + INCLUDE 'AVL_ad_seeds.inc' +C working variables (AVL original) + INTEGER isurf + INTEGER kcmax + INTEGER ksmax + PARAMETER (kcmax=50, ksmax=500) + REAL chsin, chcos, chsinl, chsinr, chcosl, chcosr, aincl, aincr, + + chordl, chordr, clafl, clafr, slopel, sloper, dxdx, zu_l, + + zl_l, zu_r, zl_r, zl, zr, sum, wtot, astrp + REAL chsin_diff, chcos_diff, chsinl_diff, chsinr_diff, chcosl_diff + + , chcosr_diff, aincl_diff, aincr_diff, chordl_diff, + + chordr_diff, clafl_diff, clafr_diff, slopel_diff, sloper_diff + REAL chsinl_g(ngmax), chcosl_g(ngmax), chsinr_g(ngmax), chcosr_g( + + ngmax), xled(ndmax), xted(ndmax), gainda(ndmax) + REAL chsinl_g_diff(ngmax), chcosl_g_diff(ngmax), chsinr_g_diff( + + ngmax), chcosr_g_diff(ngmax), xled_diff(ndmax), xted_diff( + + ndmax) +C working variables (OptVL additions) + INTEGER isconl(ndmax), isconr(ndmax) + REAL m1, m2, m3, f1, f2, fc, dc1, dc2, dc, a1, a2, a3, xptxind1, + + xptxind2 + REAL m2_diff, m3_diff, dc1_diff, dc2_diff, a1_diff, a2_diff, + + a3_diff + REAL mesh_surf(3, (nvc(isurf)+1)*(nvs(isurf)+1)) + REAL mesh_surf_diff(3, (nvc(isurf)+1)*(nvs(isurf)+1)) +C functions + INTEGER idx_vor, idx_strip, idx_sec, idx_dim, idx_coef, idx_x, + + idx_node, idx_nodel, idx_noder, idx_node_yp1, idx_node_nx + + , idx_node_nx_yp1, idx_y, nx, ny +C +C Get data from common block + INTEGER FLATIDX + INTEGER isec + INTEGER ii + INTEGER ispan + INTEGER iptl + INTEGER iptr + INTRINSIC SQRT + INTRINSIC SIN + INTRINSIC COS + INTEGER n + INTEGER iscon + INTEGER isdes + INTRINSIC ATAN2 + REAL chsin_g + REAL chsin_g_diff + REAL chcos_g + REAL chcos_g_diff + INTEGER icl + INTEGER icr + REAL xhd + REAL xhd_diff + REAL vhx + REAL vhx_diff + REAL vhy + REAL vhy_diff + REAL vhz + REAL vhz_diff + REAL vsq + REAL vsq_diff + INTRINSIC ABS + REAL vmod + REAL vmod_diff + INTEGER nsl + INTEGER nsr + REAL clafc + REAL clafc_diff + REAL dx1 + REAL dx1_diff + REAL dx2 + REAL dx2_diff + REAL dx3 + REAL dx3_diff + REAL dsdx + REAL xpt + REAL xpt_diff + REAL fracle + REAL fracle_diff + REAL fracte + REAL fracte_diff + INTRINSIC MAX + INTRINSIC MIN + REAL zu + INTEGER jj + INTEGER j + REAL y1 + REAL y1_diff + REAL y2 + REAL y2_diff + REAL(kind=avl_real) abs0 + REAL(kind=avl_real) abs0_diff + REAL(kind=avl_real) abs1 + REAL(kind=avl_real) abs1_diff + REAL arg1 + REAL arg1_diff + REAL arg2 + REAL arg2_diff + REAL(kind=8) arg10 + REAL(kind=8) arg10_diff + REAL temp + REAL temp0 + REAL(kind=8) temp1 + REAL(kind=8) temp2 + REAL(kind=avl_real) temp3 +C + nx = nvc(isurf) + 1 +C Check MFRST + ny = nvs(isurf) + 1 +C Get the mesh for this surface from the the common block +C + IF (mfrst(isurf) .EQ. 0) PRINT*, + + '* Provide the index where the mesh begins for surface' + + , isurf +C +C Perform input checks from makesurf (section check removed) + mesh_surf_diff = mshblk_diff(:, mfrst(isurf):mfrst(isurf)+nx*ny-1) + mesh_surf = mshblk(:, mfrst(isurf):mfrst(isurf)+nx*ny-1) +C +C + IF (nvc(isurf) .GT. kcmax) THEN + WRITE(*, *) + + '* makesurf_mesh: Array overflow. Increase KCMAX to', nvc + + (isurf) + nvc(isurf) = kcmax + END IF +C Image flag set to indicate section definition direction +C IMAGS= 1 defines edge 1 located at surface root edge +C IMAGS=-1 defines edge 2 located at surface root edge (reflected surfaces) +C + IF (nvs(isurf) .GT. ksmax) THEN + WRITE(*, *) + + '* makesurf_mesh: Array overflow. Increase KSMAX to', nvs + + (isurf) + nvs(isurf) = ksmax + END IF +C +C Start accumulating the element and strip index references +C Accumulate the first element in surface + imags(isurf) = 1 +C Accumulate the first strip in surface + IF (isurf .EQ. 1) THEN + ifrst(isurf) = 1 + ELSE + ifrst(isurf) = ifrst(isurf-1) + nk(isurf-1)*nj(isurf-1) + END IF +C Set NK from input data (python layer will ensure this is consistent) +C + IF (isurf .EQ. 1) THEN + jfrst(isurf) = 1 + ELSE + jfrst(isurf) = jfrst(isurf-1) + nj(isurf-1) + END IF +C We need to start counting strips now since it is a global count + nk(isurf) = nvc(isurf) +C +C Bypass the entire spanwise node generation routine and go straight to store counters +C skips MAKESURF 94-234 +C Index of first strip in surface +C This is normally used to store the index of each section in AVL +C but since we use strips now each is effectively just a section +C We assign this variable accordingly so as not to break anything else + idx_strip = jfrst(isurf) +C Number of strips/sections in surface +C + IF (isurf .EQ. 1) THEN + icntfrst(isurf) = 1 + ELSE + icntfrst(isurf) = icntfrst(isurf-1) + ncntsec(isurf-1) + END IF +C Store the spanwise index of each strip in each surface + ncntsec(isurf) = nsec(isurf) + DO isec=1,nsec(isurf) + ii = icntfrst(isurf) + (isec-1) + icntsec(ii) = idx_strip + ENDDO +C Apply the scaling and translations to the mesh as a whole +C +C + DO idx_y=1,ny + DO idx_x=1,nx + DO idx_dim=1,3 + idx_node = FLATIDX(idx_x, idx_y, isurf) + mesh_surf_diff(idx_dim, idx_node) = mesh_surf(idx_dim, + + idx_node)*xyzscal_diff(idx_dim, isurf) + xyzscal(idx_dim, + + isurf)*mesh_surf_diff(idx_dim, idx_node) + xyztran_diff( + + idx_dim, isurf) + mesh_surf(idx_dim, idx_node) = xyzscal(idx_dim, isurf)* + + mesh_surf(idx_dim, idx_node) + xyztran(idx_dim, isurf) + ENDDO + ENDDO + ENDDO +C Setup the strips +C Set spanwise elements to 0 +C +C +C +C Check control and design vars + nj(isurf) = 0 +C + IF (ncontrol .GT. ndmax) THEN + WRITE(*, *) '*** Too many control variables. Increase NDMAX to' + + , ncontrol + STOP + ELSE IF (ndesign .GT. ngmax) THEN +C Instead of looping over sections just loop over all strips in the surface +C + WRITE(*, *) '*** Too many design variables. Increase NGMAX to' + + , ndesign + STOP + ELSE + chcosl_g_diff = 0.D0 + chsinr_g_diff = 0.D0 + xted_diff = 0.D0 + xled_diff = 0.D0 + chsinl_g_diff = 0.D0 + chcosr_g_diff = 0.D0 +C +Cispan loop +C Set reference information for the strip +C This code was used in the original to loop over strips in a section. +C We will just reuse the variables here + DO ispan=1,ny-1 +C +C + idx_y = idx_strip - jfrst(isurf) + 1 + iptl = idx_y + iptr = idx_y + 1 +C We need to compute the chord and claf values at the left and right edge of the strip +C This code was used in the original to interpolate over sections. +C We will just reuse here to interpolate over a strip which is trivial but avoids pointless code rewrites. + nj(isurf) = nj(isurf) + 1 +C +C + idx_node = FLATIDX(1, iptl, isurf) + idx_node_nx = FLATIDX(nx, iptl, isurf) + arg1_diff = 2*(mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node + + ))*(mesh_surf_diff(1, idx_node_nx)-mesh_surf_diff(1, + + idx_node)) + 2*(mesh_surf(3, idx_node_nx)-mesh_surf(3, + + idx_node))*(mesh_surf_diff(3, idx_node_nx)-mesh_surf_diff(3 + + , idx_node)) + arg1 = (mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node))**2 + + + (mesh_surf(3, idx_node_nx)-mesh_surf(3, idx_node))**2 + temp = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + chordl_diff = 0.D0 + ELSE + chordl_diff = arg1_diff/(2.0*temp) + END IF + chordl = temp + idx_node = FLATIDX(1, iptr, isurf) + idx_node_nx = FLATIDX(nx, iptr, isurf) + arg1_diff = 2*(mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node + + ))*(mesh_surf_diff(1, idx_node_nx)-mesh_surf_diff(1, + + idx_node)) + 2*(mesh_surf(3, idx_node_nx)-mesh_surf(3, + + idx_node))*(mesh_surf_diff(3, idx_node_nx)-mesh_surf_diff(3 + + , idx_node)) + arg1 = (mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node))**2 + + + (mesh_surf(3, idx_node_nx)-mesh_surf(3, idx_node))**2 + temp = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + chordr_diff = 0.D0 + ELSE + chordr_diff = arg1_diff/(2.0*temp) + END IF + chordr = temp + clafl_diff = claf_diff(iptl, isurf) + clafl = claf(iptl, isurf) +C Linearly interpolate the incidence projections over the STRIP + clafr_diff = claf_diff(iptr, isurf) + clafr = claf(iptr, isurf) +C + aincl_diff = dtr*aincs_diff(iptl, isurf) + dtr*addinc_diff( + + isurf) + aincl = aincs(iptl, isurf)*dtr + addinc(isurf)*dtr + aincr_diff = dtr*aincs_diff(iptr, isurf) + dtr*addinc_diff( + + isurf) + aincr = aincs(iptr, isurf)*dtr + addinc(isurf)*dtr + temp = SIN(aincl) + chsinl_diff = temp*chordl_diff + chordl*COS(aincl)*aincl_diff + chsinl = chordl*temp + temp = SIN(aincr) + chsinr_diff = temp*chordr_diff + chordr*COS(aincr)*aincr_diff + chsinr = chordr*temp + temp = COS(aincl) + chcosl_diff = temp*chordl_diff - chordl*SIN(aincl)*aincl_diff + chcosl = chordl*temp +C We need to determine which controls belong to this section +C Bring over the routine for this from makesurf but do it for each strip now + temp = COS(aincr) + chcosr_diff = temp*chordr_diff - chordr*SIN(aincr)*aincr_diff + chcosr = chordr*temp +C + DO n=1,ncontrol + isconl(n) = 0 + isconr(n) = 0 + DO iscon=1,nscon(iptl, isurf) + IF (icontd(iscon, iptl, isurf) .EQ. n) isconl(n) = iscon + ENDDO + DO iscon=1,nscon(iptr, isurf) + IF (icontd(iscon, iptr, isurf) .EQ. n) isconr(n) = iscon + ENDDO + ENDDO +C We need to determine which dvs belong to this strip +C and setup the chord projection gains +C Bring over the routine for this from makesurf but setup for strips +C + DO n=1,ndesign + chsinl_g_diff(n) = 0.D0 + chsinl_g(n) = 0. + chsinr_g_diff(n) = 0.D0 + chsinr_g(n) = 0. + chcosl_g_diff(n) = 0.D0 + chcosl_g(n) = 0. + chcosr_g_diff(n) = 0.D0 + chcosr_g(n) = 0. +C + DO isdes=1,nsdes(iptl, isurf) + IF (idestd(isdes, iptl, isurf) .EQ. n) THEN + chsinl_g_diff(n) = gaing(isdes, iptl, isurf)*dtr* + + chcosl_diff + chsinl_g(n) = chcosl*gaing(isdes, iptl, isurf)*dtr + chcosl_g_diff(n) = -(gaing(isdes, iptl, isurf)*dtr* + + chsinl_diff) + chcosl_g(n) = -(chsinl*gaing(isdes, iptl, isurf)*dtr) + END IF + ENDDO +C + DO isdes=1,nsdes(iptr, isurf) + IF (idestd(isdes, iptr, isurf) .EQ. n) THEN + chsinr_g_diff(n) = gaing(isdes, iptr, isurf)*dtr* + + chcosr_diff + chsinr_g(n) = chcosr*gaing(isdes, iptr, isurf)*dtr + chcosr_g_diff(n) = -(gaing(isdes, iptr, isurf)*dtr* + + chsinr_diff) + chcosr_g(n) = -(chsinr*gaing(isdes, iptr, isurf)*dtr) + END IF + ENDDO + ENDDO +C Set the strip geometry data +C Note these computations assume the mesh is not necessarily planar +C ultimately if/when we flatten the mesh into a planar one we will want +C to use the leading edge positions and chords from the original input mesh +C Strip left side +C +C +C + idx_node = FLATIDX(1, idx_y, isurf) + idx_node_nx = FLATIDX(nx, idx_y, isurf) + DO idx_dim=1,3 + rle1_diff(idx_dim, idx_strip) = mesh_surf_diff(idx_dim, + + idx_node) + rle1(idx_dim, idx_strip) = mesh_surf(idx_dim, idx_node) + ENDDO +C +C Strip right side + arg1_diff = 2*(mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node + + ))*(mesh_surf_diff(1, idx_node_nx)-mesh_surf_diff(1, + + idx_node)) + 2*(mesh_surf(3, idx_node_nx)-mesh_surf(3, + + idx_node))*(mesh_surf_diff(3, idx_node_nx)-mesh_surf_diff(3 + + , idx_node)) + arg1 = (mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node))**2 + + + (mesh_surf(3, idx_node_nx)-mesh_surf(3, idx_node))**2 + temp = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + chord1_diff(idx_strip) = 0.D0 + ELSE + chord1_diff(idx_strip) = arg1_diff/(2.0*temp) + END IF + chord1(idx_strip) = temp +C + idx_node_yp1 = FLATIDX(1, idx_y + 1, isurf) + idx_node_nx_yp1 = FLATIDX(nx, idx_y + 1, isurf) + DO idx_dim=1,3 + rle2_diff(idx_dim, idx_strip) = mesh_surf_diff(idx_dim, + + idx_node_yp1) + rle2(idx_dim, idx_strip) = mesh_surf(idx_dim, idx_node_yp1) + ENDDO +C Strip mid-point + arg1_diff = 2*(mesh_surf(1, idx_node_nx_yp1)-mesh_surf(1, + + idx_node_yp1))*(mesh_surf_diff(1, idx_node_nx_yp1)- + + mesh_surf_diff(1, idx_node_yp1)) + 2*(mesh_surf(3, + + idx_node_nx_yp1)-mesh_surf(3, idx_node_yp1))*(mesh_surf_diff + + (3, idx_node_nx_yp1)-mesh_surf_diff(3, idx_node_yp1)) + arg1 = (mesh_surf(1, idx_node_nx_yp1)-mesh_surf(1, + + idx_node_yp1))**2 + (mesh_surf(3, idx_node_nx_yp1)-mesh_surf + + (3, idx_node_yp1))**2 + temp = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + chord2_diff(idx_strip) = 0.D0 + ELSE + chord2_diff(idx_strip) = arg1_diff/(2.0*temp) + END IF + chord2(idx_strip) = temp +C +C Since the strips are linear SPANWISE we can just interpolate + DO idx_dim=1,3 + rle_diff(idx_dim, idx_strip) = (rle1_diff(idx_dim, idx_strip + + )+rle2_diff(idx_dim, idx_strip))/2. + rle(idx_dim, idx_strip) = (rle1(idx_dim, idx_strip)+rle2( + + idx_dim, idx_strip))/2. + ENDDO +C The strips are not necessarily linear chord wise but by definition the chord value is +C so we can interpolate +C Strip geometric incidence angle at the mid-point +C This is strip incidence angle is computed from the LE and TE points +C of the given geometry and is completely independent of AINC +C This quantity is needed to correctly handle nonplanar meshes and is only needed if the mesh isnt flattened + chord_diff(idx_strip) = (chord1_diff(idx_strip)+chord2_diff( + + idx_strip))/2. + chord(idx_strip) = (chord1(idx_strip)+chord2(idx_strip))/2. +C +C Strip width + arg1 = (mesh_surf(3, idx_node_nx)+mesh_surf(3, idx_node_nx_yp1 + + ))/2. - (mesh_surf(3, idx_node)+mesh_surf(3, idx_node_yp1))/ + + 2. + arg2 = (mesh_surf(1, idx_node_nx)+mesh_surf(1, idx_node_nx_yp1 + + ))/2. - (mesh_surf(1, idx_node)+mesh_surf(1, idx_node_yp1))/ + + 2. + gincstrip(idx_strip) = ATAN2(arg1, arg2) +C + m2_diff = mesh_surf_diff(2, idx_node_yp1) - mesh_surf_diff(2, + + idx_node) + m2 = mesh_surf(2, idx_node_yp1) - mesh_surf(2, idx_node) + m3_diff = mesh_surf_diff(3, idx_node_yp1) - mesh_surf_diff(3, + + idx_node) + m3 = mesh_surf(3, idx_node_yp1) - mesh_surf(3, idx_node) +C Strip LE and TE sweep slopes + arg1_diff = 2*m2*m2_diff + 2*m3*m3_diff + arg1 = m2**2 + m3**2 + temp = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + wstrip_diff(idx_strip) = 0.D0 + ELSE + wstrip_diff(idx_strip) = arg1_diff/(2.0*temp) + END IF + wstrip(idx_strip) = temp +C + tanle(idx_strip) = (mesh_surf(1, idx_node_yp1)-mesh_surf(1, + + idx_node))/wstrip(idx_strip) + idx_node = FLATIDX(nx, idx_y, isurf) + idx_node_yp1 = FLATIDX(nx, idx_y + 1, isurf) +C Compute chord projections and strip twists +C In AVL the AINCS are not interpolated. The chord projections are +C So we have to replicate this effect. +C LINEAR interpolation over the strip: left, right, and midpoint + tante(idx_strip) = (mesh_surf(1, idx_node_yp1)-mesh_surf(1, + + idx_node))/wstrip(idx_strip) +C +C + idx_nodel = FLATIDX(1, iptl, isurf) +C f1 = (mesh_surf(2,idx_node)-mesh_surf(2,idx_nodel))/ +C & (mesh_surf(2,idx_noder)-mesh_surf(2,idx_nodel)) +C f2 = (mesh_surf(2,idx_node_yp1)-mesh_surf(2,idx_nodel))/ +C & (mesh_surf(2,idx_noder)-mesh_surf(2,idx_nodel)) +C fc = (((mesh_surf(2,idx_node_yp1)+mesh_surf(2,idx_node))/2.) +C & -mesh_surf(2,idx_nodel))/(mesh_surf(2,idx_noder) +C & -mesh_surf(2,idx_nodel)) +C the above expressions will always evaluate to the following for individual strips + idx_noder = FLATIDX(1, iptr, isurf) +C +C + f1 = 0.0 + f2 = 1.0 +C Strip left side incidence +C CHSIN = CHSINL + f1*(CHSINR-CHSINL) +C CHCOS = CHCOSL + f1*(CHCOSR-CHCOSL) + fc = 0.5 +C +C Strip right side incidence +C CHSIN = CHSINL + f2*(CHSINR-CHSINL) +C CHCOS = CHCOSL + f2*(CHCOSR-CHCOSL) + ainc1(idx_strip) = ATAN2(chsinl, chcosl) +C +C Strip mid-point incidence + ainc2(idx_strip) = ATAN2(chsinr, chcosr) +C + chsin_diff = chsinl_diff + fc*(chsinr_diff-chsinl_diff) + chsin = chsinl + fc*(chsinr-chsinl) + chcos_diff = chcosl_diff + fc*(chcosr_diff-chcosl_diff) + chcos = chcosl + fc*(chcosr-chcosl) +C Set dv gains for incidence angles +C Bring over the routine for this from make surf + ainc_diff(idx_strip) = chcos*chsin_diff/(chsin**2+chcos**2) - + + chsin*chcos_diff/(chsin**2+chcos**2) + ainc(idx_strip) = ATAN2(chsin, chcos) +C + DO n=1,ndesign + chsin_g_diff = (1.0-fc)*chsinl_g_diff(n) + fc*chsinr_g_diff( + + n) + chsin_g = (1.0-fc)*chsinl_g(n) + fc*chsinr_g(n) + chcos_g_diff = (1.0-fc)*chcosl_g_diff(n) + fc*chcosr_g_diff( + + n) + chcos_g = (1.0-fc)*chcosl_g(n) + fc*chcosr_g(n) + temp = chsin*chsin + chcos*chcos + temp0 = (chcos*chsin_g-chsin*chcos_g)/temp + ainc_g_diff(idx_strip, n) = (chsin_g*chcos_diff+chcos* + + chsin_g_diff-chcos_g*chsin_diff-chsin*chcos_g_diff-temp0*( + + 2*chsin*chsin_diff+2*chcos*chcos_diff))/temp + ainc_g(idx_strip, n) = temp0 + ENDDO +C We have to now setup any control surfaces we defined for this strip +C Bring over the routine for this from makesurf but modified for a strip +C + DO n=1,ncontrol + icl = isconl(n) + icr = isconr(n) +C + IF (icl .EQ. 0 .OR. icr .EQ. 0) THEN +C no control effect here + gainda(n) = 0. + xled_diff(n) = 0.D0 + xled(n) = 0. + xted_diff(n) = 0.D0 + xted(n) = 0. +C + vhinge_diff(1, idx_strip, n) = 0.D0 + vhinge(1, idx_strip, n) = 0. + vhinge_diff(2, idx_strip, n) = 0.D0 + vhinge(2, idx_strip, n) = 0. + vhinge_diff(3, idx_strip, n) = 0.D0 + vhinge(3, idx_strip, n) = 0. +C + vrefl(idx_strip, n) = 0. +C + phinge(1, idx_strip, n) = 0. + phinge(2, idx_strip, n) = 0. + phinge(3, idx_strip, n) = 0. +C + ELSE +C control variable # N is active here +C SAB Note: This interpolation ensures that the hinge line is +C is linear which I think it is an ok assumption for arbitrary wings as long as the user is aware +C A curve hinge line could work if needed if we just interpolate XHINGED and scaled by local chord + gainda(n) = gaind(icl, iptl, isurf)*(1.0-fc) + gaind(icr, + + iptr, isurf)*fc +C + xhd_diff = xhinged(icl, iptl, isurf)*(1.0-fc)*chordl_diff + + + xhinged(icr, iptr, isurf)*fc*chordr_diff + xhd = chordl*xhinged(icl, iptl, isurf)*(1.0-fc) + chordr* + + xhinged(icr, iptr, isurf)*fc + IF (xhd .GE. 0.0) THEN +C TE control surface, with hinge at XHD + xled_diff(n) = xhd_diff + xled(n) = xhd + xted_diff(n) = chord_diff(idx_strip) + xted(n) = chord(idx_strip) + ELSE +C LE control surface, with hinge at -XHD + xled_diff(n) = 0.D0 + xled(n) = 0.0 + xted_diff(n) = -xhd_diff + xted(n) = -xhd + END IF +C + vhx_diff = vhinged(1, icl, iptl, isurf)*xyzscal_diff(1, + + isurf) + vhx = vhinged(1, icl, iptl, isurf)*xyzscal(1, isurf) + vhy_diff = vhinged(2, icl, iptl, isurf)*xyzscal_diff(2, + + isurf) + vhy = vhinged(2, icl, iptl, isurf)*xyzscal(2, isurf) + vhz_diff = vhinged(3, icl, iptl, isurf)*xyzscal_diff(3, + + isurf) + vhz = vhinged(3, icl, iptl, isurf)*xyzscal(3, isurf) + vsq_diff = 2*vhx*vhx_diff + 2*vhy*vhy_diff + 2*vhz* + + vhz_diff + vsq = vhx**2 + vhy**2 + vhz**2 + IF (vsq .EQ. 0.0) THEN + IF (chordr*xhinged(icr, iptr, isurf) .GE. 0.) THEN + abs0_diff = xhinged(icr, iptr, isurf)*chordr_diff + abs0 = chordr*xhinged(icr, iptr, isurf) + ELSE + abs0_diff = -(xhinged(icr, iptr, isurf)*chordr_diff) + abs0 = -(chordr*xhinged(icr, iptr, isurf)) + END IF + IF (chordl*xhinged(icl, iptl, isurf) .GE. 0.) THEN + abs1_diff = xhinged(icl, iptl, isurf)*chordl_diff + abs1 = chordl*xhinged(icl, iptl, isurf) + ELSE + abs1_diff = -(xhinged(icl, iptl, isurf)*chordl_diff) + abs1 = -(chordl*xhinged(icl, iptl, isurf)) + END IF +C default: set hinge vector along hingeline +C We are just setting the hinge line across the section +C this assumes the hinge is linear even for a nonlinear wing + vhx_diff = mesh_surf_diff(1, idx_noder) + abs0_diff - + + mesh_surf_diff(1, idx_nodel) - abs1_diff + vhx = mesh_surf(1, idx_noder) + abs0 - mesh_surf(1, + + idx_nodel) - abs1 + vhy_diff = mesh_surf_diff(2, idx_noder) - mesh_surf_diff + + (2, idx_nodel) + vhy = mesh_surf(2, idx_noder) - mesh_surf(2, idx_nodel) + vhz_diff = mesh_surf_diff(3, idx_noder) - mesh_surf_diff + + (3, idx_nodel) + vhz = mesh_surf(3, idx_noder) - mesh_surf(3, idx_nodel) + vhx_diff = xyzscal(1, isurf)*vhx_diff + vhx*xyzscal_diff + + (1, isurf) + vhx = vhx*xyzscal(1, isurf) + vhy_diff = xyzscal(2, isurf)*vhy_diff + vhy*xyzscal_diff + + (2, isurf) + vhy = vhy*xyzscal(2, isurf) + vhz_diff = xyzscal(3, isurf)*vhz_diff + vhz*xyzscal_diff + + (3, isurf) + vhz = vhz*xyzscal(3, isurf) + vsq_diff = 2*vhx*vhx_diff + 2*vhy*vhy_diff + 2*vhz* + + vhz_diff + vsq = vhx**2 + vhy**2 + vhz**2 + END IF +C + temp0 = SQRT(vsq) + IF (vsq .EQ. 0.D0) THEN + vmod_diff = 0.D0 + ELSE + vmod_diff = vsq_diff/(2.0*temp0) + END IF + vmod = temp0 + vhinge_diff(1, idx_strip, n) = (vhx_diff-vhx*vmod_diff/ + + vmod)/vmod + vhinge(1, idx_strip, n) = vhx/vmod + vhinge_diff(2, idx_strip, n) = (vhy_diff-vhy*vmod_diff/ + + vmod)/vmod + vhinge(2, idx_strip, n) = vhy/vmod + vhinge_diff(3, idx_strip, n) = (vhz_diff-vhz*vmod_diff/ + + vmod)/vmod + vhinge(3, idx_strip, n) = vhz/vmod +C + vrefl(idx_strip, n) = refld(icl, iptl, isurf) +C + IF (xhd .GE. 0.0) THEN + phinge(1, idx_strip, n) = rle(1, idx_strip) + xhd + phinge(2, idx_strip, n) = rle(2, idx_strip) + phinge(3, idx_strip, n) = rle(3, idx_strip) + ELSE + phinge(1, idx_strip, n) = rle(1, idx_strip) - xhd + phinge(2, idx_strip, n) = rle(2, idx_strip) + phinge(3, idx_strip, n) = rle(3, idx_strip) + END IF + END IF + ENDDO +C Interpolate CD-CL polar defining data from input to strips +C + DO idx_coef=1,6 + clcd(idx_coef, idx_strip) = (1.0-fc)*clcdsec(idx_coef, iptl + + , isurf) + fc*clcdsec(idx_coef, iptr, isurf) + ENDDO +C If the min drag is zero flag the strip as no-viscous data +C Set the panel (vortex) geometry data +C Accumulate the strip element indicies and start counting vorticies + lviscstrp(idx_strip) = clcd(4, idx_strip) .NE. 0.0 +C + IF (idx_strip .EQ. 1) THEN + ijfrst(idx_strip) = 1 + ELSE + ijfrst(idx_strip) = ijfrst(idx_strip-1) + nvstrp(idx_strip-1 + + ) + END IF + idx_vor = ijfrst(idx_strip) +C Associate the strip with the surface + nvstrp(idx_strip) = nvc(isurf) +C +C Prepare for cross section interpolation + lssurf(idx_strip) = isurf +C + nsl = nasec(iptl, isurf) +C CHORDC = CHORD(idx_strip) +C Funny story. this original line is now valid now that we interpolate over the strip + nsr = nasec(iptr, isurf) +C +C +C +C Suggestion from Hal Yougren for non linear sections: +C clafc = (1.-fc)*clafl + fc*clafr +C loop over vorticies for the strip + temp1 = chordl*clafl/chord(idx_strip) + temp2 = chordr*clafr/chord(idx_strip) + clafc_diff = (1.-fc)*(clafl*chordl_diff+chordl*clafl_diff- + + temp1*chord_diff(idx_strip))/chord(idx_strip) + fc*(clafr* + + chordr_diff+chordr*clafr_diff-temp2*chord_diff(idx_strip))/ + + chord(idx_strip) + clafc = (1.-fc)*temp1 + fc*temp2 +C +C Left bound vortex points + DO idx_x=1,nvc(isurf) +C Compute the panel left side chord + idx_node = FLATIDX(idx_x, idx_y, isurf) + arg1_diff = 2*(mesh_surf(1, idx_node+1)-mesh_surf(1, + + idx_node))*(mesh_surf_diff(1, idx_node+1)-mesh_surf_diff(1 + + , idx_node)) + 2*(mesh_surf(3, idx_node+1)-mesh_surf(3, + + idx_node))*(mesh_surf_diff(3, idx_node+1)-mesh_surf_diff(3 + + , idx_node)) + arg1 = (mesh_surf(1, idx_node+1)-mesh_surf(1, idx_node))**2 + + + (mesh_surf(3, idx_node+1)-mesh_surf(3, idx_node))**2 + temp0 = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + dc1_diff = 0.D0 + ELSE + dc1_diff = arg1_diff/(2.0*temp0) + END IF + dc1 = temp0 +C Right bound vortex points +C + IF (lmeshflat(isurf)) THEN +C Place vortex at panel quarter chord of the flat mesh + arg10_diff = 2*(mesh_surf(1, idx_node)-rle1(1, idx_strip)) + + *(mesh_surf_diff(1, idx_node)-rle1_diff(1, idx_strip)) + + + 2*(mesh_surf(3, idx_node)-rle1(3, idx_strip))*( + + mesh_surf_diff(3, idx_node)-rle1_diff(3, idx_strip)) + arg10 = (mesh_surf(1, idx_node)-rle1(1, idx_strip))**2 + ( + + mesh_surf(3, idx_node)-rle1(3, idx_strip))**2 + temp3 = SQRT(arg10) + IF (arg10 .EQ. 0.D0) THEN + dx1_diff = 0.D0 + ELSE + dx1_diff = arg10_diff/(2.0*temp3) + END IF + dx1 = temp3 + rv1_diff(2, idx_vor) = rle1_diff(2, idx_strip) + rv1(2, idx_vor) = rle1(2, idx_strip) + rv1_diff(3, idx_vor) = rle1_diff(3, idx_strip) + rv1(3, idx_vor) = rle1(3, idx_strip) +C Compute the panel left side angle + rv1_diff(1, idx_vor) = rle1_diff(1, idx_strip) + dx1_diff + + + dc1_diff/4. + rv1(1, idx_vor) = rle1(1, idx_strip) + dx1 + dc1/4. +C Place vortex at panel quarter chord of the true mesh + temp0 = mesh_surf(1, idx_node+1) - mesh_surf(1, idx_node) + temp = mesh_surf(3, idx_node+1) - mesh_surf(3, idx_node) + a1_diff = temp0*(mesh_surf_diff(3, idx_node+1)- + + mesh_surf_diff(3, idx_node))/(temp**2+temp0**2) - temp*( + + mesh_surf_diff(1, idx_node+1)-mesh_surf_diff(1, idx_node + + ))/(temp**2+temp0**2) + a1 = ATAN2(temp, temp0) + rv1msh_diff(2, idx_vor) = mesh_surf_diff(2, idx_node) + rv1msh(2, idx_vor) = mesh_surf(2, idx_node) + temp0 = COS(a1) + rv1msh_diff(1, idx_vor) = mesh_surf_diff(1, idx_node) + + + temp0*dc1_diff/4. - dc1*SIN(a1)*a1_diff/4. + rv1msh(1, idx_vor) = mesh_surf(1, idx_node) + dc1/4.*temp0 + temp0 = SIN(a1) + rv1msh_diff(3, idx_vor) = mesh_surf_diff(3, idx_node) + + + temp0*dc1_diff/4. + dc1*COS(a1)*a1_diff/4. + rv1msh(3, idx_vor) = mesh_surf(3, idx_node) + dc1/4.*temp0 + ELSE +C Compute the panel left side angle +C Place vortex at panel quarter chord + temp0 = mesh_surf(1, idx_node+1) - mesh_surf(1, idx_node) + temp = mesh_surf(3, idx_node+1) - mesh_surf(3, idx_node) + a1_diff = temp0*(mesh_surf_diff(3, idx_node+1)- + + mesh_surf_diff(3, idx_node))/(temp**2+temp0**2) - temp*( + + mesh_surf_diff(1, idx_node+1)-mesh_surf_diff(1, idx_node + + ))/(temp**2+temp0**2) + a1 = ATAN2(temp, temp0) + rv1_diff(2, idx_vor) = mesh_surf_diff(2, idx_node) + rv1(2, idx_vor) = mesh_surf(2, idx_node) + temp0 = COS(a1) + rv1_diff(1, idx_vor) = mesh_surf_diff(1, idx_node) + temp0 + + *dc1_diff/4. - dc1*SIN(a1)*a1_diff/4. + rv1(1, idx_vor) = mesh_surf(1, idx_node) + dc1/4.*temp0 +C Make a copy in the true mesh array for post processing + temp0 = SIN(a1) + rv1_diff(3, idx_vor) = mesh_surf_diff(3, idx_node) + temp0 + + *dc1_diff/4. + dc1*COS(a1)*a1_diff/4. + rv1(3, idx_vor) = mesh_surf(3, idx_node) + dc1/4.*temp0 + rv1msh_diff(2, idx_vor) = rv1_diff(2, idx_vor) + rv1msh(2, idx_vor) = rv1(2, idx_vor) + rv1msh_diff(1, idx_vor) = rv1_diff(1, idx_vor) + rv1msh(1, idx_vor) = rv1(1, idx_vor) + rv1msh_diff(3, idx_vor) = rv1_diff(3, idx_vor) + rv1msh(3, idx_vor) = rv1(3, idx_vor) + END IF +C Compute the panel right side chord + idx_node_yp1 = FLATIDX(idx_x, idx_y + 1, isurf) + arg1_diff = 2*(mesh_surf(1, idx_node_yp1+1)-mesh_surf(1, + + idx_node_yp1))*(mesh_surf_diff(1, idx_node_yp1+1)- + + mesh_surf_diff(1, idx_node_yp1)) + 2*(mesh_surf(3, + + idx_node_yp1+1)-mesh_surf(3, idx_node_yp1))*( + + mesh_surf_diff(3, idx_node_yp1+1)-mesh_surf_diff(3, + + idx_node_yp1)) + arg1 = (mesh_surf(1, idx_node_yp1+1)-mesh_surf(1, + + idx_node_yp1))**2 + (mesh_surf(3, idx_node_yp1+1)- + + mesh_surf(3, idx_node_yp1))**2 + temp0 = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + dc2_diff = 0.D0 + ELSE + dc2_diff = arg1_diff/(2.0*temp0) + END IF + dc2 = temp0 +C Mid-point bound vortex points +C Compute the panel mid-point chord +C Panels themselves can never be curved so just interpolate the chord +C store as the panel chord in common block +C + IF (lmeshflat(isurf)) THEN +C Place vortex at panel quarter chord of the flat mesh + arg10_diff = 2*(mesh_surf(1, idx_node_yp1)-rle2(1, + + idx_strip))*(mesh_surf_diff(1, idx_node_yp1)-rle2_diff(1 + + , idx_strip)) + 2*(mesh_surf(3, idx_node_yp1)-rle2(3, + + idx_strip))*(mesh_surf_diff(3, idx_node_yp1)-rle2_diff(3 + + , idx_strip)) + arg10 = (mesh_surf(1, idx_node_yp1)-rle2(1, idx_strip))**2 + + + (mesh_surf(3, idx_node_yp1)-rle2(3, idx_strip))**2 + temp3 = SQRT(arg10) + IF (arg10 .EQ. 0.D0) THEN + dx2_diff = 0.D0 + ELSE + dx2_diff = arg10_diff/(2.0*temp3) + END IF + dx2 = temp3 +C + rv2_diff(2, idx_vor) = rle2_diff(2, idx_strip) + rv2(2, idx_vor) = rle2(2, idx_strip) + rv2_diff(3, idx_vor) = rle2_diff(3, idx_strip) + rv2(3, idx_vor) = rle2(3, idx_strip) +C Compute the panel right side angle + rv2_diff(1, idx_vor) = rle2_diff(1, idx_strip) + dx2_diff + + + dc2_diff/4. + rv2(1, idx_vor) = rle2(1, idx_strip) + dx2 + dc2/4. +C +C Place vortex at panel quarter chord of the true mesh + temp0 = mesh_surf(1, idx_node_yp1+1) - mesh_surf(1, + + idx_node_yp1) + temp = mesh_surf(3, idx_node_yp1+1) - mesh_surf(3, + + idx_node_yp1) + a2_diff = temp0*(mesh_surf_diff(3, idx_node_yp1+1)- + + mesh_surf_diff(3, idx_node_yp1))/(temp**2+temp0**2) - + + temp*(mesh_surf_diff(1, idx_node_yp1+1)-mesh_surf_diff(1 + + , idx_node_yp1))/(temp**2+temp0**2) + a2 = ATAN2(temp, temp0) + rv2msh_diff(2, idx_vor) = mesh_surf_diff(2, idx_node_yp1) + rv2msh(2, idx_vor) = mesh_surf(2, idx_node_yp1) + temp0 = COS(a2) + rv2msh_diff(1, idx_vor) = mesh_surf_diff(1, idx_node_yp1) + + + temp0*dc2_diff/4. - dc2*SIN(a2)*a2_diff/4. + rv2msh(1, idx_vor) = mesh_surf(1, idx_node_yp1) + dc2/4.* + + temp0 + temp0 = SIN(a2) + rv2msh_diff(3, idx_vor) = mesh_surf_diff(3, idx_node_yp1) + + + temp0*dc2_diff/4. + dc2*COS(a2)*a2_diff/4. + rv2msh(3, idx_vor) = mesh_surf(3, idx_node_yp1) + dc2/4.* + + temp0 + ELSE +C Compute the panel right side angle +C Place vortex at panel quarter chord + temp0 = mesh_surf(1, idx_node_yp1+1) - mesh_surf(1, + + idx_node_yp1) + temp = mesh_surf(3, idx_node_yp1+1) - mesh_surf(3, + + idx_node_yp1) + a2_diff = temp0*(mesh_surf_diff(3, idx_node_yp1+1)- + + mesh_surf_diff(3, idx_node_yp1))/(temp**2+temp0**2) - + + temp*(mesh_surf_diff(1, idx_node_yp1+1)-mesh_surf_diff(1 + + , idx_node_yp1))/(temp**2+temp0**2) + a2 = ATAN2(temp, temp0) + rv2_diff(2, idx_vor) = mesh_surf_diff(2, idx_node_yp1) + rv2(2, idx_vor) = mesh_surf(2, idx_node_yp1) + temp0 = COS(a2) + rv2_diff(1, idx_vor) = mesh_surf_diff(1, idx_node_yp1) + + + temp0*dc2_diff/4. - dc2*SIN(a2)*a2_diff/4. + rv2(1, idx_vor) = mesh_surf(1, idx_node_yp1) + dc2/4.* + + temp0 +C Make a copy in the true mesh array for post processing + temp0 = SIN(a2) + rv2_diff(3, idx_vor) = mesh_surf_diff(3, idx_node_yp1) + + + temp0*dc2_diff/4. + dc2*COS(a2)*a2_diff/4. + rv2(3, idx_vor) = mesh_surf(3, idx_node_yp1) + dc2/4.* + + temp0 +C + rv2msh_diff(2, idx_vor) = rv2_diff(2, idx_vor) + rv2msh(2, idx_vor) = rv2(2, idx_vor) + rv2msh_diff(1, idx_vor) = rv2_diff(1, idx_vor) + rv2msh(1, idx_vor) = rv2(1, idx_vor) + rv2msh_diff(3, idx_vor) = rv2_diff(3, idx_vor) + rv2msh(3, idx_vor) = rv2(3, idx_vor) + END IF +C +C We need to compute the midpoint angle and panel strip chord projection +C as we need them to compute normals based on the real mesh + dxv_diff(idx_vor) = (dc1_diff+dc2_diff)/2. + dxv(idx_vor) = (dc1+dc2)/2. +C project the panel chord onto the strip chord + arg1_diff = (mesh_surf_diff(3, idx_node_yp1+1)+ + + mesh_surf_diff(3, idx_node+1))/2. - (mesh_surf_diff(3, + + idx_node_yp1)+mesh_surf_diff(3, idx_node))/2. + arg1 = (mesh_surf(3, idx_node_yp1+1)+mesh_surf(3, idx_node+1 + + ))/2. - (mesh_surf(3, idx_node_yp1)+mesh_surf(3, idx_node) + + )/2. + arg2_diff = (mesh_surf_diff(1, idx_node_yp1+1)+ + + mesh_surf_diff(1, idx_node+1))/2. - (mesh_surf_diff(1, + + idx_node_yp1)+mesh_surf_diff(1, idx_node))/2. + arg2 = (mesh_surf(1, idx_node_yp1+1)+mesh_surf(1, idx_node+1 + + ))/2. - (mesh_surf(1, idx_node_yp1)+mesh_surf(1, idx_node) + + )/2. + a3_diff = arg2*arg1_diff/(arg1**2+arg2**2) - arg1*arg2_diff/ + + (arg1**2+arg2**2) + a3 = ATAN2(arg1, arg2) + dxstrpv(idx_vor) = dxv(idx_vor)*COS(a3-gincstrip(idx_strip)) +C Panel Control points +C Y- point +C is just the panel midpoint +C + IF (lmeshflat(isurf)) THEN +C Place vortex at panel quarter chord of the flat mesh + arg10_diff = 2*((mesh_surf(1, idx_node_yp1)+mesh_surf(1, + + idx_node))/2-rle(1, idx_strip))*((mesh_surf_diff(1, + + idx_node_yp1)+mesh_surf_diff(1, idx_node))/2-rle_diff(1 + + , idx_strip)) + 2*((mesh_surf(3, idx_node_yp1)+mesh_surf + + (3, idx_node))/2-rle(3, idx_strip))*((mesh_surf_diff(3, + + idx_node_yp1)+mesh_surf_diff(3, idx_node))/2-rle_diff(3 + + , idx_strip)) + arg10 = ((mesh_surf(1, idx_node_yp1)+mesh_surf(1, idx_node + + ))/2-rle(1, idx_strip))**2 + ((mesh_surf(3, idx_node_yp1 + + )+mesh_surf(3, idx_node))/2-rle(3, idx_strip))**2 + temp3 = SQRT(arg10) + IF (arg10 .EQ. 0.D0) THEN + dx3_diff = 0.D0 + ELSE + dx3_diff = arg10_diff/(2.0*temp3) + END IF + dx3 = temp3 + rv_diff(2, idx_vor) = rle_diff(2, idx_strip) + rv(2, idx_vor) = rle(2, idx_strip) + rv_diff(3, idx_vor) = rle_diff(3, idx_strip) + rv(3, idx_vor) = rle(3, idx_strip) +C Place vortex at panel quarter chord of the true mesh + rv_diff(1, idx_vor) = rle_diff(1, idx_strip) + dx3_diff + + + dxv_diff(idx_vor)/4. + rv(1, idx_vor) = rle(1, idx_strip) + dx3 + dxv(idx_vor)/4. +C + rvmsh_diff(2, idx_vor) = (mesh_surf_diff(2, idx_node_yp1)+ + + mesh_surf_diff(2, idx_node))/2. + rvmsh(2, idx_vor) = (mesh_surf(2, idx_node_yp1)+mesh_surf( + + 2, idx_node))/2. + temp0 = COS(a3) + temp2 = dxv(idx_vor)/4. + rvmsh_diff(1, idx_vor) = (mesh_surf_diff(1, idx_node_yp1)+ + + mesh_surf_diff(1, idx_node))/2. + temp0*dxv_diff(idx_vor + + )/4. - temp2*SIN(a3)*a3_diff + rvmsh(1, idx_vor) = (mesh_surf(1, idx_node_yp1)+mesh_surf( + + 1, idx_node))/2. + temp2*temp0 + temp0 = SIN(a3) + temp2 = dxv(idx_vor)/4. + rvmsh_diff(3, idx_vor) = (mesh_surf_diff(3, idx_node_yp1)+ + + mesh_surf_diff(3, idx_node))/2. + temp0*dxv_diff(idx_vor + + )/4. + temp2*COS(a3)*a3_diff + rvmsh(3, idx_vor) = (mesh_surf(3, idx_node_yp1)+mesh_surf( + + 3, idx_node))/2. + temp2*temp0 + ELSE +C Place vortex at panel quarter chord + rv_diff(2, idx_vor) = (mesh_surf_diff(2, idx_node_yp1)+ + + mesh_surf_diff(2, idx_node))/2. + rv(2, idx_vor) = (mesh_surf(2, idx_node_yp1)+mesh_surf(2, + + idx_node))/2. + temp0 = COS(a3) + temp2 = dxv(idx_vor)/4. + rv_diff(1, idx_vor) = (mesh_surf_diff(1, idx_node_yp1)+ + + mesh_surf_diff(1, idx_node))/2. + temp0*dxv_diff(idx_vor + + )/4. - temp2*SIN(a3)*a3_diff + rv(1, idx_vor) = (mesh_surf(1, idx_node_yp1)+mesh_surf(1, + + idx_node))/2. + temp2*temp0 +C Make a copy in the true mesh array for post processing + temp0 = SIN(a3) + temp2 = dxv(idx_vor)/4. + rv_diff(3, idx_vor) = (mesh_surf_diff(3, idx_node_yp1)+ + + mesh_surf_diff(3, idx_node))/2. + temp0*dxv_diff(idx_vor + + )/4. + temp2*COS(a3)*a3_diff + rv(3, idx_vor) = (mesh_surf(3, idx_node_yp1)+mesh_surf(3, + + idx_node))/2. + temp2*temp0 +C + rvmsh_diff(2, idx_vor) = rv_diff(2, idx_vor) + rvmsh(2, idx_vor) = rv(2, idx_vor) + rvmsh_diff(1, idx_vor) = rv_diff(1, idx_vor) + rvmsh(1, idx_vor) = rv(1, idx_vor) + rvmsh_diff(3, idx_vor) = rv_diff(3, idx_vor) + rvmsh(3, idx_vor) = rv(3, idx_vor) + END IF +C +C +C Place the control point at the quarter chord + half chord*clafc +C note that clafc is a scaler so is 1. is for 2pi +C use data from vortex mid-point computation + rc_diff(2, idx_vor) = rv_diff(2, idx_vor) + rc(2, idx_vor) = rv(2, idx_vor) +C Source points +C Y- point + IF (lmeshflat(isurf)) THEN + rc_diff(1, idx_vor) = rv_diff(1, idx_vor) + dxv(idx_vor)* + + clafc_diff/2. + clafc*dxv_diff(idx_vor)/2. + rc(1, idx_vor) = rv(1, idx_vor) + clafc*(dxv(idx_vor)/2.) + rc_diff(3, idx_vor) = rv_diff(3, idx_vor) + rc(3, idx_vor) = rv(3, idx_vor) +C + temp0 = COS(a3) + temp2 = clafc*dxv(idx_vor)/2. + rcmsh_diff(1, idx_vor) = rvmsh_diff(1, idx_vor) + temp0*( + + dxv(idx_vor)*clafc_diff/2.+clafc*dxv_diff(idx_vor)/2.) - + + temp2*SIN(a3)*a3_diff + rcmsh(1, idx_vor) = rvmsh(1, idx_vor) + temp2*temp0 + temp0 = SIN(a3) + temp2 = clafc*dxv(idx_vor)/2. + rcmsh_diff(3, idx_vor) = rvmsh_diff(3, idx_vor) + temp0*( + + dxv(idx_vor)*clafc_diff/2.+clafc*dxv_diff(idx_vor)/2.) + + + temp2*COS(a3)*a3_diff + rcmsh(3, idx_vor) = rvmsh(3, idx_vor) + temp2*temp0 + rcmsh_diff(2, idx_vor) = rvmsh_diff(2, idx_vor) + rcmsh(2, idx_vor) = rvmsh(2, idx_vor) + ELSE + temp0 = COS(a3) + temp2 = clafc*dxv(idx_vor)/2. + rc_diff(1, idx_vor) = rv_diff(1, idx_vor) + temp0*(dxv( + + idx_vor)*clafc_diff/2.+clafc*dxv_diff(idx_vor)/2.) - + + temp2*SIN(a3)*a3_diff + rc(1, idx_vor) = rv(1, idx_vor) + temp2*temp0 +C Make a copy in the true mesh array for post processing + temp0 = SIN(a3) + temp2 = clafc*dxv(idx_vor)/2. + rc_diff(3, idx_vor) = rv_diff(3, idx_vor) + temp0*(dxv( + + idx_vor)*clafc_diff/2.+clafc*dxv_diff(idx_vor)/2.) + + + temp2*COS(a3)*a3_diff + rc(3, idx_vor) = rv(3, idx_vor) + temp2*temp0 +C + rcmsh_diff(1, idx_vor) = rc_diff(1, idx_vor) + rcmsh(1, idx_vor) = rc(1, idx_vor) + rcmsh_diff(3, idx_vor) = rc_diff(3, idx_vor) + rcmsh(3, idx_vor) = rc(3, idx_vor) + rcmsh_diff(2, idx_vor) = rc_diff(2, idx_vor) + rcmsh(2, idx_vor) = rc(2, idx_vor) + END IF +C +C Place the source point at the half chord +C use data from vortex mid-point computation +C add another quarter chord to the quarter chord + rs_diff(2, idx_vor) = rv_diff(2, idx_vor) + rs(2, idx_vor) = rv(2, idx_vor) +C Set the camber slopes for the panel +C Camber slope at control point + IF (lmeshflat(isurf)) THEN + rs_diff(1, idx_vor) = rv_diff(1, idx_vor) + dxv_diff( + + idx_vor)/4. + rs(1, idx_vor) = rv(1, idx_vor) + dxv(idx_vor)/4. + rs_diff(3, idx_vor) = rv_diff(3, idx_vor) + dxv_diff( + + idx_vor)/4. + rs(3, idx_vor) = rv(3, idx_vor) + dxv(idx_vor)/4. + ELSE + temp0 = COS(a3) + temp2 = dxv(idx_vor)/4. + rs_diff(1, idx_vor) = rv_diff(1, idx_vor) + temp0*dxv_diff + + (idx_vor)/4. - temp2*SIN(a3)*a3_diff + rs(1, idx_vor) = rv(1, idx_vor) + temp2*temp0 + temp0 = SIN(a3) + temp2 = dxv(idx_vor)/4. + rs_diff(3, idx_vor) = rv_diff(3, idx_vor) + temp0*dxv_diff + + (idx_vor)/4. + temp2*COS(a3)*a3_diff + rs(3, idx_vor) = rv(3, idx_vor) + temp2*temp0 + END IF +C +C + temp3 = (rc(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL AKIMA_D(xasec(1, iptl, isurf), xasec_diff(1, iptl, + + isurf), sasec(1, iptl, isurf), sasec_diff(1, + + iptl, isurf), nsl, (rc(1, idx_vor)-rle(1, + + idx_strip))/chord(idx_strip), (rc_diff(1, + + idx_vor)-rle_diff(1, idx_strip)-temp3* + + chord_diff(idx_strip))/chord(idx_strip), slopel + + , slopel_diff, dsdx) + temp3 = (rc(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL AKIMA_D(xasec(1, iptr, isurf), xasec_diff(1, iptr, + + isurf), sasec(1, iptr, isurf), sasec_diff(1, + + iptr, isurf), nsr, (rc(1, idx_vor)-rle(1, + + idx_strip))/chord(idx_strip), (rc_diff(1, + + idx_vor)-rle_diff(1, idx_strip)-temp3* + + chord_diff(idx_strip))/chord(idx_strip), sloper + + , sloper_diff, dsdx) +C Alternative for nonlinear sections per Hal Youngren +C SLOPEC(idx_vor) = (1.-fc)*SLOPEL + fc*SLOPER +C The original line is valid for interpolation over a strip +C +C Camber slope at vortex mid-point + temp2 = chordl*slopel/chord(idx_strip) + temp1 = chordr*sloper/chord(idx_strip) + slopec_diff(idx_vor) = (1.-fc)*(slopel*chordl_diff+chordl* + + slopel_diff-temp2*chord_diff(idx_strip))/chord(idx_strip) + + + fc*(sloper*chordr_diff+chordr*sloper_diff-temp1* + + chord_diff(idx_strip))/chord(idx_strip) + slopec(idx_vor) = (1.-fc)*temp2 + fc*temp1 +C + temp3 = (rv(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL AKIMA_D(xasec(1, iptl, isurf), xasec_diff(1, iptl, + + isurf), sasec(1, iptl, isurf), sasec_diff(1, + + iptl, isurf), nsl, (rv(1, idx_vor)-rle(1, + + idx_strip))/chord(idx_strip), (rv_diff(1, + + idx_vor)-rle_diff(1, idx_strip)-temp3* + + chord_diff(idx_strip))/chord(idx_strip), slopel + + , slopel_diff, dsdx) + temp3 = (rv(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL AKIMA_D(xasec(1, iptr, isurf), xasec_diff(1, iptr, + + isurf), sasec(1, iptr, isurf), sasec_diff(1, + + iptr, isurf), nsr, (rv(1, idx_vor)-rle(1, + + idx_strip))/chord(idx_strip), (rv_diff(1, + + idx_vor)-rle_diff(1, idx_strip)-temp3* + + chord_diff(idx_strip))/chord(idx_strip), sloper + + , sloper_diff, dsdx) +C Alternative for nonlinear sections per Hal Youngren +C SLOPEV(idx_vor) = (1.-fc)*SLOPEL + fc*SLOPER +C The original line is valid for interpolation over a strip +C +C Associate the panel with strip chord and component + temp2 = chordl*slopel/chord(idx_strip) + temp1 = chordr*sloper/chord(idx_strip) + slopev_diff(idx_vor) = (1.-fc)*(slopel*chordl_diff+chordl* + + slopel_diff-temp2*chord_diff(idx_strip))/chord(idx_strip) + + + fc*(sloper*chordr_diff+chordr*sloper_diff-temp1* + + chord_diff(idx_strip))/chord(idx_strip) + slopev(idx_vor) = (1.-fc)*temp2 + fc*temp1 +C + chordv_diff(idx_vor) = chord_diff(idx_strip) + chordv(idx_vor) = chord(idx_strip) +C Enforce no penetration at the control point + lvcomp(idx_vor) = lncomp(isurf) +C element inherits alpha,beta flag from surface + lvnc(idx_vor) = .true. +C +C We need to scale the control surface gains by the fraction +C of the element on the control surface + lvalbe(idx_vor) = lfalbe(isurf) +C +Cscale control gain by factor 0..1, (fraction of element on control surface) + DO n=1,ncontrol + temp2 = ((mesh_surf(1, idx_node)+mesh_surf(1, idx_node_yp1 + + ))/2-rle(1, idx_strip))/chord(idx_strip) + xpt_diff = ((mesh_surf_diff(1, idx_node)+mesh_surf_diff(1 + + , idx_node_yp1))/2-rle_diff(1, idx_strip)-temp2* + + chord_diff(idx_strip))/chord(idx_strip) + xpt = temp2 +C + temp3 = chord(idx_strip)/dxv(idx_vor) + temp2 = xled(n)/chord(idx_strip) + fracle_diff = temp3*((xled_diff(n)-temp2*chord_diff( + + idx_strip))/chord(idx_strip)-xpt_diff) + (temp2-xpt)*( + + chord_diff(idx_strip)-temp3*dxv_diff(idx_vor))/dxv( + + idx_vor) + fracle = (temp2-xpt)*temp3 +C + temp3 = chord(idx_strip)/dxv(idx_vor) + temp2 = xted(n)/chord(idx_strip) + fracte_diff = temp3*((xted_diff(n)-temp2*chord_diff( + + idx_strip))/chord(idx_strip)-xpt_diff) + (temp2-xpt)*( + + chord_diff(idx_strip)-temp3*dxv_diff(idx_vor))/dxv( + + idx_vor) + fracte = (temp2-xpt)*temp3 + IF (0.0 .LT. fracle) THEN + y1_diff = fracle_diff + y1 = fracle + ELSE + y1 = 0.0 + y1_diff = 0.D0 + END IF + IF (1.0 .GT. y1) THEN + fracle_diff = y1_diff + fracle = y1 + ELSE + fracle = 1.0 + fracle_diff = 0.D0 + END IF + IF (0.0 .LT. fracte) THEN + y2_diff = fracte_diff + y2 = fracte + ELSE + y2 = 0.0 + y2_diff = 0.D0 + END IF + IF (1.0 .GT. y2) THEN + fracte_diff = y2_diff + fracte = y2 + ELSE + fracte = 1.0 + fracte_diff = 0.D0 + END IF +C + dcontrol_diff(idx_vor, n) = gainda(n)*(fracte_diff- + + fracle_diff) + dcontrol(idx_vor, n) = gainda(n)*(fracte-fracle) + ENDDO +C TE control point used only if surface sheds a wake +C Use the cross sections to generate the OML +C nodal grid associated with vortex strip (aft-panel nodes) +C NOTE: airfoil in plane of wing, but not rotated perpendicular to dihedral; +C retained in (x,z) plane at this point +C Store the panel LE mid point for the next panel in the strip +C This gets used a lot here +C We use the original input mesh (true mesh) to compute points for the OML + lvnc(idx_vor) = lfwake(isurf) +C +C +C xptxind2 = (mesh_surf(1,idx_node_yp1+1) +C & - RLE2(1,idx_strip))/CHORD2(idx_strip) +C Interpolate cross section on left side + xptxind1 = ((mesh_surf(1, idx_node+1)+mesh_surf(1, + + idx_node_yp1+1))/2-rle(1, idx_strip))/chord(idx_strip) +C +C + CALL AKIMA(xlasec(1, iptl, isurf), zlasec(1, iptl, isurf), + + nsl, xptxind1, zl_l, dsdx) + CALL AKIMA(xuasec(1, iptl, isurf), zuasec(1, iptl, isurf), + + nsl, xptxind1, zu_l, dsdx) +C Interpolate cross section on right side +C + CALL AKIMA(xlasec(1, iptr, isurf), zlasec(1, iptr, isurf), + + nsr, xptxind1, zl_r, dsdx) + CALL AKIMA(xuasec(1, iptr, isurf), zuasec(1, iptr, isurf), + + nsr, xptxind1, zu_r, dsdx) +C Compute the left aft node of panel +C X-point +C +C +C Y-point + xyn1(1, idx_vor) = rle1(1, idx_strip) + xptxind1*chord1( + + idx_strip) +C +C Interpolate z from sections to left aft node of panel + xyn1(2, idx_vor) = rle1(2, idx_strip) +C + zl = (1.-f1)*zl_l + f1*zl_r +C Store left aft z-point + zu = (1.-f1)*zu_l + f1*zu_r +C + zlon1(idx_vor) = rle1(3, idx_strip) + zl*chord1(idx_strip) +C Compute the right aft node of panel +C X-point + zupn1(idx_vor) = rle1(3, idx_strip) + zu*chord1(idx_strip) +C +C Y-point + xyn2(1, idx_vor) = rle2(1, idx_strip) + xptxind1*chord2( + + idx_strip) +C +C Interpolate z from sections to right aft node of panel + xyn2(2, idx_vor) = rle2(2, idx_strip) + zl = (1.-f2)*zl_l + f2*zl_r +C Store right aft z-point + zu = (1.-f2)*zu_l + f2*zu_r +C + zlon2(idx_vor) = rle2(3, idx_strip) + zl*chord2(idx_strip) + zupn2(idx_vor) = rle2(3, idx_strip) + zu*chord2(idx_strip) +C + idx_vor = idx_vor + 1 + ENDDO +C End vortex loop + idx_strip = idx_strip + 1 + ENDDO +C End strip loop +C Compute the wetted area and cave from the true mesh +C + sum = 0.0 + wtot = 0.0 + DO jj=1,nj(isurf) + j = jfrst(isurf) + jj - 1 + astrp = wstrip(j)*chord(j) + sum = sum + astrp + wtot = wtot + wstrip(j) + ENDDO + ssurf(isurf) = sum +C add number of strips to the global count +C + IF (wtot .EQ. 0.0) THEN + cavesurf(isurf) = 0.0 + ELSE + cavesurf(isurf) = sum/wtot + END IF +C add number of of votrices to the global count + nstrip = nstrip + nj(isurf) + nvor = nvor + nk(isurf)*nj(isurf) + END IF + END + C Differentiation of sdupl in forward (tangent) mode (with options i4 dr8 r8): -C variations of useful results: rle chord rle1 chord1 rle2 -C chord2 wstrip ainc ainc_g rv1 rv2 rv rc dxv chordv -C slopev slopec dcontrol vhinge -C with respect to varying inputs: rle chord rle1 chord1 rle2 -C chord2 wstrip ainc ainc_g rv1 rv2 rv rc dxv chordv -C slopev slopec dcontrol vhinge +C variations of useful results: rv1msh rv2msh rvmsh rcmsh rle +C chord rle1 chord1 rle2 chord2 wstrip ainc ainc_g +C rv1 rv2 rv rc dxv chordv slopev slopec dcontrol +C vhinge +C with respect to varying inputs: rv1msh rv2msh rvmsh rcmsh rle +C chord rle1 chord1 rle2 chord2 wstrip ainc ainc_g +C rv1 rv2 rv rc dxv chordv slopev slopec dcontrol +C vhinge C SUBROUTINE SDUPL_D(nn, ypt, msg) INCLUDE 'AVL.INC' @@ -1360,8 +2676,10 @@ SUBROUTINE SDUPL_D(nn, ypt, msg) lfalbe(nni) = lfalbe(nn) lfload(nni) = lfload(nn) lrange(nni) = lrange(nn) -C IFRST(NNI) = NVOR + 1 lsurfspacing(nni) = lsurfspacing(nn) + lmeshflat(nni) = lmeshflat(nn) +C IFRST(NNI) = NVOR + 1 + lsurfmsh(nni) = lsurfmsh(nn) C C---- accumulate stuff for new image surface ifrst(nni) = ifrst(nni-1) + nk(nni-1)*nj(nni-1) @@ -1430,6 +2748,7 @@ SUBROUTINE SDUPL_D(nn, ypt, msg) rle(3, jji) = rle(3, jj) chord_diff(jji) = chord_diff(jj) chord(jji) = chord(jj) + gincstrip(jji) = gincstrip(jj) wstrip_diff(jji) = wstrip_diff(jj) wstrip(jji) = wstrip(jj) tanle(jji) = -tanle(jj) @@ -1514,11 +2833,39 @@ SUBROUTINE SDUPL_D(nn, ypt, msg) slopev(iii) = slopev(ii) dxv_diff(iii) = dxv_diff(ii) dxv(iii) = dxv(ii) + dxstrpv(iii) = dxstrpv(ii) chordv_diff(iii) = chordv_diff(ii) chordv(iii) = chordv(ii) lvcomp(iii) = lncomp(nni) lvalbe(iii) = lvalbe(ii) +C Duplicate mesh data if we are using a mesh lvnc(iii) = lvnc(ii) + IF (lsurfmsh(nn)) THEN + rv1msh_diff(1, iii) = rv2msh_diff(1, ii) + rv1msh(1, iii) = rv2msh(1, ii) + rv1msh_diff(2, iii) = -rv2msh_diff(2, ii) + rv1msh(2, iii) = -rv2msh(2, ii) + yoff + rv1msh_diff(3, iii) = rv2msh_diff(3, ii) + rv1msh(3, iii) = rv2msh(3, ii) + rv2msh_diff(1, iii) = rv1msh_diff(1, ii) + rv2msh(1, iii) = rv1msh(1, ii) + rv2msh_diff(2, iii) = -rv1msh_diff(2, ii) + rv2msh(2, iii) = -rv1msh(2, ii) + yoff + rv2msh_diff(3, iii) = rv1msh_diff(3, ii) + rv2msh(3, iii) = rv1msh(3, ii) + rvmsh_diff(1, iii) = rvmsh_diff(1, ii) + rvmsh(1, iii) = rvmsh(1, ii) + rvmsh_diff(2, iii) = -rvmsh_diff(2, ii) + rvmsh(2, iii) = -rvmsh(2, ii) + yoff + rvmsh_diff(3, iii) = rvmsh_diff(3, ii) + rvmsh(3, iii) = rvmsh(3, ii) + rcmsh_diff(1, iii) = rcmsh_diff(1, ii) + rcmsh(1, iii) = rcmsh(1, ii) + rcmsh_diff(2, iii) = -rcmsh_diff(2, ii) + rcmsh(2, iii) = -rcmsh(2, ii) + yoff + rcmsh_diff(3, iii) = rcmsh_diff(3, ii) + rcmsh(3, iii) = rcmsh(3, ii) + END IF C DO n=1,ncontrol Ccc RSGN = SIGN( 1.0 , VREFL(JJ,N) ) @@ -1565,13 +2912,15 @@ SUBROUTINE SDUPL_D(nn, ypt, msg) C Differentiation of encalc in forward (tangent) mode (with options i4 dr8 r8): C variations of useful results: ess ensy ensz xsref ysref zsref C enc env enc_d -C with respect to varying inputs: ainc ainc_g rv1 rv2 rv slopev -C slopec dcontrol vhinge +C with respect to varying inputs: rv1msh rv2msh rvmsh rcmsh ainc +C ainc_g rv1 rv2 rv slopev slopec dcontrol vhinge C BDUPL C C C C +C Also checks if surface has been assigned a point cloud mesh +C and uses the real mesh to compute normals if it is SUBROUTINE ENCALC_D() INCLUDE 'AVL.INC' INCLUDE 'AVL_ad_seeds.inc' @@ -1580,6 +2929,8 @@ SUBROUTINE ENCALC_D() REAL ep_diff(3), eq_diff(3), es_diff(3), eb_diff(3), ec_diff(3), + ecxb_diff(3) REAL ec_g(3, ndmax), ecxb_g(3) + REAL(kind=avl_real) dchstrip, dxt, dyt, dzt, ec_msh(3) + REAL(kind=avl_real) dxt_diff, dyt_diff, dzt_diff, ec_msh_diff(3) INTEGER j INTEGER i REAL dxle @@ -1606,12 +2957,6 @@ SUBROUTINE ENCALC_D() REAL ayte_diff REAL azte REAL azte_diff - REAL dxt - REAL dxt_diff - REAL dyt - REAL dyt_diff - REAL dzt - REAL dzt_diff INTRINSIC SQRT INTEGER nv INTEGER ii @@ -1642,11 +2987,15 @@ SUBROUTINE ENCALC_D() REAL endot_diff REAL DOT REAL DOT_D - REAL arg1 - REAL arg1_diff - REAL result1 - REAL result1_diff - REAL temp + REAL(kind=avl_real) arg1 + REAL(kind=avl_real) arg1_diff + REAL(kind=avl_real) result1 + REAL(kind=avl_real) result1_diff + REAL arg10 + REAL arg10_diff + REAL(kind=avl_real) temp + REAL temp0 + REAL(kind=8) temp1 INTEGER ii1 INTEGER ii3 INTEGER ii2 @@ -1705,52 +3054,103 @@ SUBROUTINE ENCALC_D() DO ii1=1,3 ecxb_diff(ii1) = 0.D0 ENDDO + DO ii1=1,3 + ec_msh_diff(ii1) = 0.D0 + ENDDO C C...Calculate the normal vector at control points and bound vortex midpoints C +C Since we cannot seperate the encalc routine for direct mesh assignment we have to make it a branch here DO j=1,nstrip +C + IF (lsurfmsh(lssurf(j))) THEN +C Calculate normal vector for the strip (normal to X axis) +C we can't just interpolate this anymore given that +C the strip is no longer necessarily linear chordwise +C We want the spanwise unit vector for the strip at the +C chordwise location specified by SAXFR (usually set to 0.25) +C Loop over all panels in the strip until we find the one that contains +C the SAXFR position in it's projected chord. Since the panels themselves are still linear +C we can just use the bound vortex unit vector of that panel as +C the spanwise unit vector of the strip at SAXFR +C SAB: This is slow, find a better way to do this +C +C +C + dchstrip = 0.0 +C compute the spanwise unit vector for Vperp def + searchsaxfr:DO i=ijfrst(j),ijfrst(j)+(nvstrp(j)-1) + dchstrip = dchstrip + dxstrpv(i) + IF (dchstrip .GE. chord(j)*saxfr) EXIT + ENDDO searchsaxfr +C +C + dxt_diff = rv2msh_diff(1, i) - rv1msh_diff(1, i) + dxt = rv2msh(1, i) - rv1msh(1, i) + dyt_diff = rv2msh_diff(2, i) - rv1msh_diff(2, i) + dyt = rv2msh(2, i) - rv1msh(2, i) + dzt_diff = rv2msh_diff(3, i) - rv1msh_diff(3, i) + dzt = rv2msh(3, i) - rv1msh(3, i) + xsref_diff(j) = rvmsh_diff(1, i) + xsref(j) = rvmsh(1, i) + ysref_diff(j) = rvmsh_diff(2, i) + ysref(j) = rvmsh(2, i) + zsref_diff(j) = rvmsh_diff(3, i) + zsref(j) = rvmsh(3, i) +C + ELSE +C original encalc routine for standard AVL geometry C C...Calculate normal vector for the strip (normal to X axis) - i = ijfrst(j) - dxle_diff = rv2_diff(1, i) - rv1_diff(1, i) - dxle = rv2(1, i) - rv1(1, i) - dyle_diff = rv2_diff(2, i) - rv1_diff(2, i) - dyle = rv2(2, i) - rv1(2, i) - dzle_diff = rv2_diff(3, i) - rv1_diff(3, i) - dzle = rv2(3, i) - rv1(3, i) + i = ijfrst(j) + dxle_diff = rv2_diff(1, i) - rv1_diff(1, i) + dxle = rv2(1, i) - rv1(1, i) + dyle_diff = rv2_diff(2, i) - rv1_diff(2, i) + dyle = rv2(2, i) - rv1(2, i) + dzle_diff = rv2_diff(3, i) - rv1_diff(3, i) + dzle = rv2(3, i) - rv1(3, i) C AXLE = (RV2(1,I)+RV1(1,I))*0.5 C AYLE = (RV2(2,I)+RV1(2,I))*0.5 C AZLE = (RV2(3,I)+RV1(3,I))*0.5 - axle_diff = rv_diff(1, i) - axle = rv(1, i) - ayle_diff = rv_diff(2, i) - ayle = rv(2, i) - azle_diff = rv_diff(3, i) - azle = rv(3, i) -C - i = ijfrst(j) + (nvstrp(j)-1) - dxte_diff = rv2_diff(1, i) - rv1_diff(1, i) - dxte = rv2(1, i) - rv1(1, i) - dyte_diff = rv2_diff(2, i) - rv1_diff(2, i) - dyte = rv2(2, i) - rv1(2, i) - dzte_diff = rv2_diff(3, i) - rv1_diff(3, i) - dzte = rv2(3, i) - rv1(3, i) + axle_diff = rv_diff(1, i) + axle = rv(1, i) + ayle_diff = rv_diff(2, i) + ayle = rv(2, i) + azle_diff = rv_diff(3, i) + azle = rv(3, i) +C + i = ijfrst(j) + (nvstrp(j)-1) + dxte_diff = rv2_diff(1, i) - rv1_diff(1, i) + dxte = rv2(1, i) - rv1(1, i) + dyte_diff = rv2_diff(2, i) - rv1_diff(2, i) + dyte = rv2(2, i) - rv1(2, i) + dzte_diff = rv2_diff(3, i) - rv1_diff(3, i) + dzte = rv2(3, i) - rv1(3, i) C AXTE = (RV2(1,I)+RV1(1,I))*0.5 C AYTE = (RV2(2,I)+RV1(2,I))*0.5 C AZTE = (RV2(3,I)+RV1(3,I))*0.5 - axte_diff = rv_diff(1, i) - axte = rv(1, i) - ayte_diff = rv_diff(2, i) - ayte = rv(2, i) - azte_diff = rv_diff(3, i) - azte = rv(3, i) -C - dxt_diff = (1.0-saxfr)*dxle_diff + saxfr*dxte_diff - dxt = (1.0-saxfr)*dxle + saxfr*dxte - dyt_diff = (1.0-saxfr)*dyle_diff + saxfr*dyte_diff - dyt = (1.0-saxfr)*dyle + saxfr*dyte - dzt_diff = (1.0-saxfr)*dzle_diff + saxfr*dzte_diff - dzt = (1.0-saxfr)*dzle + saxfr*dzte + axte_diff = rv_diff(1, i) + axte = rv(1, i) + ayte_diff = rv_diff(2, i) + ayte = rv(2, i) + azte_diff = rv_diff(3, i) + azte = rv(3, i) +C + dxt_diff = (1.0-saxfr)*dxle_diff + saxfr*dxte_diff + dxt = (1.0-saxfr)*dxle + saxfr*dxte + dyt_diff = (1.0-saxfr)*dyle_diff + saxfr*dyte_diff + dyt = (1.0-saxfr)*dyle + saxfr*dyte + dzt_diff = (1.0-saxfr)*dzle_diff + saxfr*dzte_diff + dzt = (1.0-saxfr)*dzle + saxfr*dzte +C + xsref_diff(j) = (1.0-saxfr)*axle_diff + saxfr*axte_diff + xsref(j) = (1.0-saxfr)*axle + saxfr*axte + ysref_diff(j) = (1.0-saxfr)*ayle_diff + saxfr*ayte_diff + ysref(j) = (1.0-saxfr)*ayle + saxfr*ayte + zsref_diff(j) = (1.0-saxfr)*azle_diff + saxfr*azte_diff + zsref(j) = (1.0-saxfr)*azle + saxfr*azte + END IF +C C arg1_diff = 2*dxt*dxt_diff + 2*dyt*dyt_diff + 2*dzt*dzt_diff arg1 = dxt*dxt + dyt*dyt + dzt*dzt @@ -1774,6 +3174,7 @@ SUBROUTINE ENCALC_D() result1 = temp ess_diff(2, j) = (dyt_diff-dyt*result1_diff/result1)/result1 ess(2, j) = dyt/result1 +C Treffz plane normals arg1_diff = 2*dxt*dxt_diff + 2*dyt*dyt_diff + 2*dzt*dzt_diff arg1 = dxt*dxt + dyt*dyt + dzt*dzt temp = SQRT(arg1) @@ -1808,14 +3209,6 @@ SUBROUTINE ENCALC_D() result1 = temp ensz_diff(j) = (dyt_diff-dyt*result1_diff/result1)/result1 ensz(j) = dyt/result1 -C - xsref_diff(j) = (1.0-saxfr)*axle_diff + saxfr*axte_diff - xsref(j) = (1.0-saxfr)*axle + saxfr*axte - ysref_diff(j) = (1.0-saxfr)*ayle_diff + saxfr*ayte_diff - ysref(j) = (1.0-saxfr)*ayle + saxfr*ayte - zsref_diff(j) = (1.0-saxfr)*azle_diff + saxfr*azte_diff - zsref(j) = (1.0-saxfr)*azle + saxfr*azte -C C es_diff(1) = 0.D0 es(1) = 0. @@ -1851,28 +3244,45 @@ SUBROUTINE ENCALC_D() enc_g(2, i, n) = 0. enc_g(3, i, n) = 0. ENDDO +C + IF (lsurfmsh(lssurf(j))) THEN +C Define unit vector along bound leg +C right h.v. pt - left h.v. pt + dxb_diff = rv2msh_diff(1, i) - rv1msh_diff(1, i) + dxb = rv2msh(1, i) - rv1msh(1, i) + dyb_diff = rv2msh_diff(2, i) - rv1msh_diff(2, i) + dyb = rv2msh(2, i) - rv1msh(2, i) + dzb_diff = rv2msh_diff(3, i) - rv1msh_diff(3, i) + dzb = rv2msh(3, i) - rv1msh(3, i) + ELSE C C...Define unit vector along bound leg C right h.v. pt - left h.v. pt - dxb_diff = rv2_diff(1, i) - rv1_diff(1, i) - dxb = rv2(1, i) - rv1(1, i) - dyb_diff = rv2_diff(2, i) - rv1_diff(2, i) - dyb = rv2(2, i) - rv1(2, i) - dzb_diff = rv2_diff(3, i) - rv1_diff(3, i) - dzb = rv2(3, i) - rv1(3, i) - arg1_diff = 2*dxb*dxb_diff + 2*dyb*dyb_diff + 2*dzb*dzb_diff - arg1 = dxb**2 + dyb**2 + dzb**2 - temp = SQRT(arg1) - IF (arg1 .EQ. 0.D0) THEN + dxb_diff = rv2_diff(1, i) - rv1_diff(1, i) + dxb = rv2(1, i) - rv1(1, i) + dyb_diff = rv2_diff(2, i) - rv1_diff(2, i) + dyb = rv2(2, i) - rv1(2, i) + dzb_diff = rv2_diff(3, i) - rv1_diff(3, i) + dzb = rv2(3, i) - rv1(3, i) + END IF + arg10_diff = 2*dxb*dxb_diff + 2*dyb*dyb_diff + 2*dzb*dzb_diff + arg10 = dxb**2 + dyb**2 + dzb**2 + temp0 = SQRT(arg10) + IF (arg10 .EQ. 0.D0) THEN emag_diff = 0.D0 ELSE - emag_diff = arg1_diff/(2.0*temp) + emag_diff = arg10_diff/(2.0*temp0) END IF - emag = temp + emag = temp0 eb_diff(1) = (dxb_diff-dxb*emag_diff/emag)/emag eb(1) = dxb/emag eb_diff(2) = (dyb_diff-dyb*emag_diff/emag)/emag eb(2) = dyb/emag +C First start by combining the contributions to the panel +C incidence from AVL incidence and camberline slope variables +C these are not actual geometric transformations of the mesh +C but rather further modifications to the chordwise vector that +C will get used to compute normals eb_diff(3) = (dzb_diff-dzb*emag_diff/emag)/emag eb(3) = dzb/emag C @@ -1893,41 +3303,138 @@ SUBROUTINE ENCALC_D() sinc = SIN(ang) cosc_diff = -(SIN(ang)*ang_diff) cosc = COS(ang) - ec_diff(1) = cosc_diff - ec(1) = cosc - ec_diff(2) = -(es(2)*sinc_diff+sinc*es_diff(2)) - ec(2) = -(sinc*es(2)) -C EC = rotation of strip normal vector? or along chord? - ec_diff(3) = -(es(3)*sinc_diff+sinc*es_diff(3)) - ec(3) = -(sinc*es(3)) +C + IF (lsurfmsh(lssurf(j))) THEN +C direct mesh assignemnt branch +C now we compute the chordwise panel vector +C note that panel`s chordwise vector has contributions +C from both the geometry itself and the incidence modification +C from the AVL AINC and camber slope variables +C Get the geometric chordwise vector using RVMSH and RCMSH which should +C be located in the same plane given that each individual panel is a +C plane +C +C + arg1_diff = 2*(rcmsh(1, i)-rvmsh(1, i))*(rcmsh_diff(1, i)- + + rvmsh_diff(1, i)) + 2*(rcmsh(2, i)-rvmsh(2, i))*( + + rcmsh_diff(2, i)-rvmsh_diff(2, i)) + 2*(rcmsh(3, i)-rvmsh( + + 3, i))*(rcmsh_diff(3, i)-rvmsh_diff(3, i)) + arg1 = (rcmsh(1, i)-rvmsh(1, i))**2 + (rcmsh(2, i)-rvmsh(2, + + i))**2 + (rcmsh(3, i)-rvmsh(3, i))**2 + temp = SQRT(arg1) + IF (arg1 .EQ. 0.D0) THEN + emag_diff = 0.D0 + ELSE + emag_diff = arg1_diff/(2.0*temp) + END IF + emag = temp + temp1 = (rcmsh(1, i)-rvmsh(1, i))/emag + ec_msh_diff(1) = (rcmsh_diff(1, i)-rvmsh_diff(1, i)-temp1* + + emag_diff)/emag + ec_msh(1) = temp1 + temp1 = (rcmsh(2, i)-rvmsh(2, i))/emag + ec_msh_diff(2) = (rcmsh_diff(2, i)-rvmsh_diff(2, i)-temp1* + + emag_diff)/emag + ec_msh(2) = temp1 +C Now we have to rotate this vector by the incidence contribution from AINC and CAMBER +C However, this rotation needs to be done about the local y-axis of the wing +C Earlier we computed ES the normal vector of the strip projected to the Trefftz plane +C The axis we need to rotate about is the one purpendicular to this ES. +C As a result all panel normals in a given strip will be rotated about the same axis defined by the that strip +C The components of the rotation axis are obtained from ES as follows +C rot_axis(1) = 0 +C rot_axis(2) = -ES(3) +C rot_axis(3) = ES(2) +C We can then multiply ec_msh by the rotation matrix for a rotation about an arbitrary axis +C see https://pubs.aip.org/aapt/ajp/article/44/1/63/1050167/Formalism-for-the-rotation-matrix-of-rotations +C Note that standard AVL also does this exact same thing but since they always rotate the vector [1,0,0] +C the result collapses into the ridiculously simple expression for EC that you see in the other branch + temp1 = (rcmsh(3, i)-rvmsh(3, i))/emag + ec_msh_diff(3) = (rcmsh_diff(3, i)-rvmsh_diff(3, i)-temp1* + + emag_diff)/emag + ec_msh(3) = temp1 +C +C + ec_diff(1) = ec_msh(1)*cosc_diff + cosc*ec_msh_diff(1) + + + ec_msh(2)*(sinc*es_diff(2)+es(2)*sinc_diff) + es(2)*sinc* + + ec_msh_diff(2) + ec_msh(3)*(sinc*es_diff(3)+es(3)* + + sinc_diff) + es(3)*sinc*ec_msh_diff(3) + ec(1) = cosc*ec_msh(1) + es(2)*sinc*ec_msh(2) + es(3)*sinc* + + ec_msh(3) + temp0 = es(3)*es(3)*(-cosc+1) + cosc + ec_diff(2) = ec_msh(2)*((1-cosc)*2*es(3)*es_diff(3)-(es(3)** + + 2-1.0)*cosc_diff) + temp0*ec_msh_diff(2) - sinc*es_diff(2) + + - es(2)*sinc_diff - (1-cosc)*ec_msh(3)*(es(3)*es_diff(2)+ + + es(2)*es_diff(3)) - es(2)*es(3)*((1-cosc)*ec_msh_diff(3)- + + ec_msh(3)*cosc_diff) + ec(2) = temp0*ec_msh(2) - es(2)*sinc - es(2)*es(3)*((1-cosc) + + *ec_msh(3)) + temp0 = es(2)*es(2)*(-cosc+1) + cosc + ec_diff(3) = ec_msh(3)*((1-cosc)*2*es(2)*es_diff(2)-(es(2)** + + 2-1.0)*cosc_diff) + temp0*ec_msh_diff(3) - (1-cosc)*ec_msh + + (2)*(es(3)*es_diff(2)+es(2)*es_diff(3)) - es(2)*es(3)*((1- + + cosc)*ec_msh_diff(2)-ec_msh(2)*cosc_diff) - ec_msh(1)*( + + sinc*es_diff(3)+es(3)*sinc_diff) - es(3)*sinc*ec_msh_diff( + + 1) + ec(3) = temp0*ec_msh(3) - es(2)*es(3)*((1-cosc)*ec_msh(2)) - + + es(3)*sinc*ec_msh(1) +C + ELSE + ec_diff(1) = cosc_diff + ec(1) = cosc + ec_diff(2) = -(es(2)*sinc_diff+sinc*es_diff(2)) + ec(2) = -(sinc*es(2)) + ec_diff(3) = -(es(3)*sinc_diff+sinc*es_diff(3)) + ec(3) = -(sinc*es(3)) + END IF +C +C The derivative here also changes if we use a custom mesh +C Note the derivative is only wrt to AVL incidence vars +C as those are the vars AVL DVs can support DO n=1,ndesign - ec_g(1, n) = -(sinc*ainc_g(j, n)) - ec_g(2, n) = -(cosc*es(2)*ainc_g(j, n)) - ec_g(3, n) = -(cosc*es(3)*ainc_g(j, n)) + IF (lsurfmsh(lssurf(j))) THEN + ec_g(1, n) = (-(sinc*ec_msh(1))+es(2)*cosc*ec_msh(2)+es(3) + + *cosc*ec_msh(3))*ainc_g(j, n) + ec_g(2, n) = (-(es(2)*cosc)+(es(3)**2*(1+sinc)-sinc)* + + ec_msh(2)-es(2)*es(3)*(1+sinc)*ec_msh(3))*ainc_g(j, n) + ec_g(3, n) = (-(es(3)*cosc*ec_msh(1))-es(2)*es(3)*(1+sinc) + + *ec_msh(2)+(es(2)**2*(1+sinc)-sinc)*ec_msh(3))*ainc_g(j + + , n) +C + ELSE + ec_g(1, n) = -(sinc*ainc_g(j, n)) + ec_g(2, n) = -(cosc*es(2)*ainc_g(j, n)) + ec_g(3, n) = -(cosc*es(3)*ainc_g(j, n)) + END IF ENDDO C C...Normal vector is perpendicular to camberline vector and to the bound leg CALL CROSS_D(ec, ec_diff, eb, eb_diff, ecxb, ecxb_diff) - arg1_diff = 2*ecxb(1)*ecxb_diff(1) + 2*ecxb(2)*ecxb_diff(2) + + arg10_diff = 2*ecxb(1)*ecxb_diff(1) + 2*ecxb(2)*ecxb_diff(2) + + 2*ecxb(3)*ecxb_diff(3) - arg1 = ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2 - temp = SQRT(arg1) - IF (arg1 .EQ. 0.D0) THEN + arg10 = ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2 + temp0 = SQRT(arg10) + IF (arg10 .EQ. 0.D0) THEN emag_diff = 0.D0 ELSE - emag_diff = arg1_diff/(2.0*temp) + emag_diff = arg10_diff/(2.0*temp0) END IF - emag = temp + emag = temp0 +C This section is identical to the normal vector at the control +C point. The only different is that the AVL camberline slope +C is taken at the bound vortex point rather than the control point +C the geometric contributions to the normal vector at both of these +C point is identical as the lie in the plane of the same panel. IF (emag .NE. 0.0) THEN - temp = ecxb(1)/emag - enc_diff(1, i) = (ecxb_diff(1)-temp*emag_diff)/emag - enc(1, i) = temp - temp = ecxb(2)/emag - enc_diff(2, i) = (ecxb_diff(2)-temp*emag_diff)/emag - enc(2, i) = temp - temp = ecxb(3)/emag - enc_diff(3, i) = (ecxb_diff(3)-temp*emag_diff)/emag - enc(3, i) = temp + temp0 = ecxb(1)/emag + enc_diff(1, i) = (ecxb_diff(1)-temp0*emag_diff)/emag + enc(1, i) = temp0 + temp0 = ecxb(2)/emag + enc_diff(2, i) = (ecxb_diff(2)-temp0*emag_diff)/emag + enc(2, i) = temp0 + temp0 = ecxb(3)/emag + enc_diff(3, i) = (ecxb_diff(3)-temp0*emag_diff)/emag + enc(3, i) = temp0 DO n=1,ndesign CALL CROSS(ec_g(1, n), eb, ecxb_g) emag_g = enc(1, i)*ecxb_g(1) + enc(2, i)*ecxb_g(2) + enc(3 @@ -1963,40 +3470,85 @@ SUBROUTINE ENCALC_D() sinc = SIN(ang) cosc_diff = -(SIN(ang)*ang_diff) cosc = COS(ang) - ec_diff(1) = cosc_diff - ec(1) = cosc - ec_diff(2) = -(es(2)*sinc_diff+sinc*es_diff(2)) - ec(2) = -(sinc*es(2)) - ec_diff(3) = -(es(3)*sinc_diff+sinc*es_diff(3)) - ec(3) = -(sinc*es(3)) + IF (lsurfmsh(lssurf(j))) THEN +C direct mesh assignment branch +C see explanation in section above for control point normals +C ec_msh was already computed in that section + ec_diff(1) = ec_msh(1)*cosc_diff + cosc*ec_msh_diff(1) + + + ec_msh(2)*(sinc*es_diff(2)+es(2)*sinc_diff) + es(2)*sinc* + + ec_msh_diff(2) + ec_msh(3)*(sinc*es_diff(3)+es(3)* + + sinc_diff) + es(3)*sinc*ec_msh_diff(3) + ec(1) = cosc*ec_msh(1) + es(2)*sinc*ec_msh(2) + es(3)*sinc* + + ec_msh(3) + temp0 = es(3)*es(3)*(-cosc+1) + cosc + ec_diff(2) = ec_msh(2)*((1-cosc)*2*es(3)*es_diff(3)-(es(3)** + + 2-1.0)*cosc_diff) + temp0*ec_msh_diff(2) - sinc*es_diff(2) + + - es(2)*sinc_diff - (1-cosc)*ec_msh(3)*(es(3)*es_diff(2)+ + + es(2)*es_diff(3)) - es(2)*es(3)*((1-cosc)*ec_msh_diff(3)- + + ec_msh(3)*cosc_diff) + ec(2) = temp0*ec_msh(2) - es(2)*sinc - es(2)*es(3)*((1-cosc) + + *ec_msh(3)) + temp0 = es(2)*es(2)*(-cosc+1) + cosc + ec_diff(3) = ec_msh(3)*((1-cosc)*2*es(2)*es_diff(2)-(es(2)** + + 2-1.0)*cosc_diff) + temp0*ec_msh_diff(3) - (1-cosc)*ec_msh + + (2)*(es(3)*es_diff(2)+es(2)*es_diff(3)) - es(2)*es(3)*((1- + + cosc)*ec_msh_diff(2)-ec_msh(2)*cosc_diff) - ec_msh(1)*( + + sinc*es_diff(3)+es(3)*sinc_diff) - es(3)*sinc*ec_msh_diff( + + 1) + ec(3) = temp0*ec_msh(3) - es(2)*es(3)*((1-cosc)*ec_msh(2)) - + + es(3)*sinc*ec_msh(1) +C + ELSE + ec_diff(1) = cosc_diff + ec(1) = cosc + ec_diff(2) = -(es(2)*sinc_diff+sinc*es_diff(2)) + ec(2) = -(sinc*es(2)) + ec_diff(3) = -(es(3)*sinc_diff+sinc*es_diff(3)) + ec(3) = -(sinc*es(3)) + END IF +C DO n=1,ndesign - ec_g(1, n) = -(sinc*ainc_g(j, n)) - ec_g(2, n) = -(cosc*es(2)*ainc_g(j, n)) - ec_g(3, n) = -(cosc*es(3)*ainc_g(j, n)) + IF (lsurfmsh(lssurf(j))) THEN +C Direct mesh assignment branch + ec_g(1, n) = (-(sinc*ec_msh(1))+es(2)*cosc*ec_msh(2)+es(3) + + *cosc*ec_msh(3))*ainc_g(j, n) + ec_g(2, n) = (-(es(2)*cosc)+(es(3)**2*(1+sinc)-sinc)* + + ec_msh(2)-es(2)*es(3)*(1+sinc)*ec_msh(3))*ainc_g(j, n) + ec_g(3, n) = (-(es(3)*cosc*ec_msh(1))-es(2)*es(3)*(1+sinc) + + *ec_msh(2)+(es(2)**2*(1+sinc)-sinc)*ec_msh(3))*ainc_g(j + + , n) +C + ELSE + ec_g(1, n) = -(sinc*ainc_g(j, n)) + ec_g(2, n) = -(cosc*es(2)*ainc_g(j, n)) + ec_g(3, n) = -(cosc*es(3)*ainc_g(j, n)) + END IF ENDDO C C...Normal vector is perpendicular to camberline vector and to the bound leg CALL CROSS_D(ec, ec_diff, eb, eb_diff, ecxb, ecxb_diff) - arg1_diff = 2*ecxb(1)*ecxb_diff(1) + 2*ecxb(2)*ecxb_diff(2) + + arg10_diff = 2*ecxb(1)*ecxb_diff(1) + 2*ecxb(2)*ecxb_diff(2) + + 2*ecxb(3)*ecxb_diff(3) - arg1 = ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2 - temp = SQRT(arg1) - IF (arg1 .EQ. 0.D0) THEN + arg10 = ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2 + temp0 = SQRT(arg10) + IF (arg10 .EQ. 0.D0) THEN emag_diff = 0.D0 ELSE - emag_diff = arg1_diff/(2.0*temp) + emag_diff = arg10_diff/(2.0*temp0) END IF - emag = temp + emag = temp0 +C this is a pure rotation of the normal vector +C the geometric contribution from the mesh is already accounted for IF (emag .NE. 0.0) THEN - temp = ecxb(1)/emag - env_diff(1, i) = (ecxb_diff(1)-temp*emag_diff)/emag - env(1, i) = temp - temp = ecxb(2)/emag - env_diff(2, i) = (ecxb_diff(2)-temp*emag_diff)/emag - env(2, i) = temp - temp = ecxb(3)/emag - env_diff(3, i) = (ecxb_diff(3)-temp*emag_diff)/emag - env(3, i) = temp + temp0 = ecxb(1)/emag + env_diff(1, i) = (ecxb_diff(1)-temp0*emag_diff)/emag + env(1, i) = temp0 + temp0 = ecxb(2)/emag + env_diff(2, i) = (ecxb_diff(2)-temp0*emag_diff)/emag + env(2, i) = temp0 + temp0 = ecxb(3)/emag + env_diff(3, i) = (ecxb_diff(3)-temp0*emag_diff)/emag + env(3, i) = temp0 DO n=1,ndesign CALL CROSS(ec_g(1, n), eb, ecxb_g) emag_g = enc(1, i)*ecxb_g(1) + enc(2, i)*ecxb_g(2) + enc(3 @@ -2104,5 +3656,4 @@ SUBROUTINE ENCALC_D() RETURN END C ENCALC -C diff --git a/src/ad_src/forward_ad_src/sgutil_d.f b/src/ad_src/forward_ad_src/sgutil_d.f index 9bc346e..df8203a 100644 --- a/src/ad_src/forward_ad_src/sgutil_d.f +++ b/src/ad_src/forward_ad_src/sgutil_d.f @@ -290,6 +290,24 @@ SUBROUTINE AKIMA_D(x, x_diff, y, y_diff, n, xx, xx_diff, yy, C C C +C This is a extremely important funciton that is not +C documented for some reason. +C Inputs: +C NVC: NUMBER OF DESIRED POINTS IN ARRAY +C CSPACE: SPACING PARAMETER (-3<=PSPACE<=3). +C DEFINES POINT DISTRIBUTION +C TO BE USED AS FOLLOWS: +C PSPACE = 0 : EQUAL SPACING +C PSPACE = 1 : COSINE SPACING. +C PSPACE = 2 : SINE SPACING +C (CONCENTRATING POINTS NEAR 0). +C PSPACE = 3 : EQUAL SPACING. +C CLAF: CL alfa (needed to determine control point location) +C Outputs: +C XPT: Array of panel leading edge x-locations +C XVR: Array of vortex x-locations +C XSR: Array of source x-locations +C XCP: Array of control point x-locations SUBROUTINE CSPACER_D(nvc, cspace, claf, claf_diff, xpt, xvr, xsr, + xcp, xcp_diff) REAL xpt(*), xvr(*), xsr(*), xcp(*) @@ -342,6 +360,8 @@ SUBROUTINE CSPACER_D(nvc, cspace, claf, claf_diff, xpt, xvr, xsr, acsp = -cspace END IF ncsp = INT(acsp) +C Each of these provides a quarter panel chord offset for cosine, +C sine, and uniform spacing respectively. IF (ncsp .EQ. 0) THEN f0 = 1.0 - acsp f1 = acsp @@ -363,10 +383,16 @@ SUBROUTINE CSPACER_D(nvc, cspace, claf, claf_diff, xpt, xvr, xsr, C DO ivc=1,nvc C------ uniform +C eqv (IVC-1)/NVC xc0 = INT(4*ivc-4)*dxc0 xpt0 = xc0 +C quarter-chord xvr0 = xc0 + dxc0 +C half-chord xsr0 = xc0 + 2.0*dxc0 +C quarter-chord + half-chord*claf +C Note: claf is a scaling factor so typically claf = 1 and the control point +C is at the three-quarter chord position of the panel xcp0_diff = dxc0*2.0*claf_diff xcp0 = xc0 + dxc0 + 2.0*dxc0*claf C diff --git a/src/ad_src/reverse_ad_src/amake_b.f b/src/ad_src/reverse_ad_src/amake_b.f index baaab36..abb90cb 100644 --- a/src/ad_src/reverse_ad_src/amake_b.f +++ b/src/ad_src/reverse_ad_src/amake_b.f @@ -6,18 +6,18 @@ C chord2 wstrip ess ensy ensz xsref ysref zsref C rv1 rv2 rv rc rs dxv chordv enc env enc_d C with respect to varying inputs: xyzscal xyztran addinc xyzles -C chords aincs xasec sasec claf rle chord rle1 chord1 -C rle2 chord2 wstrip ess ensy ensz xsref ysref zsref -C rv1 rv2 rv rc rs dxv chordv enc env enc_d +C chords aincs xasec sasec claf mshblk rle chord +C rle1 chord1 rle2 chord2 wstrip ess ensy ensz xsref +C ysref zsref rv1 rv2 rv rc rs dxv chordv enc env +C enc_d C RW status of diff variables: xyzscal:out xyztran:out addinc:out C xyzles:out chords:out aincs:out xasec:out sasec:out -C claf:out rle:in-out chord:in-out rle1:in-out chord1:in-out -C rle2:in-out chord2:in-out wstrip:in-out ess:in-out -C ensy:in-out ensz:in-out xsref:in-out ysref:in-out -C zsref:in-out rv1:in-out rv2:in-out rv:in-out rc:in-out -C rs:in-out dxv:in-out chordv:in-out enc:in-out -C env:in-out enc_d:in-out -C MAKESURF +C claf:out mshblk:out rle:in-out chord:in-out rle1:in-out +C chord1:in-out rle2:in-out chord2:in-out wstrip:in-out +C ess:in-out ensy:in-out ensz:in-out xsref:in-out +C ysref:in-out zsref:in-out rv1:in-out rv2:in-out +C rv:in-out rc:in-out rs:in-out dxv:in-out chordv:in-out +C enc:in-out env:in-out enc_d:in-out SUBROUTINE UPDATE_SURFACES_B() use avl_heap_inc use avl_heap_diff_inc @@ -36,23 +36,55 @@ SUBROUTINE UPDATE_SURFACES_B() nstrip = 0 nvor = 0 isurf = 1 + nsurfdupl = 0 + DO ii=1,nsurf + IF (ldupl(ii)) THEN + CALL PUSHCONTROL1B(1) + nsurfdupl = nsurfdupl + 1 + ELSE + CALL PUSHCONTROL1B(0) + END IF + ENDDO C the iterations of this loop are not independent because we count C up the size information as we make each surface DO ii=1,nsurf-nsurfdupl IF (lverbose) WRITE(*, *) 'Updating surface ', isurf - CALL PUSHREAL8ARRAY(chord, nsmax) - CALL PUSHINTEGER4ARRAY(nvstrp, nsmax) - CALL PUSHINTEGER4ARRAY(ijfrst, nsmax) - CALL PUSHINTEGER4ARRAY(nvs, nfmax) - CALL PUSHINTEGER4ARRAY(nvc, nfmax) - CALL PUSHINTEGER4ARRAY(jfrst, nfmax) - CALL PUSHINTEGER4ARRAY(nj, nfmax) - CALL MAKESURF(isurf) + IF (lsurfmsh(isurf)) THEN + CALL PUSHREAL8ARRAY(dxv, nvmax) + CALL PUSHREAL8ARRAY(rc, 3*nvmax) + CALL PUSHREAL8ARRAY(rv, 3*nvmax) + CALL PUSHREAL8ARRAY(chord2, nsmax) + CALL PUSHREAL8ARRAY(rle2, 3*nsmax) + CALL PUSHREAL8ARRAY(chord1, nsmax) + CALL PUSHREAL8ARRAY(rle1, 3*nsmax) + CALL PUSHREAL8ARRAY(chord, nsmax) + CALL PUSHREAL8ARRAY(rle, 3*nsmax) + CALL PUSHINTEGER4ARRAY(nvstrp, nsmax) + CALL PUSHINTEGER4ARRAY(ijfrst, nsmax) + CALL PUSHINTEGER4ARRAY(nvs, nfmax) + CALL PUSHINTEGER4ARRAY(nvc, nfmax) + CALL PUSHINTEGER4ARRAY(jfrst, nfmax) + CALL PUSHINTEGER4ARRAY(nj, nfmax) + CALL MAKESURF_MESH(isurf) + CALL PUSHCONTROL1B(0) + ELSE + CALL PUSHREAL8ARRAY(chord, nsmax) + CALL PUSHINTEGER4ARRAY(nvstrp, nsmax) + CALL PUSHINTEGER4ARRAY(ijfrst, nsmax) + CALL PUSHINTEGER4ARRAY(nvs, nfmax) + CALL PUSHINTEGER4ARRAY(nvc, nfmax) + CALL PUSHINTEGER4ARRAY(jfrst, nfmax) + CALL PUSHINTEGER4ARRAY(nj, nfmax) + CALL MAKESURF(isurf) + CALL PUSHCONTROL1B(1) + END IF IF (ldupl(isurf)) THEN IF (lverbose) WRITE(*, *) ' reduplicating ', isurf CALL PUSHREAL8ARRAY(vrefl, nsmax*ndmax) CALL PUSHINTEGER4ARRAY(nvstrp, nsmax) CALL PUSHINTEGER4ARRAY(ijfrst, nsmax) + CALL PUSHBOOLEANARRAY(lmeshflat, nfmax) + CALL PUSHBOOLEANARRAY(lsurfmsh, nfmax) CALL PUSHINTEGER4ARRAY(nvs, nfmax) CALL PUSHINTEGER4ARRAY(nvc, nfmax) CALL PUSHBOOLEANARRAY(lsurfspacing, nfmax) @@ -122,6 +154,11 @@ SUBROUTINE UPDATE_SURFACES_B() claf_diff(ii2, ii1) = 0.D0 ENDDO ENDDO + DO ii1=1,4*nvmax + DO ii2=1,3 + mshblk_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO DO ii=nsurf-nsurfdupl,1,-1 CALL POPINTEGER4(isurf) CALL POPCONTROL1B(branch) @@ -136,19 +173,44 @@ SUBROUTINE UPDATE_SURFACES_B() CALL POPBOOLEANARRAY(lsurfspacing, nfmax) CALL POPINTEGER4ARRAY(nvc, nfmax) CALL POPINTEGER4ARRAY(nvs, nfmax) + CALL POPBOOLEANARRAY(lsurfmsh, nfmax) + CALL POPBOOLEANARRAY(lmeshflat, nfmax) CALL POPINTEGER4ARRAY(ijfrst, nsmax) CALL POPINTEGER4ARRAY(nvstrp, nsmax) CALL POPREAL8ARRAY(vrefl, nsmax*ndmax) CALL SDUPL_B(isurf, ydupl(isurf), 'ydup') END IF - CALL POPINTEGER4ARRAY(nj, nfmax) - CALL POPINTEGER4ARRAY(jfrst, nfmax) - CALL POPINTEGER4ARRAY(nvc, nfmax) - CALL POPINTEGER4ARRAY(nvs, nfmax) - CALL POPINTEGER4ARRAY(ijfrst, nsmax) - CALL POPINTEGER4ARRAY(nvstrp, nsmax) - CALL POPREAL8ARRAY(chord, nsmax) - CALL MAKESURF_B(isurf) + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + CALL POPINTEGER4ARRAY(nj, nfmax) + CALL POPINTEGER4ARRAY(jfrst, nfmax) + CALL POPINTEGER4ARRAY(nvc, nfmax) + CALL POPINTEGER4ARRAY(nvs, nfmax) + CALL POPINTEGER4ARRAY(ijfrst, nsmax) + CALL POPINTEGER4ARRAY(nvstrp, nsmax) + CALL POPREAL8ARRAY(rle, 3*nsmax) + CALL POPREAL8ARRAY(chord, nsmax) + CALL POPREAL8ARRAY(rle1, 3*nsmax) + CALL POPREAL8ARRAY(chord1, nsmax) + CALL POPREAL8ARRAY(rle2, 3*nsmax) + CALL POPREAL8ARRAY(chord2, nsmax) + CALL POPREAL8ARRAY(rv, 3*nvmax) + CALL POPREAL8ARRAY(rc, 3*nvmax) + CALL POPREAL8ARRAY(dxv, nvmax) + CALL MAKESURF_MESH_B(isurf) + ELSE + CALL POPINTEGER4ARRAY(nj, nfmax) + CALL POPINTEGER4ARRAY(jfrst, nfmax) + CALL POPINTEGER4ARRAY(nvc, nfmax) + CALL POPINTEGER4ARRAY(nvs, nfmax) + CALL POPINTEGER4ARRAY(ijfrst, nsmax) + CALL POPINTEGER4ARRAY(nvstrp, nsmax) + CALL POPREAL8ARRAY(chord, nsmax) + CALL MAKESURF_B(isurf) + END IF + ENDDO + DO ii=nsurf,1,-1 + CALL POPCONTROL1B(branch) ENDDO END @@ -508,6 +570,8 @@ SUBROUTINE MAKESURF_B(isurf) ad_count0 = 1 C C----- fudge spacing array to make nodes match up exactly with interior sections +C Throws an error in the case where the same node is the closest node +C to two consecutive sections DO isec=2,nsec(isurf)-1 CALL PUSHINTEGER4(ipt1) ipt1 = iptloc(isec-1) @@ -2159,173 +2223,1904 @@ SUBROUTINE MAKESURF_B(isurf) + ) END -C Differentiation of sdupl in reverse (adjoint) mode (with options i4 dr8 r8): -C gradient of useful results: rle chord rle1 chord1 rle2 -C chord2 wstrip ainc ainc_g rv1 rv2 rv rc dxv chordv -C slopev slopec dcontrol vhinge -C with respect to varying inputs: rle chord rle1 chord1 rle2 -C chord2 wstrip ainc ainc_g rv1 rv2 rv rc dxv chordv -C slopev slopec dcontrol vhinge -C - SUBROUTINE SDUPL_B(nn, ypt, msg) +C Differentiation of makesurf_mesh in reverse (adjoint) mode (with options i4 dr8 r8): +C gradient of useful results: xyzscal xyztran addinc aincs +C xasec sasec claf mshblk rv1msh rv2msh rvmsh rcmsh +C rle chord rle1 chord1 rle2 chord2 wstrip ainc +C ainc_g rv1 rv2 rv rc rs dxv chordv slopev slopec +C dcontrol vhinge +C with respect to varying inputs: xyzscal xyztran addinc aincs +C xasec sasec claf mshblk rv1msh rv2msh rvmsh rcmsh +C rle chord rle1 chord1 rle2 chord2 wstrip ainc +C ainc_g rv1 rv2 rv rc rs dxv chordv slopev slopec +C dcontrol vhinge +C + SUBROUTINE MAKESURF_MESH_B(isurf) INCLUDE 'AVL.INC' INCLUDE 'AVL_ad_seeds.inc' - CHARACTER*(*) msg - INTEGER idx_vor - INTEGER nni - INTEGER klen - INTRINSIC LEN - INTEGER k +C working variables (AVL original) + INTEGER isurf + INTEGER kcmax + INTEGER ksmax + PARAMETER (kcmax=50, ksmax=500) + REAL chsin, chcos, chsinl, chsinr, chcosl, chcosr, aincl, aincr, + + chordl, chordr, clafl, clafr, slopel, sloper, dxdx, zu_l, + + zl_l, zu_r, zl_r, zl, zr, sum, wtot, astrp + REAL chsin_diff, chcos_diff, chsinl_diff, chsinr_diff, chcosl_diff + + , chcosr_diff, aincl_diff, aincr_diff, chordl_diff, + + chordr_diff, clafl_diff, clafr_diff, slopel_diff, sloper_diff + REAL chsinl_g(ngmax), chcosl_g(ngmax), chsinr_g(ngmax), chcosr_g( + + ngmax), xled(ndmax), xted(ndmax), gainda(ndmax) + REAL chsinl_g_diff(ngmax), chcosl_g_diff(ngmax), chsinr_g_diff( + + ngmax), chcosr_g_diff(ngmax), xled_diff(ndmax), xted_diff( + + ndmax) +C working variables (OptVL additions) + INTEGER isconl(ndmax), isconr(ndmax) + REAL m1, m2, m3, f1, f2, fc, dc1, dc2, dc, a1, a2, a3, xptxind1, + + xptxind2 + REAL m2_diff, m3_diff, dc1_diff, dc2_diff, a1_diff, a2_diff, + + a3_diff + REAL mesh_surf(3, (nvc(isurf)+1)*(nvs(isurf)+1)) + REAL mesh_surf_diff(3, (nvc(isurf)+1)*(nvs(isurf)+1)) +C functions + INTEGER idx_vor, idx_strip, idx_sec, idx_dim, idx_coef, idx_x, + + idx_node, idx_nodel, idx_noder, idx_node_yp1, idx_node_nx + + , idx_node_nx_yp1, idx_y, nx, ny +C +C Get data from common block + INTEGER FLATIDX INTEGER isec - INTEGER idup - INTEGER iorg - REAL yoff - INTEGER idx_strip - INTEGER ivs - INTEGER jji - INTEGER jj - INTEGER n - INTEGER l - INTEGER ivc - INTEGER iii INTEGER ii - REAL rsgn - REAL(kind=avl_real) tmp - REAL(kind=avl_real) tmp0 - REAL(kind=avl_real) tmp1 - REAL(kind=avl_real) tmp_diff - REAL(kind=avl_real) tmp2 - REAL(kind=avl_real) tmp_diff0 - REAL(kind=avl_real) tmp3 - REAL(kind=avl_real) tmp_diff1 - REAL(kind=avl_real) tmp4 - REAL(kind=avl_real) tmp_diff2 - REAL(kind=avl_real) tmp5 - REAL(kind=avl_real) tmp_diff3 - REAL(kind=avl_real) tmp6 - REAL(kind=avl_real) tmp7 - REAL(kind=avl_real) tmp_diff4 - REAL(kind=avl_real) tmp8 - REAL(kind=avl_real) tmp_diff5 - REAL(kind=avl_real) tmp9 - REAL(kind=avl_real) tmp10 - REAL(kind=avl_real) tmp_diff6 - REAL(kind=avl_real) tmp11 - REAL(kind=avl_real) tmp_diff7 - REAL(kind=avl_real) tmp12 - REAL(kind=avl_real) tmp_diff8 - REAL(kind=avl_real) tmp13 - REAL(kind=avl_real) tmp14 - REAL(kind=avl_real) tmp15 - REAL(kind=avl_real) tmp16 - REAL(kind=avl_real) tmp17 - REAL(kind=avl_real) tmp_diff9 - REAL(kind=avl_real) tmp18 - REAL(kind=avl_real) tmp_diff10 - REAL(kind=avl_real) tmp19 - REAL(kind=avl_real) tmp_diff11 - REAL(kind=avl_real) tmp20 - REAL(kind=avl_real) tmp_diff12 - REAL(kind=avl_real) tmp21 - REAL(kind=avl_real) tmp_diff13 - REAL(kind=avl_real) tmp22 - REAL(kind=avl_real) tmp_diff14 - REAL(kind=avl_real) tmp23 - REAL(kind=avl_real) tmp_diff15 - REAL(kind=avl_real) tmp24 - REAL(kind=avl_real) tmp_diff16 - REAL(kind=avl_real) tmp25 - REAL(kind=avl_real) tmp_diff17 - REAL(kind=avl_real) tmp26 - REAL(kind=avl_real) tmp_diff18 - REAL(kind=avl_real) tmp27 - REAL(kind=avl_real) tmp_diff19 - INTEGER ad_count - INTEGER i - INTEGER branch + INTEGER ispan + INTEGER iptl + INTEGER iptr + INTRINSIC SQRT + INTRINSIC SIN + INTRINSIC COS + INTEGER n + INTEGER iscon + INTEGER isdes + INTRINSIC ATAN2 + REAL chsin_g + REAL chsin_g_diff + REAL chcos_g + REAL chcos_g_diff + INTEGER icl + INTEGER icr + REAL xhd + REAL xhd_diff + REAL vhx + REAL vhx_diff + REAL vhy + REAL vhy_diff + REAL vhz + REAL vhz_diff + REAL vsq + REAL vsq_diff + INTRINSIC ABS + REAL vmod + REAL vmod_diff + INTEGER nsl + INTEGER nsr + REAL clafc + REAL clafc_diff + REAL dx1 + REAL dx1_diff + REAL dx2 + REAL dx2_diff + REAL dx3 + REAL dx3_diff + REAL dsdx + REAL xpt + REAL xpt_diff + REAL fracle + REAL fracle_diff + REAL fracte + REAL fracte_diff + INTRINSIC MAX + INTRINSIC MIN + REAL zu + INTEGER jj + INTEGER j + REAL y1 + REAL y1_diff + REAL y2 + REAL y2_diff + REAL(kind=avl_real) abs0 + REAL(kind=avl_real) abs0_diff + REAL(kind=avl_real) abs1 + REAL(kind=avl_real) abs1_diff + REAL(kind=avl_real) arg1 + REAL(kind=avl_real) arg1_diff + REAL temp + REAL temp0 + REAL temp1 + REAL temp_diff + REAL temp_diff0 + REAL temp_diff1 + REAL(kind=8) temp2 + REAL(kind=8) temp_diff2 + REAL(kind=8) temp3 + REAL(kind=8) temp_diff3 + REAL(kind=8) temp4 + REAL(kind=8) temp_diff4 + REAL(kind=avl_real) temp5 + REAL(kind=avl_real) temp_diff5 INTEGER ad_to INTEGER ad_to0 INTEGER ad_to1 - INTEGER ad_count0 - INTEGER i0 - INTEGER ii3 - INTEGER ii2 - INTEGER ii1 - INTEGER nn - REAL ypt -C -C - nni = nn + 1 - IF (nni .GT. nfmax) THEN - STOP - ELSE -C - klen = LEN(stitle(nn)) - ad_count = 1 - DO k=klen,1,-1 - IF (stitle(nn)(k:k) .NE. ' ') THEN - GOTO 100 - ELSE - ad_count = ad_count + 1 - END IF - ENDDO - CALL PUSHCONTROL1B(0) - CALL PUSHINTEGER4(ad_count) - CALL PUSHINTEGER4(ivs) - CALL PUSHCONTROL1B(0) - GOTO 110 - 100 CALL PUSHCONTROL1B(1) - CALL PUSHINTEGER4(ad_count) - CALL PUSHINTEGER4(ivs) - CALL PUSHCONTROL1B(0) -C -C---- duplicate surface is assumed to be the same logical component surface + INTEGER branch + INTEGER ad_to2 + INTEGER ad_to3 + INTEGER ad_to4 C -C---- same various logical flags -C IFRST(NNI) = NVOR + 1 + nx = nvc(isurf) + 1 +C Check MFRST + ny = nvs(isurf) + 1 +C Get the mesh for this surface from the the common block C -C---- accumulate stuff for new image surface -C JFRST(NNI) = NSTRIP + 1 - 110 jfrst(nni) = jfrst(nni-1) + nj(nni-1) - nj(nni) = nj(nn) - nk(nni) = nk(nn) C - nvc(nni) = nk(nni) - nvs(nni) = nj(nni) +C Perform input checks from makesurf (section check removed) + mesh_surf = mshblk(:, mfrst(isurf):mfrst(isurf)+nx*ny-1) C -C--- Note hinge axis is flipped to reverse the Y component of the hinge -C vector. This means that deflections need to be reversed for image -C surfaces. C -C--- Image flag reversed (set to -IMAGS) for imaged surfaces + IF (nvc(isurf) .GT. kcmax) nvc(isurf) = kcmax +C Set NK from input data (python layer will ensure this is consistent) C -Cc#ifdef USE_CPOML -Cc#endif + IF (isurf .EQ. 1) THEN + jfrst(isurf) = 1 + ELSE + jfrst(isurf) = jfrst(isurf-1) + nj(isurf-1) + END IF +C We need to start counting strips now since it is a global count +C +C Bypass the entire spanwise node generation routine and go straight to store counters +C skips MAKESURF 94-234 +C Index of first strip in surface +C This is normally used to store the index of each section in AVL +C but since we use strips now each is effectively just a section +C We assign this variable accordingly so as not to break anything else + idx_strip = jfrst(isurf) +C Number of strips/sections in surface +C +C Apply the scaling and translations to the mesh as a whole +C +C + DO idx_y=1,ny + DO idx_x=1,nx + DO idx_dim=1,3 + CALL PUSHINTEGER4(idx_node) + idx_node = FLATIDX(idx_x, idx_y, isurf) + CALL PUSHREAL8(mesh_surf(idx_dim, idx_node)) + mesh_surf(idx_dim, idx_node) = xyzscal(idx_dim, isurf)* + + mesh_surf(idx_dim, idx_node) + xyztran(idx_dim, isurf) + ENDDO + ENDDO + CALL PUSHINTEGER4(idx_x - 1) + ENDDO + CALL PUSHINTEGER4(idx_y - 1) +C Setup the strips +C Set spanwise elements to 0 C C -C--- Create image strips, to maintain the same sense of positive GAMMA -C these have the 1 and 2 strip edges reversed (i.e. root is edge 2, -C not edge 1 as for a strip with IMAGS=1 - idx_strip = jfrst(nni) -C NSTRIP = NSTRIP + 1 - DO ivs=1,nvs(nni) - IF (idx_strip .GT. nsmax) THEN - GOTO 130 - ELSE C - jji = jfrst(nni) + ivs - 1 - jj = jfrst(nn) + ivs - 1 +C Check control and design vars C -Cc#ifdef USE_CPOML + IF (ncontrol .GT. ndmax) THEN + STOP + ELSE IF (ndesign .GT. ngmax) THEN +C Instead of looping over sections just loop over all strips in the surface C -Cc#endif + STOP + ELSE C - n = ndesign + 1 - CALL PUSHINTEGER4(n - 1) +Cispan loop +C Set reference information for the strip +C This code was used in the original to loop over strips in a section. +C We will just reuse the variables here + DO ispan=1,ny-1 +C +C + CALL PUSHINTEGER4(idx_y) + idx_y = idx_strip - jfrst(isurf) + 1 + CALL PUSHINTEGER4(iptl) + iptl = idx_y + CALL PUSHINTEGER4(iptr) + iptr = idx_y + 1 +C We need to compute the chord and claf values at the left and right edge of the strip +C This code was used in the original to interpolate over sections. +C We will just reuse here to interpolate over a strip which is trivial but avoids pointless code rewrites. +C +C + CALL PUSHINTEGER4(idx_node) + idx_node = FLATIDX(1, iptl, isurf) + CALL PUSHINTEGER4(idx_node_nx) + idx_node_nx = FLATIDX(nx, iptl, isurf) + CALL PUSHREAL8(chordl) + chordl = SQRT((mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node + + ))**2 + (mesh_surf(3, idx_node_nx)-mesh_surf(3, idx_node))** + + 2) + CALL PUSHINTEGER4(idx_node) + idx_node = FLATIDX(1, iptr, isurf) + CALL PUSHINTEGER4(idx_node_nx) + idx_node_nx = FLATIDX(nx, iptr, isurf) + CALL PUSHREAL8(chordr) + chordr = SQRT((mesh_surf(1, idx_node_nx)-mesh_surf(1, idx_node + + ))**2 + (mesh_surf(3, idx_node_nx)-mesh_surf(3, idx_node))** + + 2) + clafl = claf(iptl, isurf) +C Linearly interpolate the incidence projections over the STRIP + clafr = claf(iptr, isurf) +C + aincl = aincs(iptl, isurf)*dtr + addinc(isurf)*dtr + aincr = aincs(iptr, isurf)*dtr + addinc(isurf)*dtr + chsinl = chordl*SIN(aincl) + chsinr = chordr*SIN(aincr) + chcosl = chordl*COS(aincl) +C We need to determine which controls belong to this section +C Bring over the routine for this from makesurf but do it for each strip now + chcosr = chordr*COS(aincr) C - DO n=1,ncontrol - tmp9 = vrefl(jj, n) - vrefl(jji, n) = tmp9 + DO n=1,ncontrol + isconl(n) = 0 + isconr(n) = 0 + DO iscon=1,nscon(iptl, isurf) + IF (icontd(iscon, iptl, isurf) .EQ. n) THEN + CALL PUSHCONTROL1B(1) + isconl(n) = iscon + ELSE + CALL PUSHCONTROL1B(0) + END IF + ENDDO + CALL PUSHINTEGER4(iscon - 1) + DO iscon=1,nscon(iptr, isurf) + IF (icontd(iscon, iptr, isurf) .EQ. n) THEN + CALL PUSHCONTROL1B(1) + isconr(n) = iscon + ELSE + CALL PUSHCONTROL1B(0) + END IF + ENDDO + CALL PUSHINTEGER4(iscon - 1) + ENDDO +C We need to determine which dvs belong to this strip +C and setup the chord projection gains +C Bring over the routine for this from makesurf but setup for strips +C + DO n=1,ndesign + chsinl_g(n) = 0. + chsinr_g(n) = 0. + chcosl_g(n) = 0. + chcosr_g(n) = 0. +C + DO isdes=1,nsdes(iptl, isurf) + IF (idestd(isdes, iptl, isurf) .EQ. n) THEN + chsinl_g(n) = chcosl*gaing(isdes, iptl, isurf)*dtr + chcosl_g(n) = -(chsinl*gaing(isdes, iptl, isurf)*dtr) + CALL PUSHCONTROL1B(1) + ELSE + CALL PUSHCONTROL1B(0) + END IF + ENDDO + CALL PUSHINTEGER4(isdes - 1) +C + DO isdes=1,nsdes(iptr, isurf) + IF (idestd(isdes, iptr, isurf) .EQ. n) THEN + chsinr_g(n) = chcosr*gaing(isdes, iptr, isurf)*dtr + chcosr_g(n) = -(chsinr*gaing(isdes, iptr, isurf)*dtr) + CALL PUSHCONTROL1B(1) + ELSE + CALL PUSHCONTROL1B(0) + END IF + ENDDO + CALL PUSHINTEGER4(isdes - 1) + ENDDO +C Set the strip geometry data +C Note these computations assume the mesh is not necessarily planar +C ultimately if/when we flatten the mesh into a planar one we will want +C to use the leading edge positions and chords from the original input mesh +C Strip left side +C +C +C + CALL PUSHINTEGER4(idx_node) + idx_node = FLATIDX(1, idx_y, isurf) + CALL PUSHINTEGER4(idx_node_nx) + idx_node_nx = FLATIDX(nx, idx_y, isurf) + DO idx_dim=1,3 + CALL PUSHREAL8(rle1(idx_dim, idx_strip)) + rle1(idx_dim, idx_strip) = mesh_surf(idx_dim, idx_node) + ENDDO +C +C Strip right side + chord1(idx_strip) = SQRT((mesh_surf(1, idx_node_nx)-mesh_surf( + + 1, idx_node))**2 + (mesh_surf(3, idx_node_nx)-mesh_surf(3, + + idx_node))**2) +C + CALL PUSHINTEGER4(idx_node_yp1) + idx_node_yp1 = FLATIDX(1, idx_y + 1, isurf) + CALL PUSHINTEGER4(idx_node_nx_yp1) + idx_node_nx_yp1 = FLATIDX(nx, idx_y + 1, isurf) + DO idx_dim=1,3 + CALL PUSHREAL8(rle2(idx_dim, idx_strip)) + rle2(idx_dim, idx_strip) = mesh_surf(idx_dim, idx_node_yp1) + ENDDO +C Strip mid-point + chord2(idx_strip) = SQRT((mesh_surf(1, idx_node_nx_yp1)- + + mesh_surf(1, idx_node_yp1))**2 + (mesh_surf(3, + + idx_node_nx_yp1)-mesh_surf(3, idx_node_yp1))**2) +C +C Since the strips are linear SPANWISE we can just interpolate + DO idx_dim=1,3 + CALL PUSHREAL8(rle(idx_dim, idx_strip)) + rle(idx_dim, idx_strip) = (rle1(idx_dim, idx_strip)+rle2( + + idx_dim, idx_strip))/2. + ENDDO +C The strips are not necessarily linear chord wise but by definition the chord value is +C so we can interpolate +C Strip geometric incidence angle at the mid-point +C This is strip incidence angle is computed from the LE and TE points +C of the given geometry and is completely independent of AINC +C This quantity is needed to correctly handle nonplanar meshes and is only needed if the mesh isnt flattened + CALL PUSHREAL8(chord(idx_strip)) + chord(idx_strip) = (chord1(idx_strip)+chord2(idx_strip))/2. +C +C Strip width +C +C Strip LE and TE sweep slopes +C +C Compute chord projections and strip twists +C In AVL the AINCS are not interpolated. The chord projections are +C So we have to replicate this effect. +C LINEAR interpolation over the strip: left, right, and midpoint +C +C + CALL PUSHINTEGER4(idx_nodel) + idx_nodel = FLATIDX(1, iptl, isurf) +C f1 = (mesh_surf(2,idx_node)-mesh_surf(2,idx_nodel))/ +C & (mesh_surf(2,idx_noder)-mesh_surf(2,idx_nodel)) +C f2 = (mesh_surf(2,idx_node_yp1)-mesh_surf(2,idx_nodel))/ +C & (mesh_surf(2,idx_noder)-mesh_surf(2,idx_nodel)) +C fc = (((mesh_surf(2,idx_node_yp1)+mesh_surf(2,idx_node))/2.) +C & -mesh_surf(2,idx_nodel))/(mesh_surf(2,idx_noder) +C & -mesh_surf(2,idx_nodel)) +C the above expressions will always evaluate to the following for individual strips + CALL PUSHINTEGER4(idx_noder) + idx_noder = FLATIDX(1, iptr, isurf) +C +C +C Strip left side incidence +C CHSIN = CHSINL + f1*(CHSINR-CHSINL) +C CHCOS = CHCOSL + f1*(CHCOSR-CHCOSL) + fc = 0.5 +C +C Strip right side incidence +C CHSIN = CHSINL + f2*(CHSINR-CHSINL) +C CHCOS = CHCOSL + f2*(CHCOSR-CHCOSL) +C +C Strip mid-point incidence +C + CALL PUSHREAL8(chsin) + chsin = chsinl + fc*(chsinr-chsinl) + CALL PUSHREAL8(chcos) + chcos = chcosl + fc*(chcosr-chcosl) +C Set dv gains for incidence angles +C Bring over the routine for this from make surf +C + DO n=1,ndesign + CALL PUSHREAL8(chsin_g) + chsin_g = (1.0-fc)*chsinl_g(n) + fc*chsinr_g(n) + CALL PUSHREAL8(chcos_g) + chcos_g = (1.0-fc)*chcosl_g(n) + fc*chcosr_g(n) + ENDDO +C We have to now setup any control surfaces we defined for this strip +C Bring over the routine for this from makesurf but modified for a strip +C + DO n=1,ncontrol + CALL PUSHINTEGER4(icl) + icl = isconl(n) + CALL PUSHINTEGER4(icr) + icr = isconr(n) +C + IF (icl .EQ. 0 .OR. icr .EQ. 0) THEN +C no control effect here + CALL PUSHREAL8(gainda(n)) + gainda(n) = 0. + CALL PUSHREAL8(xled(n)) + xled(n) = 0. + CALL PUSHREAL8(xted(n)) + xted(n) = 0. +C +C +C +C + CALL PUSHCONTROL1B(1) + ELSE +C control variable # N is active here +C SAB Note: This interpolation ensures that the hinge line is +C is linear which I think it is an ok assumption for arbitrary wings as long as the user is aware +C A curve hinge line could work if needed if we just interpolate XHINGED and scaled by local chord + CALL PUSHREAL8(gainda(n)) + gainda(n) = gaind(icl, iptl, isurf)*(1.0-fc) + gaind(icr, + + iptr, isurf)*fc +C + xhd = chordl*xhinged(icl, iptl, isurf)*(1.0-fc) + chordr* + + xhinged(icr, iptr, isurf)*fc + IF (xhd .GE. 0.0) THEN +C TE control surface, with hinge at XHD + CALL PUSHREAL8(xled(n)) + xled(n) = xhd + CALL PUSHREAL8(xted(n)) + xted(n) = chord(idx_strip) + CALL PUSHCONTROL1B(0) + ELSE +C LE control surface, with hinge at -XHD + CALL PUSHREAL8(xled(n)) + xled(n) = 0.0 + CALL PUSHREAL8(xted(n)) + xted(n) = -xhd + CALL PUSHCONTROL1B(1) + END IF +C + CALL PUSHREAL8(vhx) + vhx = vhinged(1, icl, iptl, isurf)*xyzscal(1, isurf) + CALL PUSHREAL8(vhy) + vhy = vhinged(2, icl, iptl, isurf)*xyzscal(2, isurf) + CALL PUSHREAL8(vhz) + vhz = vhinged(3, icl, iptl, isurf)*xyzscal(3, isurf) + CALL PUSHREAL8(vsq) + vsq = vhx**2 + vhy**2 + vhz**2 + IF (vsq .EQ. 0.0) THEN + IF (chordr*xhinged(icr, iptr, isurf) .GE. 0.) THEN + abs0 = chordr*xhinged(icr, iptr, isurf) + CALL PUSHCONTROL1B(1) + ELSE + abs0 = -(chordr*xhinged(icr, iptr, isurf)) + CALL PUSHCONTROL1B(0) + END IF + IF (chordl*xhinged(icl, iptl, isurf) .GE. 0.) THEN + abs1 = chordl*xhinged(icl, iptl, isurf) + CALL PUSHCONTROL1B(0) + ELSE + abs1 = -(chordl*xhinged(icl, iptl, isurf)) + CALL PUSHCONTROL1B(1) + END IF +C default: set hinge vector along hingeline +C We are just setting the hinge line across the section +C this assumes the hinge is linear even for a nonlinear wing + vhx = mesh_surf(1, idx_noder) + abs0 - mesh_surf(1, + + idx_nodel) - abs1 + vhy = mesh_surf(2, idx_noder) - mesh_surf(2, idx_nodel) + vhz = mesh_surf(3, idx_noder) - mesh_surf(3, idx_nodel) + CALL PUSHREAL8(vhx) + vhx = vhx*xyzscal(1, isurf) + CALL PUSHREAL8(vhy) + vhy = vhy*xyzscal(2, isurf) + CALL PUSHREAL8(vhz) + vhz = vhz*xyzscal(3, isurf) + vsq = vhx**2 + vhy**2 + vhz**2 + CALL PUSHCONTROL1B(0) + ELSE + CALL PUSHCONTROL1B(1) + END IF +C + CALL PUSHREAL8(vmod) + vmod = SQRT(vsq) +C +C + CALL PUSHCONTROL1B(0) + END IF + ENDDO +C If the min drag is zero flag the strip as no-viscous data +C Set the panel (vortex) geometry data +C Accumulate the strip element indicies and start counting vorticies +C + IF (idx_strip .EQ. 1) THEN + ijfrst(idx_strip) = 1 + ELSE + ijfrst(idx_strip) = ijfrst(idx_strip-1) + nvstrp(idx_strip-1 + + ) + END IF + idx_vor = ijfrst(idx_strip) +C Associate the strip with the surface + nvstrp(idx_strip) = nvc(isurf) +C +C Prepare for cross section interpolation +C + nsl = nasec(iptl, isurf) +C CHORDC = CHORD(idx_strip) +C Funny story. this original line is now valid now that we interpolate over the strip + nsr = nasec(iptr, isurf) +C +C +C +C Suggestion from Hal Yougren for non linear sections: +C clafc = (1.-fc)*clafl + fc*clafr +C loop over vorticies for the strip + CALL PUSHREAL8(clafc) + clafc = (1.-fc)*(chordl/chord(idx_strip))*clafl + fc*(chordr/ + + chord(idx_strip))*clafr +C +C Left bound vortex points + DO idx_x=1,nvc(isurf) +C Compute the panel left side chord + CALL PUSHINTEGER4(idx_node) + idx_node = FLATIDX(idx_x, idx_y, isurf) + CALL PUSHREAL8(dc1) + dc1 = SQRT((mesh_surf(1, idx_node+1)-mesh_surf(1, idx_node)) + + **2 + (mesh_surf(3, idx_node+1)-mesh_surf(3, idx_node))**2 + + ) +C Right bound vortex points +C + IF (lmeshflat(isurf)) THEN +C Place vortex at panel quarter chord of the flat mesh +C Compute the panel left side angle +C Place vortex at panel quarter chord of the true mesh + CALL PUSHREAL8(a1) + a1 = ATAN2(mesh_surf(3, idx_node+1) - mesh_surf(3, + + idx_node), mesh_surf(1, idx_node+1) - mesh_surf(1, + + idx_node)) + CALL PUSHCONTROL1B(0) + ELSE +C Compute the panel left side angle +C Place vortex at panel quarter chord + CALL PUSHREAL8(a1) + a1 = ATAN2(mesh_surf(3, idx_node+1) - mesh_surf(3, + + idx_node), mesh_surf(1, idx_node+1) - mesh_surf(1, + + idx_node)) +C Make a copy in the true mesh array for post processing + CALL PUSHCONTROL1B(1) + END IF +C Compute the panel right side chord + CALL PUSHINTEGER4(idx_node_yp1) + idx_node_yp1 = FLATIDX(idx_x, idx_y + 1, isurf) + CALL PUSHREAL8(dc2) + dc2 = SQRT((mesh_surf(1, idx_node_yp1+1)-mesh_surf(1, + + idx_node_yp1))**2 + (mesh_surf(3, idx_node_yp1+1)- + + mesh_surf(3, idx_node_yp1))**2) +C Mid-point bound vortex points +C Compute the panel mid-point chord +C Panels themselves can never be curved so just interpolate the chord +C store as the panel chord in common block +C + IF (lmeshflat(isurf)) THEN +C Place vortex at panel quarter chord of the flat mesh +C +C Compute the panel right side angle +C +C Place vortex at panel quarter chord of the true mesh + CALL PUSHREAL8(a2) + a2 = ATAN2(mesh_surf(3, idx_node_yp1+1) - mesh_surf(3, + + idx_node_yp1), mesh_surf(1, idx_node_yp1+1) - mesh_surf( + + 1, idx_node_yp1)) + CALL PUSHCONTROL1B(0) + ELSE +C Compute the panel right side angle +C Place vortex at panel quarter chord + CALL PUSHREAL8(a2) + a2 = ATAN2(mesh_surf(3, idx_node_yp1+1) - mesh_surf(3, + + idx_node_yp1), mesh_surf(1, idx_node_yp1+1) - mesh_surf( + + 1, idx_node_yp1)) +C Make a copy in the true mesh array for post processing +C + CALL PUSHCONTROL1B(1) + END IF +C +C We need to compute the midpoint angle and panel strip chord projection +C as we need them to compute normals based on the real mesh + CALL PUSHREAL8(dxv(idx_vor)) + dxv(idx_vor) = (dc1+dc2)/2. +C project the panel chord onto the strip chord + CALL PUSHREAL8(a3) + a3 = ATAN2((mesh_surf(3, idx_node_yp1+1)+mesh_surf(3, + + idx_node+1))/2. - (mesh_surf(3, idx_node_yp1)+mesh_surf(3 + + , idx_node))/2., (mesh_surf(1, idx_node_yp1+1)+mesh_surf(1 + + , idx_node+1))/2. - (mesh_surf(1, idx_node_yp1)+mesh_surf( + + 1, idx_node))/2.) +C Panel Control points +C Y- point +C is just the panel midpoint +C + IF (lmeshflat(isurf)) THEN +C Place vortex at panel quarter chord of the flat mesh + dx3 = SQRT(((mesh_surf(1, idx_node_yp1)+mesh_surf(1, + + idx_node))/2-rle(1, idx_strip))**2 + ((mesh_surf(3, + + idx_node_yp1)+mesh_surf(3, idx_node))/2-rle(3, idx_strip + + ))**2) + CALL PUSHREAL8(rv(2, idx_vor)) + rv(2, idx_vor) = rle(2, idx_strip) + CALL PUSHREAL8(rv(3, idx_vor)) + rv(3, idx_vor) = rle(3, idx_strip) +C Place vortex at panel quarter chord of the true mesh + CALL PUSHREAL8(rv(1, idx_vor)) + rv(1, idx_vor) = rle(1, idx_strip) + dx3 + dxv(idx_vor)/4. +C + CALL PUSHCONTROL1B(0) + ELSE +C Place vortex at panel quarter chord + CALL PUSHREAL8(rv(2, idx_vor)) + rv(2, idx_vor) = (mesh_surf(2, idx_node_yp1)+mesh_surf(2, + + idx_node))/2. + CALL PUSHREAL8(rv(1, idx_vor)) + rv(1, idx_vor) = (mesh_surf(1, idx_node_yp1)+mesh_surf(1, + + idx_node))/2. + dxv(idx_vor)/4.*COS(a3) +C Make a copy in the true mesh array for post processing + CALL PUSHREAL8(rv(3, idx_vor)) + rv(3, idx_vor) = (mesh_surf(3, idx_node_yp1)+mesh_surf(3, + + idx_node))/2. + dxv(idx_vor)/4.*SIN(a3) +C + CALL PUSHCONTROL1B(1) + END IF +C +C +C Place the control point at the quarter chord + half chord*clafc +C note that clafc is a scaler so is 1. is for 2pi +C use data from vortex mid-point computation + CALL PUSHREAL8(rc(2, idx_vor)) + rc(2, idx_vor) = rv(2, idx_vor) +C Source points +C Y- point + IF (lmeshflat(isurf)) THEN + CALL PUSHREAL8(rc(1, idx_vor)) + rc(1, idx_vor) = rv(1, idx_vor) + clafc*(dxv(idx_vor)/2.) + CALL PUSHREAL8(rc(3, idx_vor)) + rc(3, idx_vor) = rv(3, idx_vor) +C + CALL PUSHCONTROL1B(0) + ELSE + CALL PUSHREAL8(rc(1, idx_vor)) + rc(1, idx_vor) = rv(1, idx_vor) + clafc*(dxv(idx_vor)/2.)* + + COS(a3) +C Make a copy in the true mesh array for post processing + CALL PUSHREAL8(rc(3, idx_vor)) + rc(3, idx_vor) = rv(3, idx_vor) + clafc*(dxv(idx_vor)/2.)* + + SIN(a3) +C + CALL PUSHCONTROL1B(1) + END IF +C +C Place the source point at the half chord +C use data from vortex mid-point computation +C add another quarter chord to the quarter chord +C Set the camber slopes for the panel +C Camber slope at control point + IF (lmeshflat(isurf)) THEN + CALL PUSHCONTROL1B(0) + ELSE + CALL PUSHCONTROL1B(1) + END IF +C +C + arg1 = (rc(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL PUSHREAL8(slopel) + CALL AKIMA(xasec(1, iptl, isurf), sasec(1, iptl, isurf), nsl + + , arg1, slopel, dsdx) + arg1 = (rc(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL PUSHREAL8(sloper) + CALL AKIMA(xasec(1, iptr, isurf), sasec(1, iptr, isurf), nsr + + , arg1, sloper, dsdx) +C Alternative for nonlinear sections per Hal Youngren +C SLOPEC(idx_vor) = (1.-fc)*SLOPEL + fc*SLOPER +C The original line is valid for interpolation over a strip +C +C Camber slope at vortex mid-point +C + arg1 = (rv(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL PUSHREAL8(slopel) + CALL AKIMA(xasec(1, iptl, isurf), sasec(1, iptl, isurf), nsl + + , arg1, slopel, dsdx) + arg1 = (rv(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL PUSHREAL8(sloper) + CALL AKIMA(xasec(1, iptr, isurf), sasec(1, iptr, isurf), nsr + + , arg1, sloper, dsdx) +C Alternative for nonlinear sections per Hal Youngren +C SLOPEV(idx_vor) = (1.-fc)*SLOPEL + fc*SLOPER +C The original line is valid for interpolation over a strip +C +C Associate the panel with strip chord and component +C +C Enforce no penetration at the control point +C element inherits alpha,beta flag from surface +C +C We need to scale the control surface gains by the fraction +C of the element on the control surface +C +Cscale control gain by factor 0..1, (fraction of element on control surface) + DO n=1,ncontrol + xpt = ((mesh_surf(1, idx_node)+mesh_surf(1, idx_node_yp1)) + + /2-rle(1, idx_strip))/chord(idx_strip) +C + fracle = (xled(n)/chord(idx_strip)-xpt)/(dxv(idx_vor)/ + + chord(idx_strip)) +C + fracte = (xted(n)/chord(idx_strip)-xpt)/(dxv(idx_vor)/ + + chord(idx_strip)) + IF (0.0 .LT. fracle) THEN + y1 = fracle + CALL PUSHCONTROL1B(0) + ELSE + y1 = 0.0 + CALL PUSHCONTROL1B(1) + END IF + IF (1.0 .GT. y1) THEN + CALL PUSHCONTROL1B(0) + ELSE + CALL PUSHCONTROL1B(1) + END IF + IF (0.0 .LT. fracte) THEN + y2 = fracte + CALL PUSHCONTROL1B(0) + ELSE + y2 = 0.0 + CALL PUSHCONTROL1B(1) + END IF + IF (1.0 .GT. y2) THEN + CALL PUSHCONTROL1B(0) + ELSE + CALL PUSHCONTROL1B(1) + END IF + ENDDO +C TE control point used only if surface sheds a wake +C Use the cross sections to generate the OML +C nodal grid associated with vortex strip (aft-panel nodes) +C NOTE: airfoil in plane of wing, but not rotated perpendicular to dihedral; +C retained in (x,z) plane at this point +C Store the panel LE mid point for the next panel in the strip +C This gets used a lot here +C We use the original input mesh (true mesh) to compute points for the OML +C +C +C xptxind2 = (mesh_surf(1,idx_node_yp1+1) +C & - RLE2(1,idx_strip))/CHORD2(idx_strip) +C Interpolate cross section on left side +C +C +C Interpolate cross section on right side +C +C Compute the left aft node of panel +C X-point +C +C +C Y-point +C +C Interpolate z from sections to left aft node of panel +C +C Store left aft z-point +C +C Compute the right aft node of panel +C X-point +C +C Y-point +C +C Interpolate z from sections to right aft node of panel +C Store right aft z-point +C +C + CALL PUSHINTEGER4(idx_vor) + idx_vor = idx_vor + 1 + ENDDO +C End vortex loop + CALL PUSHINTEGER4(idx_strip) + idx_strip = idx_strip + 1 + ENDDO + mesh_surf_diff = 0.D0 + chcosl_g_diff = 0.D0 + chsinr_g_diff = 0.D0 + xted_diff = 0.D0 + xled_diff = 0.D0 + chsinl_g_diff = 0.D0 + chcosr_g_diff = 0.D0 + DO ispan=ny-1,1,-1 + CALL POPINTEGER4(idx_strip) + fc = 0.5 + nsl = nasec(iptl, isurf) + nsr = nasec(iptr, isurf) + chordl_diff = 0.D0 + chordr_diff = 0.D0 + clafc_diff = 0.D0 + DO idx_x=nvc(isurf),1,-1 + CALL POPINTEGER4(idx_vor) + DO n=ncontrol,1,-1 + fracte_diff = gainda(n)*dcontrol_diff(idx_vor, n) + fracle_diff = -(gainda(n)*dcontrol_diff(idx_vor, n)) + dcontrol_diff(idx_vor, n) = 0.D0 + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + xpt = ((mesh_surf(1, idx_node)+mesh_surf(1, idx_node_yp1 + + ))/2-rle(1, idx_strip))/chord(idx_strip) + y2_diff = fracte_diff + ELSE + xpt = ((mesh_surf(1, idx_node)+mesh_surf(1, idx_node_yp1 + + ))/2-rle(1, idx_strip))/chord(idx_strip) + y2_diff = 0.D0 + END IF + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + fracte_diff = y2_diff + ELSE + fracte_diff = 0.D0 + END IF + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + y1_diff = fracle_diff + ELSE + y1_diff = 0.D0 + END IF + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + fracle_diff = y1_diff + ELSE + fracle_diff = 0.D0 + END IF + temp5 = chord(idx_strip)/dxv(idx_vor) + temp4 = xted(n)/chord(idx_strip) + temp_diff4 = temp5*fracte_diff/chord(idx_strip) + xpt_diff = -(temp5*fracte_diff) + temp_diff5 = (temp4-xpt)*fracte_diff/dxv(idx_vor) + chord_diff(idx_strip) = chord_diff(idx_strip) + temp_diff5 + + - temp4*temp_diff4 + dxv_diff(idx_vor) = dxv_diff(idx_vor) - temp5*temp_diff5 + xted_diff(n) = xted_diff(n) + temp_diff4 + temp5 = chord(idx_strip)/dxv(idx_vor) + temp4 = xled(n)/chord(idx_strip) + temp_diff4 = temp5*fracle_diff/chord(idx_strip) + xpt_diff = xpt_diff - temp5*fracle_diff + temp_diff5 = (temp4-xpt)*fracle_diff/dxv(idx_vor) + chord_diff(idx_strip) = chord_diff(idx_strip) + temp_diff5 + + - temp4*temp_diff4 + dxv_diff(idx_vor) = dxv_diff(idx_vor) - temp5*temp_diff5 + xled_diff(n) = xled_diff(n) + temp_diff4 + temp_diff4 = xpt_diff/chord(idx_strip) + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) + + + temp_diff4/2 + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) + temp_diff4/2 + rle_diff(1, idx_strip) = rle_diff(1, idx_strip) - + + temp_diff4 + chord_diff(idx_strip) = chord_diff(idx_strip) - (( + + mesh_surf(1, idx_node)+mesh_surf(1, idx_node_yp1))/2-rle + + (1, idx_strip))*temp_diff4/chord(idx_strip) + ENDDO + temp_diff3 = fc*slopev_diff(idx_vor)/chord(idx_strip) + chord_diff(idx_strip) = chord_diff(idx_strip) + chordv_diff( + + idx_vor) - chordr*sloper*temp_diff3/chord(idx_strip) + chordv_diff(idx_vor) = 0.D0 + temp_diff4 = (1.-fc)*slopev_diff(idx_vor)/chord(idx_strip) + slopev_diff(idx_vor) = 0.D0 + chordr_diff = chordr_diff + sloper*temp_diff3 + sloper_diff = chordr*temp_diff3 + chordl_diff = chordl_diff + slopel*temp_diff4 + slopel_diff = chordl*temp_diff4 + arg1 = (rv(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL POPREAL8(sloper) + arg1_diff = 0.D0 + CALL AKIMA_B(xasec(1, iptr, isurf), xasec_diff(1, iptr, + + isurf), sasec(1, iptr, isurf), sasec_diff(1, + + iptr, isurf), nsr, arg1, arg1_diff, sloper, + + sloper_diff, dsdx) + temp_diff5 = arg1_diff/chord(idx_strip) + chord_diff(idx_strip) = chord_diff(idx_strip) - chordl* + + slopel*temp_diff4/chord(idx_strip) - (rv(1, idx_vor)-rle(1 + + , idx_strip))*temp_diff5/chord(idx_strip) + rv_diff(1, idx_vor) = rv_diff(1, idx_vor) + temp_diff5 + rle_diff(1, idx_strip) = rle_diff(1, idx_strip) - temp_diff5 + arg1 = (rv(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL POPREAL8(slopel) + arg1_diff = 0.D0 + CALL AKIMA_B(xasec(1, iptl, isurf), xasec_diff(1, iptl, + + isurf), sasec(1, iptl, isurf), sasec_diff(1, + + iptl, isurf), nsl, arg1, arg1_diff, slopel, + + slopel_diff, dsdx) + temp_diff5 = arg1_diff/chord(idx_strip) + rv_diff(1, idx_vor) = rv_diff(1, idx_vor) + temp_diff5 + rle_diff(1, idx_strip) = rle_diff(1, idx_strip) - temp_diff5 + temp_diff4 = (1.-fc)*slopec_diff(idx_vor)/chord(idx_strip) + temp_diff3 = fc*slopec_diff(idx_vor)/chord(idx_strip) + chord_diff(idx_strip) = chord_diff(idx_strip) - (rv(1, + + idx_vor)-rle(1, idx_strip))*temp_diff5/chord(idx_strip) - + + chordr*sloper*temp_diff3/chord(idx_strip) + slopec_diff(idx_vor) = 0.D0 + chordr_diff = chordr_diff + sloper*temp_diff3 + sloper_diff = chordr*temp_diff3 + chordl_diff = chordl_diff + slopel*temp_diff4 + slopel_diff = chordl*temp_diff4 + arg1 = (rc(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL POPREAL8(sloper) + arg1_diff = 0.D0 + CALL AKIMA_B(xasec(1, iptr, isurf), xasec_diff(1, iptr, + + isurf), sasec(1, iptr, isurf), sasec_diff(1, + + iptr, isurf), nsr, arg1, arg1_diff, sloper, + + sloper_diff, dsdx) + temp_diff5 = arg1_diff/chord(idx_strip) + chord_diff(idx_strip) = chord_diff(idx_strip) - chordl* + + slopel*temp_diff4/chord(idx_strip) - (rc(1, idx_vor)-rle(1 + + , idx_strip))*temp_diff5/chord(idx_strip) + rc_diff(1, idx_vor) = rc_diff(1, idx_vor) + temp_diff5 + rle_diff(1, idx_strip) = rle_diff(1, idx_strip) - temp_diff5 + arg1 = (rc(1, idx_vor)-rle(1, idx_strip))/chord(idx_strip) + CALL POPREAL8(slopel) + arg1_diff = 0.D0 + CALL AKIMA_B(xasec(1, iptl, isurf), xasec_diff(1, iptl, + + isurf), sasec(1, iptl, isurf), sasec_diff(1, + + iptl, isurf), nsl, arg1, arg1_diff, slopel, + + slopel_diff, dsdx) + temp_diff5 = arg1_diff/chord(idx_strip) + rc_diff(1, idx_vor) = rc_diff(1, idx_vor) + temp_diff5 + rle_diff(1, idx_strip) = rle_diff(1, idx_strip) - temp_diff5 + chord_diff(idx_strip) = chord_diff(idx_strip) - (rc(1, + + idx_vor)-rle(1, idx_strip))*temp_diff5/chord(idx_strip) + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + rv_diff(3, idx_vor) = rv_diff(3, idx_vor) + rs_diff(3, + + idx_vor) + dxv_diff(idx_vor) = dxv_diff(idx_vor) + rs_diff(3, idx_vor + + )/4. + rs_diff(1, idx_vor)/4. + rs_diff(3, idx_vor) = 0.D0 + rv_diff(1, idx_vor) = rv_diff(1, idx_vor) + rs_diff(1, + + idx_vor) + rs_diff(1, idx_vor) = 0.D0 + a3_diff = 0.D0 + ELSE + rv_diff(3, idx_vor) = rv_diff(3, idx_vor) + rs_diff(3, + + idx_vor) + dxv_diff(idx_vor) = dxv_diff(idx_vor) + SIN(a3)*rs_diff(3 + + , idx_vor)/4. + COS(a3)*rs_diff(1, idx_vor)/4. + a3_diff = COS(a3)*dxv(idx_vor)*rs_diff(3, idx_vor)/4. - + + SIN(a3)*dxv(idx_vor)*rs_diff(1, idx_vor)/4. + rs_diff(3, idx_vor) = 0.D0 + rv_diff(1, idx_vor) = rv_diff(1, idx_vor) + rs_diff(1, + + idx_vor) + rs_diff(1, idx_vor) = 0.D0 + END IF + rv_diff(2, idx_vor) = rv_diff(2, idx_vor) + rs_diff(2, + + idx_vor) + rs_diff(2, idx_vor) = 0.D0 + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + rvmsh_diff(2, idx_vor) = rvmsh_diff(2, idx_vor) + + + rcmsh_diff(2, idx_vor) + rcmsh_diff(2, idx_vor) = 0.D0 + rvmsh_diff(3, idx_vor) = rvmsh_diff(3, idx_vor) + + + rcmsh_diff(3, idx_vor) + temp_diff4 = SIN(a3)*rcmsh_diff(3, idx_vor) + a3_diff = a3_diff + COS(a3)*clafc*dxv(idx_vor)*rcmsh_diff( + + 3, idx_vor)/2. - SIN(a3)*clafc*dxv(idx_vor)*rcmsh_diff(1 + + , idx_vor)/2. + rcmsh_diff(3, idx_vor) = 0.D0 + clafc_diff = clafc_diff + dxv(idx_vor)*temp_diff4/2. + dxv_diff(idx_vor) = dxv_diff(idx_vor) + clafc*temp_diff4/ + + 2. + rvmsh_diff(1, idx_vor) = rvmsh_diff(1, idx_vor) + + + rcmsh_diff(1, idx_vor) + temp_diff4 = COS(a3)*rcmsh_diff(1, idx_vor) + rcmsh_diff(1, idx_vor) = 0.D0 + clafc_diff = clafc_diff + dxv(idx_vor)*temp_diff4/2. + dxv + + (idx_vor)*rc_diff(1, idx_vor)/2. + dxv_diff(idx_vor) = dxv_diff(idx_vor) + clafc*temp_diff4/ + + 2. + clafc*rc_diff(1, idx_vor)/2. + CALL POPREAL8(rc(3, idx_vor)) + rv_diff(3, idx_vor) = rv_diff(3, idx_vor) + rc_diff(3, + + idx_vor) + rc_diff(3, idx_vor) = 0.D0 + CALL POPREAL8(rc(1, idx_vor)) + rv_diff(1, idx_vor) = rv_diff(1, idx_vor) + rc_diff(1, + + idx_vor) + rc_diff(1, idx_vor) = 0.D0 + ELSE + rc_diff(2, idx_vor) = rc_diff(2, idx_vor) + rcmsh_diff(2, + + idx_vor) + rcmsh_diff(2, idx_vor) = 0.D0 + rc_diff(3, idx_vor) = rc_diff(3, idx_vor) + rcmsh_diff(3, + + idx_vor) + rcmsh_diff(3, idx_vor) = 0.D0 + rc_diff(1, idx_vor) = rc_diff(1, idx_vor) + rcmsh_diff(1, + + idx_vor) + rcmsh_diff(1, idx_vor) = 0.D0 + CALL POPREAL8(rc(3, idx_vor)) + rv_diff(3, idx_vor) = rv_diff(3, idx_vor) + rc_diff(3, + + idx_vor) + temp_diff4 = SIN(a3)*rc_diff(3, idx_vor) + a3_diff = a3_diff + COS(a3)*clafc*dxv(idx_vor)*rc_diff(3, + + idx_vor)/2. - SIN(a3)*clafc*dxv(idx_vor)*rc_diff(1, + + idx_vor)/2. + rc_diff(3, idx_vor) = 0.D0 + clafc_diff = clafc_diff + dxv(idx_vor)*temp_diff4/2. + dxv_diff(idx_vor) = dxv_diff(idx_vor) + clafc*temp_diff4/ + + 2. + CALL POPREAL8(rc(1, idx_vor)) + rv_diff(1, idx_vor) = rv_diff(1, idx_vor) + rc_diff(1, + + idx_vor) + temp_diff4 = COS(a3)*rc_diff(1, idx_vor) + rc_diff(1, idx_vor) = 0.D0 + clafc_diff = clafc_diff + dxv(idx_vor)*temp_diff4/2. + dxv_diff(idx_vor) = dxv_diff(idx_vor) + clafc*temp_diff4/ + + 2. + END IF + CALL POPREAL8(rc(2, idx_vor)) + rv_diff(2, idx_vor) = rv_diff(2, idx_vor) + rc_diff(2, + + idx_vor) + rc_diff(2, idx_vor) = 0.D0 + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + dx3_diff = rv_diff(1, idx_vor) + temp4 = (mesh_surf(3, idx_node_yp1)+mesh_surf(3, idx_node) + + )/2 - rle(3, idx_strip) + temp3 = (mesh_surf(1, idx_node_yp1)+mesh_surf(1, idx_node) + + )/2 - rle(1, idx_strip) + IF (temp3**2 + temp4**2 .EQ. 0.D0) THEN + temp_diff2 = 0.D0 + ELSE + temp_diff2 = dx3_diff/(2.0*SQRT(temp3**2+temp4**2)) + END IF + temp_diff3 = 2*temp3*temp_diff2 + temp_diff4 = 2*temp4*temp_diff2 + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) + rvmsh_diff(3, idx_vor)/2. + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) + + + rvmsh_diff(3, idx_vor)/2. + dxv_diff(idx_vor) = dxv_diff(idx_vor) + SIN(a3)*rvmsh_diff + + (3, idx_vor)/4. + COS(a3)*rvmsh_diff(1, idx_vor)/4. + + + rv_diff(1, idx_vor)/4. + a3_diff = a3_diff + COS(a3)*dxv(idx_vor)*rvmsh_diff(3, + + idx_vor)/4. - SIN(a3)*dxv(idx_vor)*rvmsh_diff(1, idx_vor + + )/4. + rvmsh_diff(3, idx_vor) = 0.D0 + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) + rvmsh_diff(1, idx_vor)/2. + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) + + + rvmsh_diff(1, idx_vor)/2. + rvmsh_diff(1, idx_vor) = 0.D0 + mesh_surf_diff(2, idx_node_yp1) = mesh_surf_diff(2, + + idx_node_yp1) + rvmsh_diff(2, idx_vor)/2. + mesh_surf_diff(2, idx_node) = mesh_surf_diff(2, idx_node) + + + rvmsh_diff(2, idx_vor)/2. + rvmsh_diff(2, idx_vor) = 0.D0 + CALL POPREAL8(rv(1, idx_vor)) + rle_diff(1, idx_strip) = rle_diff(1, idx_strip) + rv_diff( + + 1, idx_vor) - temp_diff3 + rv_diff(1, idx_vor) = 0.D0 + CALL POPREAL8(rv(3, idx_vor)) + rle_diff(3, idx_strip) = rle_diff(3, idx_strip) + rv_diff( + + 3, idx_vor) - temp_diff4 + rv_diff(3, idx_vor) = 0.D0 + CALL POPREAL8(rv(2, idx_vor)) + rle_diff(2, idx_strip) = rle_diff(2, idx_strip) + rv_diff( + + 2, idx_vor) + rv_diff(2, idx_vor) = 0.D0 + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) + temp_diff4/2 + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) + + + temp_diff4/2 + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) + temp_diff3/2 + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) + + + temp_diff3/2 + ELSE + rv_diff(3, idx_vor) = rv_diff(3, idx_vor) + rvmsh_diff(3, + + idx_vor) + rvmsh_diff(3, idx_vor) = 0.D0 + rv_diff(1, idx_vor) = rv_diff(1, idx_vor) + rvmsh_diff(1, + + idx_vor) + rvmsh_diff(1, idx_vor) = 0.D0 + rv_diff(2, idx_vor) = rv_diff(2, idx_vor) + rvmsh_diff(2, + + idx_vor) + rvmsh_diff(2, idx_vor) = 0.D0 + CALL POPREAL8(rv(3, idx_vor)) + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) + rv_diff(3, idx_vor)/2. + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) + + + rv_diff(3, idx_vor)/2. + dxv_diff(idx_vor) = dxv_diff(idx_vor) + SIN(a3)*rv_diff(3 + + , idx_vor)/4. + COS(a3)*rv_diff(1, idx_vor)/4. + a3_diff = a3_diff + COS(a3)*dxv(idx_vor)*rv_diff(3, + + idx_vor)/4. - SIN(a3)*dxv(idx_vor)*rv_diff(1, idx_vor)/ + + 4. + rv_diff(3, idx_vor) = 0.D0 + CALL POPREAL8(rv(1, idx_vor)) + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) + rv_diff(1, idx_vor)/2. + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) + + + rv_diff(1, idx_vor)/2. + rv_diff(1, idx_vor) = 0.D0 + CALL POPREAL8(rv(2, idx_vor)) + mesh_surf_diff(2, idx_node_yp1) = mesh_surf_diff(2, + + idx_node_yp1) + rv_diff(2, idx_vor)/2. + mesh_surf_diff(2, idx_node) = mesh_surf_diff(2, idx_node) + + + rv_diff(2, idx_vor)/2. + rv_diff(2, idx_vor) = 0.D0 + END IF + CALL POPREAL8(a3) + temp1 = (mesh_surf(1, idx_node_yp1+1)+mesh_surf(1, idx_node+ + + 1))/2. - (mesh_surf(1, idx_node_yp1)+mesh_surf(1, idx_node + + ))/2. + temp0 = (mesh_surf(3, idx_node_yp1+1)+mesh_surf(3, idx_node+ + + 1))/2. - (mesh_surf(3, idx_node_yp1)+mesh_surf(3, idx_node + + ))/2. + temp_diff0 = temp1*a3_diff/(temp0**2+temp1**2) + temp_diff = -(temp0*a3_diff/(temp0**2+temp1**2)) + mesh_surf_diff(1, idx_node_yp1+1) = mesh_surf_diff(1, + + idx_node_yp1+1) + temp_diff/2. + mesh_surf_diff(1, idx_node+1) = mesh_surf_diff(1, idx_node+1 + + ) + temp_diff/2. + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) - temp_diff/2. + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) - + + temp_diff/2. + mesh_surf_diff(3, idx_node_yp1+1) = mesh_surf_diff(3, + + idx_node_yp1+1) + temp_diff0/2. + mesh_surf_diff(3, idx_node+1) = mesh_surf_diff(3, idx_node+1 + + ) + temp_diff0/2. + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) - temp_diff0/2. + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) - + + temp_diff0/2. + CALL POPREAL8(dxv(idx_vor)) + dc1_diff = dxv_diff(idx_vor)/2. + dc2_diff = dxv_diff(idx_vor)/2. + dxv_diff(idx_vor) = 0.D0 + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + dx2_diff = rv2_diff(1, idx_vor) + temp4 = mesh_surf(3, idx_node_yp1) - rle2(3, idx_strip) + temp3 = mesh_surf(1, idx_node_yp1) - rle2(1, idx_strip) + IF (temp3**2 + temp4**2 .EQ. 0.D0) THEN + temp_diff2 = 0.D0 + ELSE + temp_diff2 = dx2_diff/(2.0*SQRT(temp3**2+temp4**2)) + END IF + temp_diff3 = 2*temp3*temp_diff2 + temp_diff4 = 2*temp4*temp_diff2 + a2_diff = COS(a2)*dc2*rv2msh_diff(3, idx_vor)/4. - SIN(a2) + + *dc2*rv2msh_diff(1, idx_vor)/4. + temp1 = mesh_surf(1, idx_node_yp1+1) - mesh_surf(1, + + idx_node_yp1) + temp0 = mesh_surf(3, idx_node_yp1+1) - mesh_surf(3, + + idx_node_yp1) + temp_diff0 = temp1*a2_diff/(temp0**2+temp1**2) + temp_diff = -(temp0*a2_diff/(temp0**2+temp1**2)) + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) + rv2msh_diff(3, idx_vor) + temp_diff4 - + + temp_diff0 + dc2_diff = dc2_diff + SIN(a2)*rv2msh_diff(3, idx_vor)/4. + + + COS(a2)*rv2msh_diff(1, idx_vor)/4. + rv2_diff(1, idx_vor + + )/4. + rv2msh_diff(3, idx_vor) = 0.D0 + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) + rv2msh_diff(1, idx_vor) + temp_diff3 - + + temp_diff + rv2msh_diff(1, idx_vor) = 0.D0 + mesh_surf_diff(2, idx_node_yp1) = mesh_surf_diff(2, + + idx_node_yp1) + rv2msh_diff(2, idx_vor) + rv2msh_diff(2, idx_vor) = 0.D0 + CALL POPREAL8(a2) + mesh_surf_diff(1, idx_node_yp1+1) = mesh_surf_diff(1, + + idx_node_yp1+1) + temp_diff + mesh_surf_diff(3, idx_node_yp1+1) = mesh_surf_diff(3, + + idx_node_yp1+1) + temp_diff0 + rle2_diff(1, idx_strip) = rle2_diff(1, idx_strip) + + + rv2_diff(1, idx_vor) - temp_diff3 + rv2_diff(1, idx_vor) = 0.D0 + rle2_diff(3, idx_strip) = rle2_diff(3, idx_strip) + + + rv2_diff(3, idx_vor) - temp_diff4 + rv2_diff(3, idx_vor) = 0.D0 + rle2_diff(2, idx_strip) = rle2_diff(2, idx_strip) + + + rv2_diff(2, idx_vor) + rv2_diff(2, idx_vor) = 0.D0 + ELSE + rv2_diff(3, idx_vor) = rv2_diff(3, idx_vor) + rv2msh_diff( + + 3, idx_vor) + rv2msh_diff(3, idx_vor) = 0.D0 + rv2_diff(1, idx_vor) = rv2_diff(1, idx_vor) + rv2msh_diff( + + 1, idx_vor) + rv2msh_diff(1, idx_vor) = 0.D0 + rv2_diff(2, idx_vor) = rv2_diff(2, idx_vor) + rv2msh_diff( + + 2, idx_vor) + rv2msh_diff(2, idx_vor) = 0.D0 + dc2_diff = dc2_diff + SIN(a2)*rv2_diff(3, idx_vor)/4. + + + COS(a2)*rv2_diff(1, idx_vor)/4. + a2_diff = COS(a2)*dc2*rv2_diff(3, idx_vor)/4. - SIN(a2)* + + dc2*rv2_diff(1, idx_vor)/4. + mesh_surf_diff(2, idx_node_yp1) = mesh_surf_diff(2, + + idx_node_yp1) + rv2_diff(2, idx_vor) + rv2_diff(2, idx_vor) = 0.D0 + CALL POPREAL8(a2) + temp1 = mesh_surf(1, idx_node_yp1+1) - mesh_surf(1, + + idx_node_yp1) + temp0 = mesh_surf(3, idx_node_yp1+1) - mesh_surf(3, + + idx_node_yp1) + temp_diff0 = temp1*a2_diff/(temp0**2+temp1**2) + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) + rv2_diff(3, idx_vor) - temp_diff0 + rv2_diff(3, idx_vor) = 0.D0 + temp_diff = -(temp0*a2_diff/(temp0**2+temp1**2)) + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) + rv2_diff(1, idx_vor) - temp_diff + rv2_diff(1, idx_vor) = 0.D0 + mesh_surf_diff(1, idx_node_yp1+1) = mesh_surf_diff(1, + + idx_node_yp1+1) + temp_diff + mesh_surf_diff(3, idx_node_yp1+1) = mesh_surf_diff(3, + + idx_node_yp1+1) + temp_diff0 + END IF + CALL POPREAL8(dc2) + temp1 = mesh_surf(3, idx_node_yp1+1) - mesh_surf(3, + + idx_node_yp1) + temp0 = mesh_surf(1, idx_node_yp1+1) - mesh_surf(1, + + idx_node_yp1) + IF (temp0**2 + temp1**2 .EQ. 0.D0) THEN + temp_diff1 = 0.D0 + ELSE + temp_diff1 = dc2_diff/(2.0*SQRT(temp0**2+temp1**2)) + END IF + temp_diff0 = 2*temp0*temp_diff1 + temp_diff = 2*temp1*temp_diff1 + mesh_surf_diff(3, idx_node_yp1+1) = mesh_surf_diff(3, + + idx_node_yp1+1) + temp_diff + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) - temp_diff + mesh_surf_diff(1, idx_node_yp1+1) = mesh_surf_diff(1, + + idx_node_yp1+1) + temp_diff0 + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) - temp_diff0 + CALL POPINTEGER4(idx_node_yp1) + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + dx1_diff = rv1_diff(1, idx_vor) + temp3 = mesh_surf(3, idx_node) - rle1(3, idx_strip) + temp2 = mesh_surf(1, idx_node) - rle1(1, idx_strip) + IF (temp2**2 + temp3**2 .EQ. 0.D0) THEN + temp_diff4 = 0.D0 + ELSE + temp_diff4 = dx1_diff/(2.0*SQRT(temp2**2+temp3**2)) + END IF + temp_diff2 = 2*temp2*temp_diff4 + temp_diff3 = 2*temp3*temp_diff4 + a1_diff = COS(a1)*dc1*rv1msh_diff(3, idx_vor)/4. - SIN(a1) + + *dc1*rv1msh_diff(1, idx_vor)/4. + temp1 = mesh_surf(1, idx_node+1) - mesh_surf(1, idx_node) + temp0 = mesh_surf(3, idx_node+1) - mesh_surf(3, idx_node) + temp_diff0 = temp1*a1_diff/(temp0**2+temp1**2) + temp_diff = -(temp0*a1_diff/(temp0**2+temp1**2)) + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) + + + rv1msh_diff(3, idx_vor) + temp_diff3 - temp_diff0 + dc1_diff = dc1_diff + SIN(a1)*rv1msh_diff(3, idx_vor)/4. + + + COS(a1)*rv1msh_diff(1, idx_vor)/4. + rv1_diff(1, idx_vor + + )/4. + rv1msh_diff(3, idx_vor) = 0.D0 + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) + + + rv1msh_diff(1, idx_vor) + temp_diff2 - temp_diff + rv1msh_diff(1, idx_vor) = 0.D0 + mesh_surf_diff(2, idx_node) = mesh_surf_diff(2, idx_node) + + + rv1msh_diff(2, idx_vor) + rv1msh_diff(2, idx_vor) = 0.D0 + CALL POPREAL8(a1) + mesh_surf_diff(1, idx_node+1) = mesh_surf_diff(1, idx_node + + +1) + temp_diff + mesh_surf_diff(3, idx_node+1) = mesh_surf_diff(3, idx_node + + +1) + temp_diff0 + rle1_diff(1, idx_strip) = rle1_diff(1, idx_strip) + + + rv1_diff(1, idx_vor) - temp_diff2 + rv1_diff(1, idx_vor) = 0.D0 + rle1_diff(3, idx_strip) = rle1_diff(3, idx_strip) + + + rv1_diff(3, idx_vor) - temp_diff3 + rv1_diff(3, idx_vor) = 0.D0 + rle1_diff(2, idx_strip) = rle1_diff(2, idx_strip) + + + rv1_diff(2, idx_vor) + rv1_diff(2, idx_vor) = 0.D0 + ELSE + rv1_diff(3, idx_vor) = rv1_diff(3, idx_vor) + rv1msh_diff( + + 3, idx_vor) + rv1msh_diff(3, idx_vor) = 0.D0 + rv1_diff(1, idx_vor) = rv1_diff(1, idx_vor) + rv1msh_diff( + + 1, idx_vor) + rv1msh_diff(1, idx_vor) = 0.D0 + rv1_diff(2, idx_vor) = rv1_diff(2, idx_vor) + rv1msh_diff( + + 2, idx_vor) + rv1msh_diff(2, idx_vor) = 0.D0 + dc1_diff = dc1_diff + SIN(a1)*rv1_diff(3, idx_vor)/4. + + + COS(a1)*rv1_diff(1, idx_vor)/4. + a1_diff = COS(a1)*dc1*rv1_diff(3, idx_vor)/4. - SIN(a1)* + + dc1*rv1_diff(1, idx_vor)/4. + mesh_surf_diff(2, idx_node) = mesh_surf_diff(2, idx_node) + + + rv1_diff(2, idx_vor) + rv1_diff(2, idx_vor) = 0.D0 + CALL POPREAL8(a1) + temp1 = mesh_surf(1, idx_node+1) - mesh_surf(1, idx_node) + temp0 = mesh_surf(3, idx_node+1) - mesh_surf(3, idx_node) + temp_diff0 = temp1*a1_diff/(temp0**2+temp1**2) + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) + + + rv1_diff(3, idx_vor) - temp_diff0 + rv1_diff(3, idx_vor) = 0.D0 + temp_diff = -(temp0*a1_diff/(temp0**2+temp1**2)) + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) + + + rv1_diff(1, idx_vor) - temp_diff + rv1_diff(1, idx_vor) = 0.D0 + mesh_surf_diff(1, idx_node+1) = mesh_surf_diff(1, idx_node + + +1) + temp_diff + mesh_surf_diff(3, idx_node+1) = mesh_surf_diff(3, idx_node + + +1) + temp_diff0 + END IF + CALL POPREAL8(dc1) + temp1 = mesh_surf(3, idx_node+1) - mesh_surf(3, idx_node) + temp0 = mesh_surf(1, idx_node+1) - mesh_surf(1, idx_node) + IF (temp0**2 + temp1**2 .EQ. 0.D0) THEN + temp_diff1 = 0.D0 + ELSE + temp_diff1 = dc1_diff/(2.0*SQRT(temp0**2+temp1**2)) + END IF + temp_diff0 = 2*temp0*temp_diff1 + temp_diff = 2*temp1*temp_diff1 + mesh_surf_diff(3, idx_node+1) = mesh_surf_diff(3, idx_node+1 + + ) + temp_diff + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) - + + temp_diff + mesh_surf_diff(1, idx_node+1) = mesh_surf_diff(1, idx_node+1 + + ) + temp_diff0 + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) - + + temp_diff0 + CALL POPINTEGER4(idx_node) + ENDDO + clafr = claf(iptr, isurf) + clafl = claf(iptl, isurf) + CALL POPREAL8(clafc) + temp_diff2 = (1.-fc)*clafc_diff/chord(idx_strip) + temp_diff3 = fc*clafc_diff/chord(idx_strip) + chordr_diff = chordr_diff + clafr*temp_diff3 + clafr_diff = chordr*temp_diff3 + chord_diff(idx_strip) = chord_diff(idx_strip) - chordr*clafr* + + temp_diff3/chord(idx_strip) - chordl*clafl*temp_diff2/chord( + + idx_strip) + chordl_diff = chordl_diff + clafl*temp_diff2 + clafl_diff = chordl*temp_diff2 + DO n=ncontrol,1,-1 + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + vhz_diff = vhinge_diff(3, idx_strip, n)/vmod + vmod_diff = -(vhz*vhinge_diff(3, idx_strip, n)/vmod**2) - + + vhy*vhinge_diff(2, idx_strip, n)/vmod**2 - vhx* + + vhinge_diff(1, idx_strip, n)/vmod**2 + vhinge_diff(3, idx_strip, n) = 0.D0 + vhy_diff = vhinge_diff(2, idx_strip, n)/vmod + vhinge_diff(2, idx_strip, n) = 0.D0 + vhx_diff = vhinge_diff(1, idx_strip, n)/vmod + vhinge_diff(1, idx_strip, n) = 0.D0 + CALL POPREAL8(vmod) + IF (vsq .EQ. 0.D0) THEN + vsq_diff = 0.D0 + ELSE + vsq_diff = vmod_diff/(2.0*SQRT(vsq)) + END IF + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + vhx_diff = vhx_diff + 2*vhx*vsq_diff + vhy_diff = vhy_diff + 2*vhy*vsq_diff + vhz_diff = vhz_diff + 2*vhz*vsq_diff + CALL POPREAL8(vhz) + xyzscal_diff(3, isurf) = xyzscal_diff(3, isurf) + vhz* + + vhz_diff + vhz_diff = xyzscal(3, isurf)*vhz_diff + CALL POPREAL8(vhy) + xyzscal_diff(2, isurf) = xyzscal_diff(2, isurf) + vhy* + + vhy_diff + vhy_diff = xyzscal(2, isurf)*vhy_diff + CALL POPREAL8(vhx) + xyzscal_diff(1, isurf) = xyzscal_diff(1, isurf) + vhx* + + vhx_diff + vhx_diff = xyzscal(1, isurf)*vhx_diff + mesh_surf_diff(3, idx_noder) = mesh_surf_diff(3, + + idx_noder) + vhz_diff + mesh_surf_diff(3, idx_nodel) = mesh_surf_diff(3, + + idx_nodel) - vhz_diff + mesh_surf_diff(2, idx_noder) = mesh_surf_diff(2, + + idx_noder) + vhy_diff + mesh_surf_diff(2, idx_nodel) = mesh_surf_diff(2, + + idx_nodel) - vhy_diff + mesh_surf_diff(1, idx_noder) = mesh_surf_diff(1, + + idx_noder) + vhx_diff + abs0_diff = vhx_diff + mesh_surf_diff(1, idx_nodel) = mesh_surf_diff(1, + + idx_nodel) - vhx_diff + abs1_diff = -vhx_diff + vhy = vhinged(2, icl, iptl, isurf)*xyzscal(2, isurf) + vhz = vhinged(3, icl, iptl, isurf)*xyzscal(3, isurf) + vhx = vhinged(1, icl, iptl, isurf)*xyzscal(1, isurf) + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + chordl_diff = chordl_diff + xhinged(icl, iptl, isurf)* + + abs1_diff + ELSE + chordl_diff = chordl_diff - xhinged(icl, iptl, isurf)* + + abs1_diff + END IF + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + chordr_diff = chordr_diff - xhinged(icr, iptr, isurf)* + + abs0_diff + ELSE + chordr_diff = chordr_diff + xhinged(icr, iptr, isurf)* + + abs0_diff + END IF + vhx_diff = 0.D0 + vhy_diff = 0.D0 + vhz_diff = 0.D0 + vsq_diff = 0.D0 + END IF + CALL POPREAL8(vsq) + vhx_diff = vhx_diff + 2*vhx*vsq_diff + vhy_diff = vhy_diff + 2*vhy*vsq_diff + vhz_diff = vhz_diff + 2*vhz*vsq_diff + CALL POPREAL8(vhz) + xyzscal_diff(3, isurf) = xyzscal_diff(3, isurf) + vhinged( + + 3, icl, iptl, isurf)*vhz_diff + CALL POPREAL8(vhy) + xyzscal_diff(2, isurf) = xyzscal_diff(2, isurf) + vhinged( + + 2, icl, iptl, isurf)*vhy_diff + CALL POPREAL8(vhx) + xyzscal_diff(1, isurf) = xyzscal_diff(1, isurf) + vhinged( + + 1, icl, iptl, isurf)*vhx_diff + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + CALL POPREAL8(xted(n)) + chord_diff(idx_strip) = chord_diff(idx_strip) + + + xted_diff(n) + xted_diff(n) = 0.D0 + CALL POPREAL8(xled(n)) + xhd_diff = xled_diff(n) + xled_diff(n) = 0.D0 + ELSE + CALL POPREAL8(xted(n)) + xhd_diff = -xted_diff(n) + xted_diff(n) = 0.D0 + CALL POPREAL8(xled(n)) + xled_diff(n) = 0.D0 + END IF + chordl_diff = chordl_diff + (1.0-fc)*xhinged(icl, iptl, + + isurf)*xhd_diff + chordr_diff = chordr_diff + fc*xhinged(icr, iptr, isurf)* + + xhd_diff + CALL POPREAL8(gainda(n)) + ELSE + vhinge_diff(3, idx_strip, n) = 0.D0 + vhinge_diff(2, idx_strip, n) = 0.D0 + vhinge_diff(1, idx_strip, n) = 0.D0 + CALL POPREAL8(xted(n)) + xted_diff(n) = 0.D0 + CALL POPREAL8(xled(n)) + xled_diff(n) = 0.D0 + CALL POPREAL8(gainda(n)) + END IF + CALL POPINTEGER4(icr) + CALL POPINTEGER4(icl) + ENDDO + chsin_diff = 0.D0 + chcos_diff = 0.D0 + DO n=ndesign,1,-1 + temp1 = chsin*chsin + chcos*chcos + temp_diff0 = ainc_g_diff(idx_strip, n)/temp1 + ainc_g_diff(idx_strip, n) = 0.D0 + chsin_g_diff = chcos*temp_diff0 + chcos_g_diff = -(chsin*temp_diff0) + temp_diff = -((chcos*chsin_g-chsin*chcos_g)*temp_diff0/temp1 + + ) + chcos_diff = chcos_diff + chsin_g*temp_diff0 + 2*chcos* + + temp_diff + chsin_diff = chsin_diff + 2*chsin*temp_diff - chcos_g* + + temp_diff0 + CALL POPREAL8(chcos_g) + chcosl_g_diff(n) = chcosl_g_diff(n) + (1.0-fc)*chcos_g_diff + chcosr_g_diff(n) = chcosr_g_diff(n) + fc*chcos_g_diff + CALL POPREAL8(chsin_g) + chsinl_g_diff(n) = chsinl_g_diff(n) + (1.0-fc)*chsin_g_diff + chsinr_g_diff(n) = chsinr_g_diff(n) + fc*chsin_g_diff + ENDDO + chsin_diff = chsin_diff + chcos*ainc_diff(idx_strip)/(chsin**2 + + +chcos**2) + chcos_diff = chcos_diff - chsin*ainc_diff(idx_strip)/(chsin**2 + + +chcos**2) + ainc_diff(idx_strip) = 0.D0 + CALL POPREAL8(chcos) + chcosl_diff = (1.0-fc)*chcos_diff + chcosr_diff = fc*chcos_diff + CALL POPREAL8(chsin) + chsinl_diff = (1.0-fc)*chsin_diff + chsinr_diff = fc*chsin_diff + CALL POPINTEGER4(idx_noder) + CALL POPINTEGER4(idx_nodel) + m2 = mesh_surf(2, idx_node_yp1) - mesh_surf(2, idx_node) + m3 = mesh_surf(3, idx_node_yp1) - mesh_surf(3, idx_node) + IF (m2**2 + m3**2 .EQ. 0.D0) THEN + temp_diff = 0.D0 + ELSE + temp_diff = wstrip_diff(idx_strip)/(2.0*SQRT(m2**2+m3**2)) + END IF + wstrip_diff(idx_strip) = 0.D0 + m2_diff = 2*m2*temp_diff + m3_diff = 2*m3*temp_diff + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) + m3_diff + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) - + + m3_diff + mesh_surf_diff(2, idx_node_yp1) = mesh_surf_diff(2, + + idx_node_yp1) + m2_diff + mesh_surf_diff(2, idx_node) = mesh_surf_diff(2, idx_node) - + + m2_diff + CALL POPREAL8(chord(idx_strip)) + chord1_diff(idx_strip) = chord1_diff(idx_strip) + chord_diff( + + idx_strip)/2. + chord2_diff(idx_strip) = chord2_diff(idx_strip) + chord_diff( + + idx_strip)/2. + chord_diff(idx_strip) = 0.D0 + DO idx_dim=3,1,-1 + CALL POPREAL8(rle(idx_dim, idx_strip)) + rle1_diff(idx_dim, idx_strip) = rle1_diff(idx_dim, idx_strip + + ) + rle_diff(idx_dim, idx_strip)/2. + rle2_diff(idx_dim, idx_strip) = rle2_diff(idx_dim, idx_strip + + ) + rle_diff(idx_dim, idx_strip)/2. + rle_diff(idx_dim, idx_strip) = 0.D0 + ENDDO + temp1 = mesh_surf(3, idx_node_nx_yp1) - mesh_surf(3, + + idx_node_yp1) + temp0 = mesh_surf(1, idx_node_nx_yp1) - mesh_surf(1, + + idx_node_yp1) + IF (temp0**2 + temp1**2 .EQ. 0.D0) THEN + temp_diff1 = 0.D0 + ELSE + temp_diff1 = chord2_diff(idx_strip)/(2.0*SQRT(temp0**2+temp1 + + **2)) + END IF + chord2_diff(idx_strip) = 0.D0 + temp_diff0 = 2*temp0*temp_diff1 + temp_diff = 2*temp1*temp_diff1 + mesh_surf_diff(3, idx_node_nx_yp1) = mesh_surf_diff(3, + + idx_node_nx_yp1) + temp_diff + mesh_surf_diff(3, idx_node_yp1) = mesh_surf_diff(3, + + idx_node_yp1) - temp_diff + mesh_surf_diff(1, idx_node_nx_yp1) = mesh_surf_diff(1, + + idx_node_nx_yp1) + temp_diff0 + mesh_surf_diff(1, idx_node_yp1) = mesh_surf_diff(1, + + idx_node_yp1) - temp_diff0 + DO idx_dim=3,1,-1 + CALL POPREAL8(rle2(idx_dim, idx_strip)) + mesh_surf_diff(idx_dim, idx_node_yp1) = mesh_surf_diff( + + idx_dim, idx_node_yp1) + rle2_diff(idx_dim, idx_strip) + rle2_diff(idx_dim, idx_strip) = 0.D0 + ENDDO + CALL POPINTEGER4(idx_node_nx_yp1) + CALL POPINTEGER4(idx_node_yp1) + temp1 = mesh_surf(3, idx_node_nx) - mesh_surf(3, idx_node) + temp0 = mesh_surf(1, idx_node_nx) - mesh_surf(1, idx_node) + IF (temp0**2 + temp1**2 .EQ. 0.D0) THEN + temp_diff1 = 0.D0 + ELSE + temp_diff1 = chord1_diff(idx_strip)/(2.0*SQRT(temp0**2+temp1 + + **2)) + END IF + chord1_diff(idx_strip) = 0.D0 + temp_diff0 = 2*temp0*temp_diff1 + temp_diff = 2*temp1*temp_diff1 + mesh_surf_diff(3, idx_node_nx) = mesh_surf_diff(3, idx_node_nx + + ) + temp_diff + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) - + + temp_diff + mesh_surf_diff(1, idx_node_nx) = mesh_surf_diff(1, idx_node_nx + + ) + temp_diff0 + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) - + + temp_diff0 + DO idx_dim=3,1,-1 + CALL POPREAL8(rle1(idx_dim, idx_strip)) + mesh_surf_diff(idx_dim, idx_node) = mesh_surf_diff(idx_dim, + + idx_node) + rle1_diff(idx_dim, idx_strip) + rle1_diff(idx_dim, idx_strip) = 0.D0 + ENDDO + CALL POPINTEGER4(idx_node_nx) + CALL POPINTEGER4(idx_node) + DO n=ndesign,1,-1 + CALL POPINTEGER4(ad_to4) + DO isdes=ad_to4,1,-1 + CALL POPCONTROL1B(branch) + IF (branch .NE. 0) THEN + chsinr_diff = chsinr_diff - dtr*gaing(isdes, iptr, isurf + + )*chcosr_g_diff(n) + chcosr_g_diff(n) = 0.D0 + chcosr_diff = chcosr_diff + dtr*gaing(isdes, iptr, isurf + + )*chsinr_g_diff(n) + chsinr_g_diff(n) = 0.D0 + END IF + ENDDO + CALL POPINTEGER4(ad_to3) + DO isdes=ad_to3,1,-1 + CALL POPCONTROL1B(branch) + IF (branch .NE. 0) THEN + chsinl_diff = chsinl_diff - dtr*gaing(isdes, iptl, isurf + + )*chcosl_g_diff(n) + chcosl_g_diff(n) = 0.D0 + chcosl_diff = chcosl_diff + dtr*gaing(isdes, iptl, isurf + + )*chsinl_g_diff(n) + chsinl_g_diff(n) = 0.D0 + END IF + ENDDO + chcosr_g_diff(n) = 0.D0 + chcosl_g_diff(n) = 0.D0 + chsinr_g_diff(n) = 0.D0 + chsinl_g_diff(n) = 0.D0 + ENDDO + DO n=ncontrol,1,-1 + CALL POPINTEGER4(ad_to2) + DO iscon=ad_to2,1,-1 + CALL POPCONTROL1B(branch) + ENDDO + CALL POPINTEGER4(ad_to1) + DO iscon=ad_to1,1,-1 + CALL POPCONTROL1B(branch) + ENDDO + ENDDO + aincr = aincs(iptr, isurf)*dtr + addinc(isurf)*dtr + chordr_diff = chordr_diff + COS(aincr)*chcosr_diff + SIN(aincr + + )*chsinr_diff + aincr_diff = COS(aincr)*chordr*chsinr_diff - SIN(aincr)*chordr + + *chcosr_diff + aincl = aincs(iptl, isurf)*dtr + addinc(isurf)*dtr + chordl_diff = chordl_diff + COS(aincl)*chcosl_diff + SIN(aincl + + )*chsinl_diff + aincl_diff = COS(aincl)*chordl*chsinl_diff - SIN(aincl)*chordl + + *chcosl_diff + aincs_diff(iptr, isurf) = aincs_diff(iptr, isurf) + dtr* + + aincr_diff + addinc_diff(isurf) = addinc_diff(isurf) + dtr*aincr_diff + dtr + + *aincl_diff + aincs_diff(iptl, isurf) = aincs_diff(iptl, isurf) + dtr* + + aincl_diff + claf_diff(iptr, isurf) = claf_diff(iptr, isurf) + clafr_diff + claf_diff(iptl, isurf) = claf_diff(iptl, isurf) + clafl_diff + CALL POPREAL8(chordr) + temp1 = mesh_surf(3, idx_node_nx) - mesh_surf(3, idx_node) + temp0 = mesh_surf(1, idx_node_nx) - mesh_surf(1, idx_node) + IF (temp0**2 + temp1**2 .EQ. 0.D0) THEN + temp_diff1 = 0.D0 + ELSE + temp_diff1 = chordr_diff/(2.0*SQRT(temp0**2+temp1**2)) + END IF + temp_diff0 = 2*temp0*temp_diff1 + temp_diff = 2*temp1*temp_diff1 + mesh_surf_diff(3, idx_node_nx) = mesh_surf_diff(3, idx_node_nx + + ) + temp_diff + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) - + + temp_diff + mesh_surf_diff(1, idx_node_nx) = mesh_surf_diff(1, idx_node_nx + + ) + temp_diff0 + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) - + + temp_diff0 + CALL POPINTEGER4(idx_node_nx) + CALL POPINTEGER4(idx_node) + CALL POPREAL8(chordl) + temp = mesh_surf(3, idx_node_nx) - mesh_surf(3, idx_node) + temp0 = mesh_surf(1, idx_node_nx) - mesh_surf(1, idx_node) + IF (temp0**2 + temp**2 .EQ. 0.D0) THEN + temp_diff = 0.D0 + ELSE + temp_diff = chordl_diff/(2.0*SQRT(temp0**2+temp**2)) + END IF + temp_diff0 = 2*temp0*temp_diff + temp_diff1 = 2*temp*temp_diff + mesh_surf_diff(3, idx_node_nx) = mesh_surf_diff(3, idx_node_nx + + ) + temp_diff1 + mesh_surf_diff(3, idx_node) = mesh_surf_diff(3, idx_node) - + + temp_diff1 + mesh_surf_diff(1, idx_node_nx) = mesh_surf_diff(1, idx_node_nx + + ) + temp_diff0 + mesh_surf_diff(1, idx_node) = mesh_surf_diff(1, idx_node) - + + temp_diff0 + CALL POPINTEGER4(idx_node_nx) + CALL POPINTEGER4(idx_node) + CALL POPINTEGER4(iptr) + CALL POPINTEGER4(iptl) + CALL POPINTEGER4(idx_y) + ENDDO + CALL POPINTEGER4(ad_to0) + DO idx_y=ad_to0,1,-1 + CALL POPINTEGER4(ad_to) + DO idx_x=ad_to,1,-1 + DO idx_dim=3,1,-1 + CALL POPREAL8(mesh_surf(idx_dim, idx_node)) + xyzscal_diff(idx_dim, isurf) = xyzscal_diff(idx_dim, isurf + + ) + mesh_surf(idx_dim, idx_node)*mesh_surf_diff(idx_dim + + , idx_node) + xyztran_diff(idx_dim, isurf) = xyztran_diff(idx_dim, isurf + + ) + mesh_surf_diff(idx_dim, idx_node) + mesh_surf_diff(idx_dim, idx_node) = xyzscal(idx_dim, isurf + + )*mesh_surf_diff(idx_dim, idx_node) + CALL POPINTEGER4(idx_node) + ENDDO + ENDDO + ENDDO + mshblk_diff(:, mfrst(isurf):mfrst(isurf)+nx*ny-1) = mshblk_diff( + + :, mfrst(isurf):mfrst(isurf)+nx*ny-1) + mesh_surf_diff + END IF + END + +C Differentiation of sdupl in reverse (adjoint) mode (with options i4 dr8 r8): +C gradient of useful results: rv1msh rv2msh rvmsh rcmsh rle +C chord rle1 chord1 rle2 chord2 wstrip ainc ainc_g +C rv1 rv2 rv rc dxv chordv slopev slopec dcontrol +C vhinge +C with respect to varying inputs: rv1msh rv2msh rvmsh rcmsh rle +C chord rle1 chord1 rle2 chord2 wstrip ainc ainc_g +C rv1 rv2 rv rc dxv chordv slopev slopec dcontrol +C vhinge +C + SUBROUTINE SDUPL_B(nn, ypt, msg) + INCLUDE 'AVL.INC' + INCLUDE 'AVL_ad_seeds.inc' + CHARACTER*(*) msg + INTEGER idx_vor + INTEGER nni + INTEGER klen + INTRINSIC LEN + INTEGER k + INTEGER isec + INTEGER idup + INTEGER iorg + REAL yoff + INTEGER idx_strip + INTEGER ivs + INTEGER jji + INTEGER jj + INTEGER n + INTEGER l + INTEGER ivc + INTEGER iii + INTEGER ii + REAL rsgn + REAL(kind=avl_real) tmp + REAL(kind=avl_real) tmp0 + REAL(kind=avl_real) tmp1 + REAL(kind=avl_real) tmp_diff + REAL(kind=avl_real) tmp2 + REAL(kind=avl_real) tmp_diff0 + REAL(kind=avl_real) tmp3 + REAL(kind=avl_real) tmp_diff1 + REAL(kind=avl_real) tmp4 + REAL(kind=avl_real) tmp_diff2 + REAL(kind=avl_real) tmp5 + REAL(kind=avl_real) tmp6 + REAL(kind=avl_real) tmp_diff3 + REAL(kind=avl_real) tmp7 + REAL(kind=avl_real) tmp8 + REAL(kind=avl_real) tmp_diff4 + REAL(kind=avl_real) tmp9 + REAL(kind=avl_real) tmp_diff5 + REAL(kind=avl_real) tmp10 + REAL(kind=avl_real) tmp11 + REAL(kind=avl_real) tmp_diff6 + REAL(kind=avl_real) tmp12 + REAL(kind=avl_real) tmp_diff7 + REAL(kind=avl_real) tmp13 + REAL(kind=avl_real) tmp_diff8 + REAL(kind=avl_real) tmp14 + REAL(kind=avl_real) tmp15 + REAL(kind=avl_real) tmp16 + REAL(kind=avl_real) tmp17 + REAL(kind=avl_real) tmp18 + REAL(kind=avl_real) tmp_diff9 + REAL(kind=avl_real) tmp19 + REAL(kind=avl_real) tmp_diff10 + REAL(kind=avl_real) tmp20 + REAL(kind=avl_real) tmp_diff11 + REAL(kind=avl_real) tmp21 + REAL(kind=avl_real) tmp_diff12 + REAL(kind=avl_real) tmp22 + REAL(kind=avl_real) tmp_diff13 + REAL(kind=avl_real) tmp23 + REAL(kind=avl_real) tmp_diff14 + REAL(kind=avl_real) tmp24 + REAL(kind=avl_real) tmp_diff15 + REAL(kind=avl_real) tmp25 + REAL(kind=avl_real) tmp_diff16 + REAL(kind=avl_real) tmp26 + REAL(kind=avl_real) tmp_diff17 + REAL(kind=avl_real) tmp27 + REAL(kind=avl_real) tmp28 + REAL(kind=avl_real) tmp_diff18 + REAL(kind=avl_real) tmp29 + REAL(kind=avl_real) tmp_diff19 + REAL(kind=avl_real) tmp30 + REAL(kind=avl_real) tmp_diff20 + REAL(kind=avl_real) tmp31 + REAL(kind=avl_real) tmp_diff21 + REAL(kind=avl_real) tmp32 + REAL(kind=avl_real) tmp_diff22 + REAL(kind=avl_real) tmp33 + REAL(kind=avl_real) tmp_diff23 + REAL(kind=avl_real) tmp34 + REAL(kind=avl_real) tmp_diff24 + REAL(kind=avl_real) tmp35 + REAL(kind=avl_real) tmp_diff25 + INTEGER ad_count + INTEGER i + INTEGER branch + INTEGER ad_to + INTEGER ad_to0 + INTEGER ad_to1 + INTEGER ad_count0 + INTEGER i0 + INTEGER ii3 + INTEGER ii2 + INTEGER ii1 + INTEGER nn + REAL ypt +C +C + nni = nn + 1 + IF (nni .GT. nfmax) THEN + STOP + ELSE +C + klen = LEN(stitle(nn)) + ad_count = 1 + DO k=klen,1,-1 + IF (stitle(nn)(k:k) .NE. ' ') THEN + GOTO 100 + ELSE + ad_count = ad_count + 1 + END IF + ENDDO + CALL PUSHCONTROL1B(0) + CALL PUSHINTEGER4(ad_count) + CALL PUSHINTEGER4(ivs) + CALL PUSHCONTROL1B(0) + GOTO 110 + 100 CALL PUSHCONTROL1B(1) + CALL PUSHINTEGER4(ad_count) + CALL PUSHINTEGER4(ivs) + CALL PUSHCONTROL1B(0) +C +C---- duplicate surface is assumed to be the same logical component surface +C +C---- same various logical flags +C IFRST(NNI) = NVOR + 1 + 110 lsurfmsh(nni) = lsurfmsh(nn) +C +C---- accumulate stuff for new image surface +C JFRST(NNI) = NSTRIP + 1 + jfrst(nni) = jfrst(nni-1) + nj(nni-1) + nj(nni) = nj(nn) + nk(nni) = nk(nn) +C + nvc(nni) = nk(nni) + nvs(nni) = nj(nni) +C +C--- Note hinge axis is flipped to reverse the Y component of the hinge +C vector. This means that deflections need to be reversed for image +C surfaces. +C +C--- Image flag reversed (set to -IMAGS) for imaged surfaces +C +Cc#ifdef USE_CPOML +Cc#endif +C +C +C--- Create image strips, to maintain the same sense of positive GAMMA +C these have the 1 and 2 strip edges reversed (i.e. root is edge 2, +C not edge 1 as for a strip with IMAGS=1 + idx_strip = jfrst(nni) +C NSTRIP = NSTRIP + 1 + DO ivs=1,nvs(nni) + IF (idx_strip .GT. nsmax) THEN + GOTO 130 + ELSE +C + jji = jfrst(nni) + ivs - 1 + jj = jfrst(nn) + ivs - 1 +C +Cc#ifdef USE_CPOML +C +Cc#endif +C + n = ndesign + 1 + CALL PUSHINTEGER4(n - 1) +C + DO n=1,ncontrol + tmp10 = vrefl(jj, n) + vrefl(jji, n) = tmp10 C C ENDDO @@ -2351,6 +4146,12 @@ SUBROUTINE SDUPL_B(nn, ypt, msg) iii = ijfrst(jji) + ivc - 1 CALL PUSHINTEGER4(ii) ii = ijfrst(jj) + ivc - 1 +C Duplicate mesh data if we are using a mesh + IF (lsurfmsh(nn)) THEN + CALL PUSHCONTROL1B(1) + ELSE + CALL PUSHCONTROL1B(0) + END IF C DO n=1,ncontrol Ccc RSGN = SIGN( 1.0 , VREFL(JJ,N) ) @@ -2385,6 +4186,26 @@ SUBROUTINE SDUPL_B(nn, ypt, msg) IF (i0 .EQ. 1) THEN CALL POPCONTROL1B(branch) IF (branch .NE. 0) THEN + DO ii1=1,nvor + DO ii2=1,3 + rv1msh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rv2msh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rvmsh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rcmsh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO DO ii1=1,NSTRIP DO ii2=1,3 rle_diff(ii2, ii1) = 0.D0 @@ -2468,12 +4289,51 @@ SUBROUTINE SDUPL_B(nn, ypt, msg) ELSE CALL POPINTEGER4(ad_to1) DO n=ad_to1,1,-1 - tmp_diff19 = dcontrol_diff(iii, n) + tmp_diff25 = dcontrol_diff(iii, n) dcontrol_diff(iii, n) = 0.D0 dcontrol_diff(ii, n) = dcontrol_diff(ii, n) - rsgn* - + tmp_diff19 + + tmp_diff25 CALL POPREAL8(rsgn) ENDDO + CALL POPCONTROL1B(branch) + IF (branch .NE. 0) THEN + tmp_diff24 = rcmsh_diff(3, iii) + rcmsh_diff(3, iii) = 0.D0 + rcmsh_diff(3, ii) = rcmsh_diff(3, ii) + tmp_diff24 + tmp_diff23 = rcmsh_diff(2, iii) + rcmsh_diff(2, iii) = 0.D0 + rcmsh_diff(2, ii) = rcmsh_diff(2, ii) - tmp_diff23 + tmp_diff22 = rcmsh_diff(1, iii) + rcmsh_diff(1, iii) = 0.D0 + rcmsh_diff(1, ii) = rcmsh_diff(1, ii) + tmp_diff22 + tmp_diff21 = rvmsh_diff(3, iii) + rvmsh_diff(3, iii) = 0.D0 + rvmsh_diff(3, ii) = rvmsh_diff(3, ii) + tmp_diff21 + tmp_diff20 = rvmsh_diff(2, iii) + rvmsh_diff(2, iii) = 0.D0 + rvmsh_diff(2, ii) = rvmsh_diff(2, ii) - tmp_diff20 + tmp_diff19 = rvmsh_diff(1, iii) + rvmsh_diff(1, iii) = 0.D0 + rvmsh_diff(1, ii) = rvmsh_diff(1, ii) + tmp_diff19 + rv1msh_diff(3, ii) = rv1msh_diff(3, ii) + rv2msh_diff(3 + + , iii) + rv2msh_diff(3, iii) = 0.D0 + rv1msh_diff(2, ii) = rv1msh_diff(2, ii) - rv2msh_diff(2 + + , iii) + rv2msh_diff(2, iii) = 0.D0 + rv1msh_diff(1, ii) = rv1msh_diff(1, ii) + rv2msh_diff(1 + + , iii) + rv2msh_diff(1, iii) = 0.D0 + rv2msh_diff(3, ii) = rv2msh_diff(3, ii) + rv1msh_diff(3 + + , iii) + rv1msh_diff(3, iii) = 0.D0 + rv2msh_diff(2, ii) = rv2msh_diff(2, ii) - rv1msh_diff(2 + + , iii) + rv1msh_diff(2, iii) = 0.D0 + rv2msh_diff(1, ii) = rv2msh_diff(1, ii) + rv1msh_diff(1 + + , iii) + rv1msh_diff(1, iii) = 0.D0 + END IF tmp_diff18 = chordv_diff(iii) chordv_diff(iii) = 0.D0 chordv_diff(ii) = chordv_diff(ii) + tmp_diff18 @@ -2591,14 +4451,16 @@ SUBROUTINE SDUPL_B(nn, ypt, msg) C Differentiation of encalc in reverse (adjoint) mode (with options i4 dr8 r8): C gradient of useful results: ess ensy ensz xsref ysref zsref C rv1 rv2 rv enc env enc_d -C with respect to varying inputs: ess ensy ensz xsref ysref zsref -C ainc ainc_g rv1 rv2 rv slopev slopec dcontrol -C vhinge enc env enc_d +C with respect to varying inputs: rv1msh rv2msh rvmsh rcmsh ess +C ensy ensz xsref ysref zsref ainc ainc_g rv1 rv2 +C rv slopev slopec dcontrol vhinge enc env enc_d C BDUPL C C C C +C Also checks if surface has been assigned a point cloud mesh +C and uses the real mesh to compute normals if it is SUBROUTINE ENCALC_B() INCLUDE 'AVL.INC' INCLUDE 'AVL_ad_seeds.inc' @@ -2607,6 +4469,8 @@ SUBROUTINE ENCALC_B() REAL ep_diff(3), eq_diff(3), es_diff(3), eb_diff(3), ec_diff(3), + ecxb_diff(3) REAL ec_g(3, ndmax), ecxb_g(3) + REAL(kind=avl_real) dchstrip, dxt, dyt, dzt, ec_msh(3) + REAL(kind=avl_real) dxt_diff, dyt_diff, dzt_diff, ec_msh_diff(3) INTEGER j INTEGER i REAL dxle @@ -2633,12 +4497,6 @@ SUBROUTINE ENCALC_B() REAL ayte_diff REAL azte REAL azte_diff - REAL dxt - REAL dxt_diff - REAL dyt - REAL dyt_diff - REAL dzt - REAL dzt_diff INTRINSIC SQRT INTEGER nv INTEGER ii @@ -2668,10 +4526,18 @@ SUBROUTINE ENCALC_B() REAL endot REAL endot_diff REAL DOT - REAL temp - REAL temp0 - REAL temp_diff - REAL temp_diff0 + REAL(kind=avl_real) temp + REAL(kind=avl_real) temp0 + REAL(kind=avl_real) temp_diff + REAL(kind=avl_real) temp_diff0 + REAL temp_diff1 + REAL(kind=avl_real) temp1 + REAL(kind=avl_real) temp_diff2 + REAL(kind=avl_real) temp_diff3 + REAL(kind=8) temp_diff4 + REAL temp_diff5 + INTEGER ad_count + INTEGER i0 INTEGER branch INTEGER ad_to INTEGER ii1 @@ -2680,38 +4546,87 @@ SUBROUTINE ENCALC_B() C C...Calculate the normal vector at control points and bound vortex midpoints C +C Since we cannot seperate the encalc routine for direct mesh assignment we have to make it a branch here DO j=1,nstrip +C + IF (lsurfmsh(lssurf(j))) THEN +C Calculate normal vector for the strip (normal to X axis) +C we can't just interpolate this anymore given that +C the strip is no longer necessarily linear chordwise +C We want the spanwise unit vector for the strip at the +C chordwise location specified by SAXFR (usually set to 0.25) +C Loop over all panels in the strip until we find the one that contains +C the SAXFR position in it's projected chord. Since the panels themselves are still linear +C we can just use the bound vortex unit vector of that panel as +C the spanwise unit vector of the strip at SAXFR +C SAB: This is slow, find a better way to do this +C +C +C + dchstrip = 0.0 + CALL PUSHINTEGER4(i) + ad_count = 1 +C compute the spanwise unit vector for Vperp def + searchsaxfr:DO i=ijfrst(j),ijfrst(j)+(nvstrp(j)-1) + dchstrip = dchstrip + dxstrpv(i) + IF (dchstrip .GE. chord(j)*saxfr) THEN + GOTO 100 + ELSE + CALL PUSHINTEGER4(i) + ad_count = ad_count + 1 + END IF + ENDDO searchsaxfr + CALL PUSHCONTROL1B(0) + CALL PUSHINTEGER4(ad_count) + GOTO 110 + 100 CALL PUSHCONTROL1B(1) + CALL PUSHINTEGER4(ad_count) +C +C + 110 CALL PUSHREAL8(dxt) + dxt = rv2msh(1, i) - rv1msh(1, i) + CALL PUSHREAL8(dyt) + dyt = rv2msh(2, i) - rv1msh(2, i) + CALL PUSHREAL8(dzt) + dzt = rv2msh(3, i) - rv1msh(3, i) +C + CALL PUSHCONTROL1B(0) + ELSE +C original encalc routine for standard AVL geometry C C...Calculate normal vector for the strip (normal to X axis) - CALL PUSHINTEGER4(i) - i = ijfrst(j) - dxle = rv2(1, i) - rv1(1, i) - dyle = rv2(2, i) - rv1(2, i) - dzle = rv2(3, i) - rv1(3, i) + CALL PUSHINTEGER4(i) + i = ijfrst(j) + dxle = rv2(1, i) - rv1(1, i) + dyle = rv2(2, i) - rv1(2, i) + dzle = rv2(3, i) - rv1(3, i) C AXLE = (RV2(1,I)+RV1(1,I))*0.5 C AYLE = (RV2(2,I)+RV1(2,I))*0.5 C AZLE = (RV2(3,I)+RV1(3,I))*0.5 C - i = ijfrst(j) + (nvstrp(j)-1) - dxte = rv2(1, i) - rv1(1, i) - dyte = rv2(2, i) - rv1(2, i) - dzte = rv2(3, i) - rv1(3, i) + i = ijfrst(j) + (nvstrp(j)-1) + dxte = rv2(1, i) - rv1(1, i) + dyte = rv2(2, i) - rv1(2, i) + dzte = rv2(3, i) - rv1(3, i) C AXTE = (RV2(1,I)+RV1(1,I))*0.5 C AYTE = (RV2(2,I)+RV1(2,I))*0.5 C AZTE = (RV2(3,I)+RV1(3,I))*0.5 C - CALL PUSHREAL8(dxt) - dxt = (1.0-saxfr)*dxle + saxfr*dxte - CALL PUSHREAL8(dyt) - dyt = (1.0-saxfr)*dyle + saxfr*dyte - CALL PUSHREAL8(dzt) - dzt = (1.0-saxfr)*dzle + saxfr*dzte + CALL PUSHREAL8(dxt) + dxt = (1.0-saxfr)*dxle + saxfr*dxte + CALL PUSHREAL8(dyt) + dyt = (1.0-saxfr)*dyle + saxfr*dyte + CALL PUSHREAL8(dzt) + dzt = (1.0-saxfr)*dzle + saxfr*dzte C + CALL PUSHCONTROL1B(1) + END IF C - ensy(j) = -(dzt/SQRT(dyt*dyt+dzt*dzt)) - ensz(j) = dyt/SQRT(dyt*dyt+dzt*dzt) C +C Treffz plane normals C + ensy(j) = -(dzt/SQRT(dyt*dyt+dzt*dzt)) + ensz(j) = dyt/SQRT(dyt*dyt+dzt*dzt) C CALL PUSHREAL8(es(1)) es(1) = 0. @@ -2726,18 +4641,40 @@ SUBROUTINE ENCALC_B() C CALL PUSHINTEGER4(i) i = ijfrst(j) + (ii-1) +C + IF (lsurfmsh(lssurf(j))) THEN +C Define unit vector along bound leg +C right h.v. pt - left h.v. pt + CALL PUSHREAL8(dxb) + dxb = rv2msh(1, i) - rv1msh(1, i) + CALL PUSHREAL8(dyb) + dyb = rv2msh(2, i) - rv1msh(2, i) + CALL PUSHREAL8(dzb) + dzb = rv2msh(3, i) - rv1msh(3, i) + CALL PUSHCONTROL1B(0) + ELSE C C...Define unit vector along bound leg C right h.v. pt - left h.v. pt - dxb = rv2(1, i) - rv1(1, i) - dyb = rv2(2, i) - rv1(2, i) - dzb = rv2(3, i) - rv1(3, i) + CALL PUSHREAL8(dxb) + dxb = rv2(1, i) - rv1(1, i) + CALL PUSHREAL8(dyb) + dyb = rv2(2, i) - rv1(2, i) + CALL PUSHREAL8(dzb) + dzb = rv2(3, i) - rv1(3, i) + CALL PUSHCONTROL1B(1) + END IF CALL PUSHREAL8(emag) emag = SQRT(dxb**2 + dyb**2 + dzb**2) CALL PUSHREAL8(eb(1)) eb(1) = dxb/emag CALL PUSHREAL8(eb(2)) eb(2) = dyb/emag +C First start by combining the contributions to the panel +C incidence from AVL incidence and camberline slope variables +C these are not actual geometric transformations of the mesh +C but rather further modifications to the chordwise vector that +C will get used to compute normals CALL PUSHREAL8(eb(3)) eb(3) = dzb/emag C @@ -2755,20 +4692,75 @@ SUBROUTINE ENCALC_B() C CALL PUSHREAL8(sinc) sinc = SIN(ang) + CALL PUSHREAL8(cosc) cosc = COS(ang) - CALL PUSHREAL8(ec(1)) - ec(1) = cosc - CALL PUSHREAL8(ec(2)) - ec(2) = -(sinc*es(2)) -C EC = rotation of strip normal vector? or along chord? - CALL PUSHREAL8(ec(3)) - ec(3) = -(sinc*es(3)) +C + IF (lsurfmsh(lssurf(j))) THEN +C direct mesh assignemnt branch +C now we compute the chordwise panel vector +C note that panel`s chordwise vector has contributions +C from both the geometry itself and the incidence modification +C from the AVL AINC and camber slope variables +C Get the geometric chordwise vector using RVMSH and RCMSH which should +C be located in the same plane given that each individual panel is a +C plane +C +C + CALL PUSHREAL8(emag) + emag = SQRT((rcmsh(1, i)-rvmsh(1, i))**2 + (rcmsh(2, i)- + + rvmsh(2, i))**2 + (rcmsh(3, i)-rvmsh(3, i))**2) + CALL PUSHREAL8(ec_msh(1)) + ec_msh(1) = (rcmsh(1, i)-rvmsh(1, i))/emag + CALL PUSHREAL8(ec_msh(2)) + ec_msh(2) = (rcmsh(2, i)-rvmsh(2, i))/emag +C Now we have to rotate this vector by the incidence contribution from AINC and CAMBER +C However, this rotation needs to be done about the local y-axis of the wing +C Earlier we computed ES the normal vector of the strip projected to the Trefftz plane +C The axis we need to rotate about is the one purpendicular to this ES. +C As a result all panel normals in a given strip will be rotated about the same axis defined by the that strip +C The components of the rotation axis are obtained from ES as follows +C rot_axis(1) = 0 +C rot_axis(2) = -ES(3) +C rot_axis(3) = ES(2) +C We can then multiply ec_msh by the rotation matrix for a rotation about an arbitrary axis +C see https://pubs.aip.org/aapt/ajp/article/44/1/63/1050167/Formalism-for-the-rotation-matrix-of-rotations +C Note that standard AVL also does this exact same thing but since they always rotate the vector [1,0,0] +C the result collapses into the ridiculously simple expression for EC that you see in the other branch + CALL PUSHREAL8(ec_msh(3)) + ec_msh(3) = (rcmsh(3, i)-rvmsh(3, i))/emag +C +C + CALL PUSHREAL8(ec(1)) + ec(1) = cosc*ec_msh(1) + es(2)*sinc*ec_msh(2) + es(3)*sinc* + + ec_msh(3) + CALL PUSHREAL8(ec(2)) + ec(2) = -(es(2)*sinc) + (es(3)**2*(1-cosc)+cosc)*ec_msh(2) - + + es(2)*es(3)*(1-cosc)*ec_msh(3) + CALL PUSHREAL8(ec(3)) + ec(3) = -(es(3)*sinc*ec_msh(1)) - es(2)*es(3)*(1-cosc)* + + ec_msh(2) + (es(2)**2*(1-cosc)+cosc)*ec_msh(3) +C + CALL PUSHCONTROL1B(1) + ELSE + CALL PUSHREAL8(ec(1)) + ec(1) = cosc + CALL PUSHREAL8(ec(2)) + ec(2) = -(sinc*es(2)) + CALL PUSHREAL8(ec(3)) + ec(3) = -(sinc*es(3)) + CALL PUSHCONTROL1B(0) + END IF C C...Normal vector is perpendicular to camberline vector and to the bound leg CALL PUSHREAL8ARRAY(ecxb, 3) CALL CROSS(ec, eb, ecxb) CALL PUSHREAL8(emag) emag = SQRT(ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2) +C This section is identical to the normal vector at the control +C point. The only different is that the AVL camberline slope +C is taken at the bound vortex point rather than the control point +C the geometric contributions to the normal vector at both of these +C point is identical as the lie in the plane of the same panel. IF (emag .NE. 0.0) THEN CALL PUSHREAL8(enc(1, i)) enc(1, i) = ecxb(1)/emag @@ -2802,19 +4794,40 @@ SUBROUTINE ENCALC_B() C CALL PUSHREAL8(sinc) sinc = SIN(ang) + CALL PUSHREAL8(cosc) cosc = COS(ang) - CALL PUSHREAL8(ec(1)) - ec(1) = cosc - CALL PUSHREAL8(ec(2)) - ec(2) = -(sinc*es(2)) - CALL PUSHREAL8(ec(3)) - ec(3) = -(sinc*es(3)) + IF (lsurfmsh(lssurf(j))) THEN +C direct mesh assignment branch +C see explanation in section above for control point normals +C ec_msh was already computed in that section + CALL PUSHREAL8(ec(1)) + ec(1) = cosc*ec_msh(1) + es(2)*sinc*ec_msh(2) + es(3)*sinc* + + ec_msh(3) + CALL PUSHREAL8(ec(2)) + ec(2) = -(es(2)*sinc) + (es(3)**2*(1-cosc)+cosc)*ec_msh(2) - + + es(2)*es(3)*(1-cosc)*ec_msh(3) + CALL PUSHREAL8(ec(3)) + ec(3) = -(es(3)*sinc*ec_msh(1)) - es(2)*es(3)*(1-cosc)* + + ec_msh(2) + (es(2)**2*(1-cosc)+cosc)*ec_msh(3) +C + CALL PUSHCONTROL1B(1) + ELSE + CALL PUSHREAL8(ec(1)) + ec(1) = cosc + CALL PUSHREAL8(ec(2)) + ec(2) = -(sinc*es(2)) + CALL PUSHREAL8(ec(3)) + ec(3) = -(sinc*es(3)) + CALL PUSHCONTROL1B(0) + END IF C C...Normal vector is perpendicular to camberline vector and to the bound leg CALL PUSHREAL8ARRAY(ecxb, 3) CALL CROSS(ec, eb, ecxb) CALL PUSHREAL8(emag) emag = SQRT(ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2) +C this is a pure rotation of the normal vector +C the geometric contribution from the mesh is already accounted for IF (emag .NE. 0.0) THEN CALL PUSHREAL8(env(1, i)) env(1, i) = ecxb(1)/emag @@ -2896,6 +4909,26 @@ SUBROUTINE ENCALC_B() ENDDO CALL PUSHINTEGER4(ii - 1) ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rv1msh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rv2msh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rvmsh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO + DO ii1=1,nvor + DO ii2=1,3 + rcmsh_diff(ii2, ii1) = 0.D0 + ENDDO + ENDDO DO ii1=1,NSTRIP ainc_diff(ii1) = 0.D0 ENDDO @@ -2940,10 +4973,12 @@ SUBROUTINE ENCALC_B() DO ii1=1,3 ecxb_diff(ii1) = 0.D0 ENDDO + DO ii1=1,3 + ec_msh_diff(ii1) = 0.D0 + ENDDO DO j=nstrip,1,-1 CALL POPINTEGER4(ad_to) DO ii=ad_to,1,-1 - i = ijfrst(j) + (ii-1) DO n=ncontrol,1,-1 CALL POPCONTROL1B(branch) IF (branch .NE. 0) THEN @@ -3029,26 +5064,62 @@ SUBROUTINE ENCALC_B() END IF CALL POPREAL8(emag) IF (ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2 .EQ. 0.D0) THEN - temp_diff0 = 0.D0 + temp_diff5 = 0.D0 ELSE - temp_diff0 = emag_diff/(2.0*SQRT(ecxb(1)**2+ecxb(2)**2+ecxb( + temp_diff5 = emag_diff/(2.0*SQRT(ecxb(1)**2+ecxb(2)**2+ecxb( + 3)**2)) END IF - ecxb_diff(1) = ecxb_diff(1) + 2*ecxb(1)*temp_diff0 - ecxb_diff(2) = ecxb_diff(2) + 2*ecxb(2)*temp_diff0 - ecxb_diff(3) = ecxb_diff(3) + 2*ecxb(3)*temp_diff0 + ecxb_diff(1) = ecxb_diff(1) + 2*ecxb(1)*temp_diff5 + ecxb_diff(2) = ecxb_diff(2) + 2*ecxb(2)*temp_diff5 + ecxb_diff(3) = ecxb_diff(3) + 2*ecxb(3)*temp_diff5 CALL POPREAL8ARRAY(ecxb, 3) CALL CROSS_B(ec, ec_diff, eb, eb_diff, ecxb, ecxb_diff) - CALL POPREAL8(ec(3)) - sinc_diff = -(es(3)*ec_diff(3)) - es(2)*ec_diff(2) - es_diff(3) = es_diff(3) - sinc*ec_diff(3) - ec_diff(3) = 0.D0 - CALL POPREAL8(ec(2)) - es_diff(2) = es_diff(2) - sinc*ec_diff(2) - ec_diff(2) = 0.D0 - CALL POPREAL8(ec(1)) - cosc_diff = ec_diff(1) - ec_diff(1) = 0.D0 + n = 0 + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + CALL POPREAL8(ec(3)) + sinc_diff = -(es(3)*ec_diff(3)) - es(2)*ec_diff(2) + es_diff(3) = es_diff(3) - sinc*ec_diff(3) + ec_diff(3) = 0.D0 + CALL POPREAL8(ec(2)) + es_diff(2) = es_diff(2) - sinc*ec_diff(2) + ec_diff(2) = 0.D0 + CALL POPREAL8(ec(1)) + cosc_diff = ec_diff(1) + ec_diff(1) = 0.D0 + ELSE + CALL POPREAL8(ec(3)) + temp_diff5 = ec_msh(3)*ec_diff(3) + temp_diff1 = -((1-cosc)*ec_msh(2)*ec_diff(3)) + temp_diff4 = -(es(2)*es(3)*ec_diff(3)) + es_diff(3) = es_diff(3) + es(2)*temp_diff1 - sinc*ec_msh(1)* + + ec_diff(3) + sinc_diff = (es(2)*ec_msh(2)+es(3)*ec_msh(3))*ec_diff(1) - + + es(3)*ec_msh(1)*ec_diff(3) - es(2)*ec_diff(2) + ec_msh_diff(1) = ec_msh_diff(1) + cosc*ec_diff(1) - es(3)* + + sinc*ec_diff(3) + cosc_diff = (1.0-es(2)**2)*temp_diff5 - ec_msh(2)*temp_diff4 + ec_msh_diff(2) = ec_msh_diff(2) + (1-cosc)*temp_diff4 + (es( + + 3)**2*(1-cosc)+cosc)*ec_diff(2) + es(2)*sinc*ec_diff(1) + es_diff(2) = es_diff(2) + es(3)*temp_diff1 + CALL POPREAL8(ec(2)) + temp_diff1 = -((1-cosc)*ec_msh(3)*ec_diff(2)) + es_diff(2) = es_diff(2) + 2*es(2)*(1-cosc)*temp_diff5 + es(3 + + )*temp_diff1 - sinc*ec_diff(2) + sinc*ec_msh(2)*ec_diff(1) + temp_diff5 = ec_msh(2)*ec_diff(2) + temp_diff4 = -(es(2)*es(3)*ec_diff(2)) + ec_msh_diff(3) = ec_msh_diff(3) + (es(2)**2*(1-cosc)+cosc)* + + ec_diff(3) + (1-cosc)*temp_diff4 + es(3)*sinc*ec_diff(1) + ec_diff(3) = 0.D0 + ec_diff(2) = 0.D0 + cosc_diff = cosc_diff + (1.0-es(3)**2)*temp_diff5 - ec_msh(3 + + )*temp_diff4 + ec_msh(1)*ec_diff(1) + es_diff(3) = es_diff(3) + es(2)*temp_diff1 + 2*es(3)*(1-cosc + + )*temp_diff5 + sinc*ec_msh(3)*ec_diff(1) + CALL POPREAL8(ec(1)) + ec_diff(1) = 0.D0 + END IF + CALL POPREAL8(cosc) ang_diff = COS(ang)*sinc_diff - SIN(ang)*cosc_diff CALL POPREAL8(sinc) DO n=ndesign,1,-1 @@ -3084,60 +5155,150 @@ SUBROUTINE ENCALC_B() END IF CALL POPREAL8(emag) IF (ecxb(1)**2 + ecxb(2)**2 + ecxb(3)**2 .EQ. 0.D0) THEN - temp_diff0 = 0.D0 + temp_diff5 = 0.D0 ELSE - temp_diff0 = emag_diff/(2.0*SQRT(ecxb(1)**2+ecxb(2)**2+ecxb( + temp_diff5 = emag_diff/(2.0*SQRT(ecxb(1)**2+ecxb(2)**2+ecxb( + 3)**2)) END IF - ecxb_diff(1) = ecxb_diff(1) + 2*ecxb(1)*temp_diff0 - ecxb_diff(2) = ecxb_diff(2) + 2*ecxb(2)*temp_diff0 - ecxb_diff(3) = ecxb_diff(3) + 2*ecxb(3)*temp_diff0 + ecxb_diff(1) = ecxb_diff(1) + 2*ecxb(1)*temp_diff5 + ecxb_diff(2) = ecxb_diff(2) + 2*ecxb(2)*temp_diff5 + ecxb_diff(3) = ecxb_diff(3) + 2*ecxb(3)*temp_diff5 CALL POPREAL8ARRAY(ecxb, 3) CALL CROSS_B(ec, ec_diff, eb, eb_diff, ecxb, ecxb_diff) - CALL POPREAL8(ec(3)) - sinc_diff = -(es(3)*ec_diff(3)) - es(2)*ec_diff(2) - es_diff(3) = es_diff(3) - sinc*ec_diff(3) - ec_diff(3) = 0.D0 - CALL POPREAL8(ec(2)) - es_diff(2) = es_diff(2) - sinc*ec_diff(2) - ec_diff(2) = 0.D0 - CALL POPREAL8(ec(1)) - cosc_diff = ec_diff(1) - ec_diff(1) = 0.D0 + n = 0 + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + CALL POPREAL8(ec(3)) + sinc_diff = -(es(3)*ec_diff(3)) - es(2)*ec_diff(2) + es_diff(3) = es_diff(3) - sinc*ec_diff(3) + ec_diff(3) = 0.D0 + CALL POPREAL8(ec(2)) + es_diff(2) = es_diff(2) - sinc*ec_diff(2) + ec_diff(2) = 0.D0 + CALL POPREAL8(ec(1)) + cosc_diff = ec_diff(1) + ec_diff(1) = 0.D0 + ELSE + temp_diff4 = -(es(2)*es(3)*ec_diff(3)) + cosc_diff = -(ec_msh(2)*temp_diff4) + ec_msh_diff(2) = ec_msh_diff(2) + (1-cosc)*temp_diff4 + (es( + + 3)**2*(1-cosc)+cosc)*ec_diff(2) + es(2)*sinc*ec_diff(1) + temp_diff4 = -(es(2)*es(3)*ec_diff(2)) + CALL POPREAL8(ec(3)) + temp_diff5 = ec_msh(3)*ec_diff(3) + ec_msh_diff(3) = ec_msh_diff(3) + (es(2)**2*(1-cosc)+cosc)* + + ec_diff(3) + (1-cosc)*temp_diff4 + es(3)*sinc*ec_diff(1) + temp_diff1 = -((1-cosc)*ec_msh(2)*ec_diff(3)) + es_diff(3) = es_diff(3) + es(2)*temp_diff1 - sinc*ec_msh(1)* + + ec_diff(3) + sinc_diff = (es(2)*ec_msh(2)+es(3)*ec_msh(3))*ec_diff(1) - + + es(3)*ec_msh(1)*ec_diff(3) - es(2)*ec_diff(2) + ec_msh_diff(1) = ec_msh_diff(1) + cosc*ec_diff(1) - es(3)* + + sinc*ec_diff(3) + ec_diff(3) = 0.D0 + es_diff(2) = es_diff(2) + es(3)*temp_diff1 + 2*es(2)*(1-cosc + + )*temp_diff5 + CALL POPREAL8(ec(2)) + temp_diff1 = ec_msh(2)*ec_diff(2) + cosc_diff = cosc_diff + (1.0-es(2)**2)*temp_diff5 + (1.0-es( + + 3)**2)*temp_diff1 - ec_msh(3)*temp_diff4 + ec_msh(1)* + + ec_diff(1) + temp_diff5 = -((1-cosc)*ec_msh(3)*ec_diff(2)) + es_diff(2) = es_diff(2) + es(3)*temp_diff5 - sinc*ec_diff(2) + + + sinc*ec_msh(2)*ec_diff(1) + ec_diff(2) = 0.D0 + es_diff(3) = es_diff(3) + es(2)*temp_diff5 + 2*es(3)*(1-cosc + + )*temp_diff1 + sinc*ec_msh(3)*ec_diff(1) + CALL POPREAL8(ec(1)) + ec_diff(1) = 0.D0 + CALL POPREAL8(ec_msh(3)) + temp_diff4 = ec_msh_diff(3)/emag + ec_msh_diff(3) = 0.D0 + rcmsh_diff(3, i) = rcmsh_diff(3, i) + temp_diff4 + rvmsh_diff(3, i) = rvmsh_diff(3, i) - temp_diff4 + emag_diff = -((rcmsh(3, i)-rvmsh(3, i))*temp_diff4/emag) + CALL POPREAL8(ec_msh(2)) + temp_diff4 = ec_msh_diff(2)/emag + ec_msh_diff(2) = 0.D0 + rcmsh_diff(2, i) = rcmsh_diff(2, i) + temp_diff4 + rvmsh_diff(2, i) = rvmsh_diff(2, i) - temp_diff4 + emag_diff = emag_diff - (rcmsh(2, i)-rvmsh(2, i))*temp_diff4 + + /emag + CALL POPREAL8(ec_msh(1)) + temp_diff4 = ec_msh_diff(1)/emag + ec_msh_diff(1) = 0.D0 + emag_diff = emag_diff - (rcmsh(1, i)-rvmsh(1, i))*temp_diff4 + + /emag + CALL POPREAL8(emag) + temp0 = rcmsh(3, i) - rvmsh(3, i) + temp = rcmsh(2, i) - rvmsh(2, i) + temp1 = rcmsh(1, i) - rvmsh(1, i) + IF (temp1**2 + temp**2 + temp0**2 .EQ. 0.D0) THEN + temp_diff2 = 0.D0 + ELSE + temp_diff2 = emag_diff/(2.0*SQRT(temp1**2+temp**2+temp0**2 + + )) + END IF + temp_diff3 = 2*temp1*temp_diff2 + rcmsh_diff(1, i) = rcmsh_diff(1, i) + temp_diff4 + + + temp_diff3 + rvmsh_diff(1, i) = rvmsh_diff(1, i) - temp_diff4 - + + temp_diff3 + temp_diff = 2*temp*temp_diff2 + temp_diff0 = 2*temp0*temp_diff2 + rcmsh_diff(3, i) = rcmsh_diff(3, i) + temp_diff0 + rvmsh_diff(3, i) = rvmsh_diff(3, i) - temp_diff0 + rcmsh_diff(2, i) = rcmsh_diff(2, i) + temp_diff + rvmsh_diff(2, i) = rvmsh_diff(2, i) - temp_diff + END IF + CALL POPREAL8(cosc) ang_diff = COS(ang)*sinc_diff - SIN(ang)*cosc_diff CALL POPREAL8(sinc) DO n=ndesign,1,-1 ainc_g_diff(j, n) = ainc_g_diff(j, n) + deldes(n)*ang_diff ENDDO - dxb = rv2(1, i) - rv1(1, i) - dyb = rv2(2, i) - rv1(2, i) - CALL POPREAL8(ang) - ainc_diff(j) = ainc_diff(j) + ang_diff - slopec_diff(i) = slopec_diff(i) - ang_diff/(1.0+slopec(i)**2) - dzb = rv2(3, i) - rv1(3, i) - CALL POPREAL8(eb(3)) emag_diff = -(dzb*eb_diff(3)/emag**2) - dyb*eb_diff(2)/emag**2 + - dxb*eb_diff(1)/emag**2 - CALL POPREAL8(eb(2)) - CALL POPREAL8(eb(1)) IF (dxb**2 + dyb**2 + dzb**2 .EQ. 0.D0) THEN - temp_diff0 = 0.D0 + temp_diff1 = 0.D0 ELSE - temp_diff0 = emag_diff/(2.0*SQRT(dxb**2+dyb**2+dzb**2)) + temp_diff1 = emag_diff/(2.0*SQRT(dxb**2+dyb**2+dzb**2)) END IF - dzb_diff = eb_diff(3)/emag + 2*dzb*temp_diff0 + CALL POPREAL8(ang) + ainc_diff(j) = ainc_diff(j) + ang_diff + slopec_diff(i) = slopec_diff(i) - ang_diff/(1.0+slopec(i)**2) + CALL POPREAL8(eb(3)) + dzb_diff = eb_diff(3)/emag + 2*dzb*temp_diff1 eb_diff(3) = 0.D0 - dyb_diff = eb_diff(2)/emag + 2*dyb*temp_diff0 + CALL POPREAL8(eb(2)) + dyb_diff = eb_diff(2)/emag + 2*dyb*temp_diff1 eb_diff(2) = 0.D0 - dxb_diff = eb_diff(1)/emag + 2*dxb*temp_diff0 + CALL POPREAL8(eb(1)) + dxb_diff = eb_diff(1)/emag + 2*dxb*temp_diff1 eb_diff(1) = 0.D0 CALL POPREAL8(emag) - rv2_diff(3, i) = rv2_diff(3, i) + dzb_diff - rv1_diff(3, i) = rv1_diff(3, i) - dzb_diff - rv2_diff(2, i) = rv2_diff(2, i) + dyb_diff - rv1_diff(2, i) = rv1_diff(2, i) - dyb_diff - rv2_diff(1, i) = rv2_diff(1, i) + dxb_diff - rv1_diff(1, i) = rv1_diff(1, i) - dxb_diff + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + CALL POPREAL8(dzb) + rv2msh_diff(3, i) = rv2msh_diff(3, i) + dzb_diff + rv1msh_diff(3, i) = rv1msh_diff(3, i) - dzb_diff + CALL POPREAL8(dyb) + rv2msh_diff(2, i) = rv2msh_diff(2, i) + dyb_diff + rv1msh_diff(2, i) = rv1msh_diff(2, i) - dyb_diff + CALL POPREAL8(dxb) + rv2msh_diff(1, i) = rv2msh_diff(1, i) + dxb_diff + rv1msh_diff(1, i) = rv1msh_diff(1, i) - dxb_diff + ELSE + CALL POPREAL8(dzb) + rv2_diff(3, i) = rv2_diff(3, i) + dzb_diff + rv1_diff(3, i) = rv1_diff(3, i) - dzb_diff + CALL POPREAL8(dyb) + rv2_diff(2, i) = rv2_diff(2, i) + dyb_diff + rv1_diff(2, i) = rv1_diff(2, i) - dyb_diff + CALL POPREAL8(dxb) + rv2_diff(1, i) = rv2_diff(1, i) + dxb_diff + rv1_diff(1, i) = rv1_diff(1, i) - dxb_diff + END IF DO n=ncontrol,1,-1 enc_d_diff(3, i, n) = 0.D0 enc_d_diff(2, i, n) = 0.D0 @@ -3153,15 +5314,6 @@ SUBROUTINE ENCALC_B() es_diff(2) = 0.D0 CALL POPREAL8(es(1)) es_diff(1) = 0.D0 - azle_diff = (1.0-saxfr)*zsref_diff(j) - azte_diff = saxfr*zsref_diff(j) - zsref_diff(j) = 0.D0 - ayle_diff = (1.0-saxfr)*ysref_diff(j) - ayte_diff = saxfr*ysref_diff(j) - ysref_diff(j) = 0.D0 - axle_diff = (1.0-saxfr)*xsref_diff(j) - axte_diff = saxfr*xsref_diff(j) - xsref_diff(j) = 0.D0 temp0 = dyt*dyt + dzt*dzt temp = SQRT(temp0) IF (temp0 .EQ. 0.D0) THEN @@ -3213,37 +5365,69 @@ SUBROUTINE ENCALC_B() dyt_diff = dyt_diff + 2*dyt*temp_diff0 + 2*dyt*temp_diff dzt_diff = dzt_diff + 2*dzt*temp_diff0 + 2*dzt*temp_diff ess_diff(1, j) = 0.D0 - CALL POPREAL8(dzt) - dzle_diff = (1.0-saxfr)*dzt_diff - dzte_diff = saxfr*dzt_diff - CALL POPREAL8(dyt) - dyle_diff = (1.0-saxfr)*dyt_diff - dyte_diff = saxfr*dyt_diff - CALL POPREAL8(dxt) - dxle_diff = (1.0-saxfr)*dxt_diff - dxte_diff = saxfr*dxt_diff - rv_diff(3, i) = rv_diff(3, i) + azte_diff - rv_diff(2, i) = rv_diff(2, i) + ayte_diff - rv_diff(1, i) = rv_diff(1, i) + axte_diff - rv2_diff(3, i) = rv2_diff(3, i) + dzte_diff - rv1_diff(3, i) = rv1_diff(3, i) - dzte_diff - rv2_diff(2, i) = rv2_diff(2, i) + dyte_diff - rv1_diff(2, i) = rv1_diff(2, i) - dyte_diff - rv2_diff(1, i) = rv2_diff(1, i) + dxte_diff - rv1_diff(1, i) = rv1_diff(1, i) - dxte_diff - i = ijfrst(j) - rv_diff(3, i) = rv_diff(3, i) + azle_diff - rv_diff(2, i) = rv_diff(2, i) + ayle_diff - rv_diff(1, i) = rv_diff(1, i) + axle_diff - rv2_diff(3, i) = rv2_diff(3, i) + dzle_diff - rv1_diff(3, i) = rv1_diff(3, i) - dzle_diff - rv2_diff(2, i) = rv2_diff(2, i) + dyle_diff - rv1_diff(2, i) = rv1_diff(2, i) - dyle_diff - rv2_diff(1, i) = rv2_diff(1, i) + dxle_diff - rv1_diff(1, i) = rv1_diff(1, i) - dxle_diff - CALL POPINTEGER4(i) + CALL POPCONTROL1B(branch) + IF (branch .EQ. 0) THEN + rvmsh_diff(3, i) = rvmsh_diff(3, i) + zsref_diff(j) + zsref_diff(j) = 0.D0 + rvmsh_diff(2, i) = rvmsh_diff(2, i) + ysref_diff(j) + ysref_diff(j) = 0.D0 + rvmsh_diff(1, i) = rvmsh_diff(1, i) + xsref_diff(j) + xsref_diff(j) = 0.D0 + CALL POPREAL8(dzt) + rv2msh_diff(3, i) = rv2msh_diff(3, i) + dzt_diff + rv1msh_diff(3, i) = rv1msh_diff(3, i) - dzt_diff + CALL POPREAL8(dyt) + rv2msh_diff(2, i) = rv2msh_diff(2, i) + dyt_diff + rv1msh_diff(2, i) = rv1msh_diff(2, i) - dyt_diff + CALL POPREAL8(dxt) + rv2msh_diff(1, i) = rv2msh_diff(1, i) + dxt_diff + rv1msh_diff(1, i) = rv1msh_diff(1, i) - dxt_diff + CALL POPINTEGER4(ad_count) + DO i0=1,ad_count + IF (i0 .EQ. 1) CALL POPCONTROL1B(branch) + CALL POPINTEGER4(i) + ENDDO + ELSE + azle_diff = (1.0-saxfr)*zsref_diff(j) + azte_diff = saxfr*zsref_diff(j) + zsref_diff(j) = 0.D0 + ayle_diff = (1.0-saxfr)*ysref_diff(j) + ayte_diff = saxfr*ysref_diff(j) + ysref_diff(j) = 0.D0 + axle_diff = (1.0-saxfr)*xsref_diff(j) + axte_diff = saxfr*xsref_diff(j) + xsref_diff(j) = 0.D0 + CALL POPREAL8(dzt) + dzle_diff = (1.0-saxfr)*dzt_diff + dzte_diff = saxfr*dzt_diff + CALL POPREAL8(dyt) + dyle_diff = (1.0-saxfr)*dyt_diff + dyte_diff = saxfr*dyt_diff + CALL POPREAL8(dxt) + dxle_diff = (1.0-saxfr)*dxt_diff + dxte_diff = saxfr*dxt_diff + rv_diff(3, i) = rv_diff(3, i) + azte_diff + rv_diff(2, i) = rv_diff(2, i) + ayte_diff + rv_diff(1, i) = rv_diff(1, i) + axte_diff + rv2_diff(3, i) = rv2_diff(3, i) + dzte_diff + rv1_diff(3, i) = rv1_diff(3, i) - dzte_diff + rv2_diff(2, i) = rv2_diff(2, i) + dyte_diff + rv1_diff(2, i) = rv1_diff(2, i) - dyte_diff + rv2_diff(1, i) = rv2_diff(1, i) + dxte_diff + rv1_diff(1, i) = rv1_diff(1, i) - dxte_diff + i = ijfrst(j) + rv_diff(3, i) = rv_diff(3, i) + azle_diff + rv_diff(2, i) = rv_diff(2, i) + ayle_diff + rv_diff(1, i) = rv_diff(1, i) + axle_diff + rv2_diff(3, i) = rv2_diff(3, i) + dzle_diff + rv1_diff(3, i) = rv1_diff(3, i) - dzle_diff + rv2_diff(2, i) = rv2_diff(2, i) + dyle_diff + rv1_diff(2, i) = rv1_diff(2, i) - dyle_diff + rv2_diff(1, i) = rv2_diff(1, i) + dxle_diff + rv1_diff(1, i) = rv1_diff(1, i) - dxle_diff + CALL POPINTEGER4(i) + END IF ENDDO END C ENCALC -C diff --git a/src/ad_src/reverse_ad_src/sgutil_b.f b/src/ad_src/reverse_ad_src/sgutil_b.f index 0ba8a1b..85c990a 100644 --- a/src/ad_src/reverse_ad_src/sgutil_b.f +++ b/src/ad_src/reverse_ad_src/sgutil_b.f @@ -391,6 +391,24 @@ SUBROUTINE AKIMA_B(x, x_diff, y, y_diff, n, xx, xx_diff, yy, C C C +C This is a extremely important funciton that is not +C documented for some reason. +C Inputs: +C NVC: NUMBER OF DESIRED POINTS IN ARRAY +C CSPACE: SPACING PARAMETER (-3<=PSPACE<=3). +C DEFINES POINT DISTRIBUTION +C TO BE USED AS FOLLOWS: +C PSPACE = 0 : EQUAL SPACING +C PSPACE = 1 : COSINE SPACING. +C PSPACE = 2 : SINE SPACING +C (CONCENTRATING POINTS NEAR 0). +C PSPACE = 3 : EQUAL SPACING. +C CLAF: CL alfa (needed to determine control point location) +C Outputs: +C XPT: Array of panel leading edge x-locations +C XVR: Array of vortex x-locations +C XSR: Array of source x-locations +C XCP: Array of control point x-locations SUBROUTINE CSPACER_B(nvc, cspace, claf, claf_diff, xpt, xvr, xsr, + xcp, xcp_diff) REAL xpt(*), xvr(*), xsr(*), xcp(*) @@ -442,6 +460,8 @@ SUBROUTINE CSPACER_B(nvc, cspace, claf, claf_diff, xpt, xvr, xsr, acsp = -cspace END IF ncsp = INT(acsp) +C Each of these provides a quarter panel chord offset for cosine, +C sine, and uniform spacing respectively. IF (ncsp .EQ. 0) THEN CALL PUSHCONTROL1B(0) f0 = 1.0 - acsp @@ -466,6 +486,12 @@ SUBROUTINE CSPACER_B(nvc, cspace, claf, claf_diff, xpt, xvr, xsr, C DO ivc=1,nvc C------ uniform +C eqv (IVC-1)/NVC +C quarter-chord +C half-chord +C quarter-chord + half-chord*claf +C Note: claf is a scaling factor so typically claf = 1 and the control point +C is at the three-quarter chord position of the panel C C------ cosine CALL PUSHREAL8(th1) diff --git a/src/amake.f b/src/amake.f index 99874a7..c0e06df 100644 --- a/src/amake.f +++ b/src/amake.f @@ -186,6 +186,8 @@ SUBROUTINE MAKESURF(ISURF) C C----- fudge spacing array to make nodes match up exactly with interior sections DO ISEC = 2, NSEC(ISURF)-1 + ! Throws an error in the case where the same node is the closest node + ! to two consecutive sections IPT1 = IPTLOC(ISEC-1) IPT2 = IPTLOC(ISEC ) IF(IPT1.EQ.IPT2) THEN @@ -637,10 +639,781 @@ SUBROUTINE MAKESURF(ISURF) C RETURN END ! MAKESURF + + integer function flatidx(idx_x, idx_y, idx_surf) + include 'AVL.INC' + ! store MFRST and NVC in the common block + integer idx_x, idx_y, idx_surf + flatidx = idx_x + (idx_y - 1) * (NVC(idx_surf)+1) + return + end function flatidx + + subroutine makesurf_mesh(isurf) +c-------------------------------------------------------------- +c Sets up all stuff for surface ISURF, +C using info from configuration input +C and the given mesh coordinate array. +c-------------------------------------------------------------- + INCLUDE 'AVL.INC' + ! input/output + integer isurf + + ! working variables (AVL original) + PARAMETER (KCMAX=50, + & KSMAX=500) + REAL CHSIN, CHCOS, CHSINL, CHSINR, CHCOSL, CHCOSR, AINCL, AINCR, + & CHORDL, CHORDR, CLAFL, CLAFR, SLOPEL, SLOPER, DXDX, ZU_L, + & ZL_L, ZU_R, ZL_R, ZL, ZR, SUM, WTOT, ASTRP + REAL CHSINL_G(NGMAX),CHCOSL_G(NGMAX), + & CHSINR_G(NGMAX),CHCOSR_G(NGMAX), + & XLED(NDMAX), XTED(NDMAX), GAINDA(NDMAX) + INTEGER ISCONL(NDMAX), ISCONR(NDMAX) + + ! working variables (OptVL additions) + real m1, m2, m3, f1, f2, fc, dc1, dc2, dc, a1, a2, a3, xptxind1, + & xptxind2 + real mesh_surf(3,(NVC(isurf)+1)*(NVS(isurf)+1)) + integer idx_vor, idx_strip, idx_sec, idx_dim, idx_coef, idx_x, + & idx_node, idx_nodel, idx_noder, idx_node_yp1, idx_node_nx, + & idx_node_nx_yp1, idx_y, nx, ny + + ! functions + integer flatidx + + ! Get data from common block + nx = NVC(isurf) + 1 + ny = NVS(isurf) + 1 + + ! Check MFRST + if (MFRST(isurf) .eq. 0) then + print *, "* Provide the index where the mesh begins for surface", + & isurf + end if + + ! Get the mesh for this surface from the the common block + mesh_surf = MSHBLK(:,MFRST(isurf):MFRST(isurf)+(nx*ny)-1) + + ! Perform input checks from makesurf (section check removed) + + IF(NVC(ISURF).GT.KCMAX) THEN + WRITE(*,*) '* makesurf_mesh: Array overflow. Increase KCMAX to', + & NVC(ISURF) + NVC(ISURF) = KCMAX + ENDIF + + IF(NVS(ISURF).GT.KSMAX) THEN + WRITE(*,*) '* makesurf_mesh: Array overflow. Increase KSMAX to', + & NVS(ISURF) + NVS(ISURF) = KSMAX + ENDIF + + ! Image flag set to indicate section definition direction + ! IMAGS= 1 defines edge 1 located at surface root edge + ! IMAGS=-1 defines edge 2 located at surface root edge (reflected surfaces) + IMAGS(ISURF) = 1 + + ! Start accumulating the element and strip index references + ! Accumulate the first element in surface + if (ISURF == 1) then + IFRST(ISURF) = 1 + else + IFRST(ISURF) = IFRST(ISURF-1) + NK(ISURF-1)*NJ(ISURF-1) + endif + + ! Accumulate the first strip in surface + if (ISURF == 1) then + JFRST(ISURF) = 1 + else + JFRST(ISURF) = JFRST(ISURF-1) + NJ(ISURF-1) + endif + + ! Set NK from input data (python layer will ensure this is consistent) + NK(ISURF) = NVC(ISURF) + + ! We need to start counting strips now since it is a global count + idx_strip = JFRST(ISURF) + + ! Bypass the entire spanwise node generation routine and go straight to store counters + ! skips MAKESURF 94-234 + ! Index of first strip in surface + ! This is normally used to store the index of each section in AVL + ! but since we use strips now each is effectively just a section + ! We assign this variable accordingly so as not to break anything else + IF (ISURF .EQ. 1) THEN + ICNTFRST(ISURF) = 1 + ELSE + ICNTFRST(ISURF) = ICNTFRST(ISURF-1) + NCNTSEC(ISURF-1) + ENDIF + ! Number of strips/sections in surface + NCNTSEC(ISURF) = NSEC(ISURF) + ! Store the spanwise index of each strip in each surface + DO ISEC = 1, NSEC(ISURF) + II = ICNTFRST(ISURF) + (ISEC-1) + ICNTSEC(II) = idx_strip + ENDDO + + + ! Apply the scaling and translations to the mesh as a whole + do idx_y = 1,ny + do idx_x = 1,nx + do idx_dim = 1,3 + idx_node = flatidx(idx_x, idx_y, isurf) + mesh_surf(idx_dim,idx_node) = XYZSCAL(idx_dim,isurf) + & *mesh_surf(idx_dim,idx_node) + XYZTRAN(idx_dim,isurf) + end do + end do + end do + + + ! Setup the strips + + ! Set spanwise elements to 0 + NJ(ISURF) = 0 + + ! Check control and design vars + IF(NCONTROL.GT.NDMAX) THEN + WRITE(*,*) '*** Too many control variables. Increase NDMAX to', + & NCONTROL + STOP + ENDIF + + IF(NDESIGN.GT.NGMAX) THEN + WRITE(*,*) '*** Too many design variables. Increase NGMAX to', + & NDESIGN + STOP + ENDIF + + ! Instead of looping over sections just loop over all strips in the surface + do ispan = 1,ny-1 !ispan loop + + + ! Set reference information for the strip + ! This code was used in the original to loop over strips in a section. + ! We will just reuse the variables here + idx_y = idx_strip - JFRST(isurf) + 1 + iptl = idx_y + iptr = idx_y + 1 + NJ(isurf) = NJ(isurf) + 1 + + + ! We need to compute the chord and claf values at the left and right edge of the strip + ! This code was used in the original to interpolate over sections. + ! We will just reuse here to interpolate over a strip which is trivial but avoids pointless code rewrites. + idx_node = flatidx(1,iptl,isurf) + idx_node_nx = flatidx(nx,iptl,isurf) + CHORDL = sqrt((mesh_surf(1,idx_node_nx)-mesh_surf(1,idx_node))**2 + & + (mesh_surf(3,idx_node_nx)-mesh_surf(3,idx_node))**2) + idx_node = flatidx(1,iptr,isurf) + idx_node_nx = flatidx(nx,iptr,isurf) + CHORDR = sqrt((mesh_surf(1,idx_node_nx)-mesh_surf(1,idx_node))**2 + & + (mesh_surf(3,idx_node_nx)-mesh_surf(3,idx_node))**2) + CLAFL = CLAF(iptl, isurf) + CLAFR = CLAF(iptr,isurf) + + ! Linearly interpolate the incidence projections over the STRIP + AINCL = AINCS(iptl,isurf)*DTR + ADDINC(isurf)*DTR + AINCR = AINCS(iptr,isurf)*DTR + ADDINC(isurf)*DTR + CHSINL = CHORDL*SIN(AINCL) + CHSINR = CHORDR*SIN(AINCR) + CHCOSL = CHORDL*COS(AINCL) + CHCOSR = CHORDR*COS(AINCR) + + ! We need to determine which controls belong to this section + ! Bring over the routine for this from makesurf but do it for each strip now + DO N = 1, NCONTROL + ISCONL(N) = 0 + ISCONR(N) = 0 + DO ISCON = 1, NSCON(iptl,isurf) + IF(ICONTD(ISCON,iptl,isurf) .EQ.N) ISCONL(N) = ISCON + ENDDO + DO ISCON = 1, NSCON(iptr,isurf) + IF(ICONTD(ISCON,iptr,isurf).EQ.N) ISCONR(N) = ISCON + ENDDO + ENDDO + + ! We need to determine which dvs belong to this strip + ! and setup the chord projection gains + ! Bring over the routine for this from makesurf but setup for strips + DO N = 1, NDESIGN + CHSINL_G(N) = 0. + CHSINR_G(N) = 0. + CHCOSL_G(N) = 0. + CHCOSR_G(N) = 0. + + DO ISDES = 1, NSDES(iptl,isurf) + IF(IDESTD(ISDES,iptl,isurf).EQ.N) THEN + CHSINL_G(N) = CHCOSL * GAING(ISDES,iptl,isurf)*DTR + CHCOSL_G(N) = -CHSINL * GAING(ISDES,iptl,isurf)*DTR + ENDIF + ENDDO + + DO ISDES = 1, NSDES(iptr,isurf) + IF(IDESTD(ISDES,iptr,isurf).EQ.N) THEN + CHSINR_G(N) = CHCOSR * GAING(ISDES,iptr,isurf)*DTR + CHCOSR_G(N) = -CHSINR * GAING(ISDES,iptr,isurf)*DTR + ENDIF + ENDDO + ENDDO + + + ! Set the strip geometry data + ! Note these computations assume the mesh is not necessarily planar + ! ultimately if/when we flatten the mesh into a planar one we will want + ! to use the leading edge positions and chords from the original input mesh + + + ! Strip left side + idx_node = flatidx(1,idx_y,isurf) + idx_node_nx = flatidx(nx,idx_y,isurf) + do idx_dim = 1,3 + RLE1(idx_dim,idx_strip) = mesh_surf(idx_dim,idx_node) + end do + + CHORD1(idx_strip) = sqrt((mesh_surf(1,idx_node_nx) + & -mesh_surf(1,idx_node))**2 + (mesh_surf(3,idx_node_nx) + & -mesh_surf(3,idx_node))**2) + + ! Strip right side + idx_node_yp1 = flatidx(1,idx_y+1,isurf) + idx_node_nx_yp1 = flatidx(nx,idx_y+1,isurf) + do idx_dim = 1,3 + RLE2(idx_dim,idx_strip) = mesh_surf(idx_dim,idx_node_yp1) + end do + CHORD2(idx_strip) = sqrt((mesh_surf(1,idx_node_nx_yp1) + & -mesh_surf(1,idx_node_yp1))**2 + (mesh_surf(3,idx_node_nx_yp1) + & -mesh_surf(3,idx_node_yp1))**2) + + ! Strip mid-point + do idx_dim = 1,3 + ! Since the strips are linear SPANWISE we can just interpolate + RLE(idx_dim,idx_strip) = (RLE1(idx_dim,idx_strip) + & + RLE2(idx_dim,idx_strip))/2. + end do + ! The strips are not necessarily linear chord wise but by definition the chord value is + ! so we can interpolate + CHORD(idx_strip) = (CHORD1(idx_strip)+CHORD2(idx_strip))/2. + + ! Strip geometric incidence angle at the mid-point + ! This is strip incidence angle is computed from the LE and TE points + ! of the given geometry and is completely independent of AINC + ! This quantity is needed to correctly handle nonplanar meshes and is only needed if the mesh isnt flattened + GINCSTRIP(idx_strip) = atan2(((mesh_surf(3,idx_node_nx) + & + mesh_surf(3,idx_node_nx_yp1))/2.- (mesh_surf(3,idx_node) + + & mesh_surf(3,idx_node_yp1))/2.), + & ((mesh_surf(1,idx_node_nx) + mesh_surf(1,idx_node_nx_yp1))/2. + & - (mesh_surf(1,idx_node) + mesh_surf(1,idx_node_yp1))/2.)) + + ! Strip width + m2 = mesh_surf(2,idx_node_yp1)-mesh_surf(2,idx_node) + m3 = mesh_surf(3,idx_node_yp1)-mesh_surf(3,idx_node) + WSTRIP(idx_strip) = sqrt(m2**2 + m3**2) + + ! Strip LE and TE sweep slopes + tanle(idx_strip) = (mesh_surf(1,idx_node_yp1) + & -mesh_surf(1,idx_node))/WSTRIP(idx_strip) + idx_node = flatidx(nx,idx_y,isurf) + idx_node_yp1 = flatidx(nx,idx_y+1,isurf) + tante(idx_strip) = (mesh_surf(1,idx_node_yp1) + & -mesh_surf(1,idx_node))/WSTRIP(idx_strip) + + ! Compute chord projections and strip twists + ! In AVL the AINCS are not interpolated. The chord projections are + ! So we have to replicate this effect. + + ! LINEAR interpolation over the strip: left, right, and midpoint + idx_nodel = flatidx(1,iptl,isurf) + idx_noder = flatidx(1,iptr,isurf) + + ! f1 = (mesh_surf(2,idx_node)-mesh_surf(2,idx_nodel))/ + ! & (mesh_surf(2,idx_noder)-mesh_surf(2,idx_nodel)) + ! f2 = (mesh_surf(2,idx_node_yp1)-mesh_surf(2,idx_nodel))/ + ! & (mesh_surf(2,idx_noder)-mesh_surf(2,idx_nodel)) + ! fc = (((mesh_surf(2,idx_node_yp1)+mesh_surf(2,idx_node))/2.) + ! & -mesh_surf(2,idx_nodel))/(mesh_surf(2,idx_noder) + ! & -mesh_surf(2,idx_nodel)) + + ! the above expressions will always evaluate to the following for individual strips + f1 = 0.0 + f2 = 1.0 + fc = 0.5 + + ! Strip left side incidence + ! CHSIN = CHSINL + f1*(CHSINR-CHSINL) + ! CHCOS = CHCOSL + f1*(CHCOSR-CHCOSL) + AINC1(idx_strip) = ATAN2(CHSINL,CHCOSL) + + ! Strip right side incidence + ! CHSIN = CHSINL + f2*(CHSINR-CHSINL) + ! CHCOS = CHCOSL + f2*(CHCOSR-CHCOSL) + AINC2(idx_strip) = ATAN2(CHSINR,CHCOSR) + + ! Strip mid-point incidence + CHSIN = CHSINL + fc*(CHSINR-CHSINL) + CHCOS = CHCOSL + fc*(CHCOSR-CHCOSL) + AINC(idx_strip) = ATAN2(CHSIN,CHCOS) + + ! Set dv gains for incidence angles + ! Bring over the routine for this from make surf + DO N = 1, NDESIGN + CHSIN_G = (1.0-FC)*CHSINL_G(N) + FC*CHSINR_G(N) + CHCOS_G = (1.0-FC)*CHCOSL_G(N) + FC*CHCOSR_G(N) + AINC_G(idx_strip,N) = (CHCOS*CHSIN_G - CHSIN*CHCOS_G) + & / (CHSIN**2 + CHCOS**2) + ENDDO + + ! We have to now setup any control surfaces we defined for this strip + ! Bring over the routine for this from makesurf but modified for a strip + DO N = 1, NCONTROL + ICL = ISCONL(N) + ICR = ISCONR(N) + + IF(ICL.EQ.0 .OR. ICR.EQ.0) THEN + ! no control effect here + GAINDA(N) = 0. + XLED(N) = 0. + XTED(N) = 0. + + VHINGE(1,idx_strip,N) = 0. + VHINGE(2,idx_strip,N) = 0. + VHINGE(3,idx_strip,N) = 0. + + VREFL(idx_strip,N) = 0. + + PHINGE(1,idx_strip,N) = 0. + PHINGE(2,idx_strip,N) = 0. + PHINGE(3,idx_strip,N) = 0. + + ELSE + ! control variable # N is active here + GAINDA(N) = GAIND(ICL,iptl ,isurf)*(1.0-FC) + & + GAIND(ICR,iptr,isurf)* FC + + ! SAB Note: This interpolation ensures that the hinge line is + ! is linear which I think it is an ok assumption for arbitrary wings as long as the user is aware + ! A curve hinge line could work if needed if we just interpolate XHINGED and scaled by local chord + XHD = CHORDL*XHINGED(ICL,iptl ,isurf)*(1.0-FC) + & + CHORDR*XHINGED(ICR,iptr,isurf)* FC + IF(XHD.GE.0.0) THEN + ! TE control surface, with hinge at XHD + XLED(N) = XHD + XTED(N) = CHORD(idx_strip) + ELSE + ! LE control surface, with hinge at -XHD + XLED(N) = 0.0 + XTED(N) = -XHD + ENDIF + + VHX = VHINGED(1,ICL,iptl,isurf)*XYZSCAL(1,isurf) + VHY = VHINGED(2,ICL,iptl,isurf)*XYZSCAL(2,isurf) + VHZ = VHINGED(3,ICL,iptl,isurf)*XYZSCAL(3,isurf) + VSQ = VHX**2 + VHY**2 + VHZ**2 + IF(VSQ.EQ.0.0) THEN + ! default: set hinge vector along hingeline + ! We are just setting the hinge line across the section + ! this assumes the hinge is linear even for a nonlinear wing + VHX = mesh_surf(1,idx_noder) + & + ABS(CHORDR*XHINGED(ICR,iptr,isurf)) + & - mesh_surf(1,idx_nodel) + & - ABS(CHORDL*XHINGED(ICL,iptl,isurf)) + VHY = mesh_surf(2,idx_noder) + & - mesh_surf(2,idx_nodel) + VHZ = mesh_surf(3,idx_noder) + & - mesh_surf(3,idx_nodel) + VHX = VHX*XYZSCAL(1,isurf) + VHY = VHY*XYZSCAL(2,isurf) + VHZ = VHZ*XYZSCAL(3,isurf) + VSQ = VHX**2 + VHY**2 + VHZ**2 + ENDIF + + VMOD = SQRT(VSQ) + VHINGE(1,idx_strip,N) = VHX/VMOD + VHINGE(2,idx_strip,N) = VHY/VMOD + VHINGE(3,idx_strip,N) = VHZ/VMOD + + VREFL(idx_strip,N) = REFLD(ICL,iptl, isurf) + + IF(XHD .GE. 0.0) THEN + PHINGE(1,idx_strip,N) = RLE(1,idx_strip) + XHD + PHINGE(2,idx_strip,N) = RLE(2,idx_strip) + PHINGE(3,idx_strip,N) = RLE(3,idx_strip) + ELSE + PHINGE(1,idx_strip,N) = RLE(1,idx_strip) - XHD + PHINGE(2,idx_strip,N) = RLE(2,idx_strip) + PHINGE(3,idx_strip,N) = RLE(3,idx_strip) + ENDIF + ENDIF + ENDDO + + ! Interpolate CD-CL polar defining data from input to strips + DO idx_coef = 1, 6 + CLCD(idx_coef,idx_strip) = (1.0-fc)* + & CLCDSEC(idx_coef,iptl,isurf) + + & fc*CLCDSEC(idx_coef,iptr,isurf) + END DO + ! If the min drag is zero flag the strip as no-viscous data + LVISCSTRP(idx_strip) = (CLCD(4,idx_strip).NE.0.0) + + + ! Set the panel (vortex) geometry data + + ! Accumulate the strip element indicies and start counting vorticies + if (idx_strip .eq. 1) then + IJFRST(idx_strip) = 1 + else + IJFRST(idx_strip) = IJFRST(idx_strip - 1) + + & NVSTRP(idx_strip - 1) + endif + idx_vor = IJFRST(idx_strip) + NVSTRP(idx_strip) = NVC(isurf) + + ! Associate the strip with the surface + LSSURF(idx_strip) = isurf + + ! Prepare for cross section interpolation + NSL = NASEC(iptl , isurf) + NSR = NASEC(iptr, isurf) + + ! CHORDC = CHORD(idx_strip) + + + ! Funny story. this original line is now valid now that we interpolate over the strip + clafc = (1.-FC)*(CHORDL/CHORD(idx_strip))*CLAFL + & + FC *(CHORDR/CHORD(idx_strip))*CLAFR + ! Suggestion from Hal Yougren for non linear sections: + ! clafc = (1.-fc)*clafl + fc*clafr + + ! loop over vorticies for the strip + do idx_x = 1, nvc(isurf) + + ! Left bound vortex points + idx_node = flatidx(idx_x,idx_y,isurf) + ! Compute the panel left side chord + dc1 = sqrt((mesh_surf(1,idx_node+1) - mesh_surf(1,idx_node))**2 + & + (mesh_surf(3,idx_node+1) - mesh_surf(3,idx_node))**2) + + if (LMESHFLAT(isurf)) then + ! Place vortex at panel quarter chord of the flat mesh + dx1 = sqrt((mesh_surf(1,idx_node) - RLE1(1,idx_strip))**2 + & + (mesh_surf(3,idx_node) - RLE1(3,idx_strip))**2) + RV1(2,idx_vor) = RLE1(2,idx_strip) + RV1(3,idx_vor) = RLE1(3,idx_strip) + RV1(1,idx_vor) = RLE1(1,idx_strip) + dx1 + (dc1/4.) + + ! Compute the panel left side angle + a1 = atan2((mesh_surf(3,idx_node+1) - mesh_surf(3,idx_node)), + & (mesh_surf(1,idx_node+1) - mesh_surf(1,idx_node))) + ! Place vortex at panel quarter chord of the true mesh + RV1MSH(2,idx_vor) = mesh_surf(2,idx_node) + RV1MSH(1,idx_vor) = mesh_surf(1,idx_node) + (dc1/4.)*cos(a1) + RV1MSH(3,idx_vor) = mesh_surf(3,idx_node) + (dc1/4.)*sin(a1) + else + ! Compute the panel left side angle + a1 = atan2((mesh_surf(3,idx_node+1) - mesh_surf(3,idx_node)), + & (mesh_surf(1,idx_node+1) - mesh_surf(1,idx_node))) + ! Place vortex at panel quarter chord + RV1(2,idx_vor) = mesh_surf(2,idx_node) + RV1(1,idx_vor) = mesh_surf(1,idx_node) + (dc1/4.)*cos(a1) + RV1(3,idx_vor) = mesh_surf(3,idx_node) + (dc1/4.)*sin(a1) + + ! Make a copy in the true mesh array for post processing + RV1MSH(2,idx_vor) = RV1(2,idx_vor) + RV1MSH(1,idx_vor) = RV1(1,idx_vor) + RV1MSH(3,idx_vor) = RV1(3,idx_vor) + end if + + ! Right bound vortex points + idx_node_yp1 = flatidx(idx_x,idx_y+1,isurf) + ! Compute the panel right side chord + dc2 = sqrt((mesh_surf(1,idx_node_yp1+1) + & - mesh_surf(1,idx_node_yp1))**2 + & + (mesh_surf(3,idx_node_yp1+1) + & - mesh_surf(3,idx_node_yp1))**2) + + if (LMESHFLAT(isurf)) then + ! Place vortex at panel quarter chord of the flat mesh + dx2 = sqrt((mesh_surf(1,idx_node_yp1) - RLE2(1,idx_strip))**2 + & + (mesh_surf(3,idx_node_yp1) - RLE2(3,idx_strip))**2) + + RV2(2,idx_vor) = RLE2(2,idx_strip) + RV2(3,idx_vor) = RLE2(3,idx_strip) + RV2(1,idx_vor) = RLE2(1,idx_strip) + dx2 + (dc2/4.) + + ! Compute the panel right side angle + a2 = atan2((mesh_surf(3,idx_node_yp1+1) + & - mesh_surf(3,idx_node_yp1)), (mesh_surf(1,idx_node_yp1+1) + & - mesh_surf(1,idx_node_yp1))) + ! Place vortex at panel quarter chord of the true mesh + RV2MSH(2,idx_vor) = mesh_surf(2,idx_node_yp1) + RV2MSH(1,idx_vor) = mesh_surf(1,idx_node_yp1) + & + (dc2/4.)*cos(a2) + RV2MSH(3,idx_vor) = mesh_surf(3,idx_node_yp1) + & + (dc2/4.)*sin(a2) + else + ! Compute the panel right side angle + a2 = atan2((mesh_surf(3,idx_node_yp1+1) - + & mesh_surf(3,idx_node_yp1)), (mesh_surf(1,idx_node_yp1+1) - + & mesh_surf(1,idx_node_yp1))) + ! Place vortex at panel quarter chord + RV2(2,idx_vor) = mesh_surf(2,idx_node_yp1) + RV2(1,idx_vor) = mesh_surf(1,idx_node_yp1) + (dc2/4.)*cos(a2) + RV2(3,idx_vor) = mesh_surf(3,idx_node_yp1) + (dc2/4.)*sin(a2) + + ! Make a copy in the true mesh array for post processing + RV2MSH(2,idx_vor) = RV2(2,idx_vor) + RV2MSH(1,idx_vor) = RV2(1,idx_vor) + RV2MSH(3,idx_vor) = RV2(3,idx_vor) + end if + + ! Mid-point bound vortex points + ! Compute the panel mid-point chord + ! Panels themselves can never be curved so just interpolate the chord + ! store as the panel chord in common block + DXV(idx_vor) = (dc1+dc2)/2. + ! We need to compute the midpoint angle and panel strip chord projection + ! as we need them to compute normals based on the real mesh + a3 = atan2(((mesh_surf(3,idx_node_yp1+1) + & + mesh_surf(3,idx_node+1))/2.- (mesh_surf(3,idx_node_yp1) + + & mesh_surf(3,idx_node))/2.), + & ((mesh_surf(1,idx_node_yp1+1) + mesh_surf(1,idx_node+1))/2. + & - (mesh_surf(1,idx_node_yp1) + mesh_surf(1,idx_node))/2.)) + ! project the panel chord onto the strip chord + DXSTRPV(idx_vor) = DXV(idx_vor)*cos(a3-GINCSTRIP(idx_strip)) + + if (LMESHFLAT(isurf)) then + ! Place vortex at panel quarter chord of the flat mesh + dx3 = sqrt(((mesh_surf(1,idx_node_yp1)+mesh_surf(1,idx_node))/2 + & - RLE(1,idx_strip))**2 + & + ((mesh_surf(3,idx_node_yp1)+mesh_surf(3,idx_node))/2 + & - RLE(3,idx_strip))**2) + RV(2,idx_vor) = RLE(2,idx_strip) + RV(3,idx_vor) = RLE(3,idx_strip) + RV(1,idx_vor) = RLE(1,idx_strip) + dx3 + (DXV(idx_vor)/4.) + + ! Place vortex at panel quarter chord of the true mesh + RVMSH(2,idx_vor) = (mesh_surf(2,idx_node_yp1) + & + mesh_surf(2,idx_node))/2. + RVMSH(1,idx_vor) = (mesh_surf(1,idx_node_yp1) + & + mesh_surf(1,idx_node))/2.+ (DXV(idx_vor)/4.)*cos(a3) + RVMSH(3,idx_vor) = (mesh_surf(3,idx_node_yp1) + & + mesh_surf(3,idx_node))/2. + (DXV(idx_vor)/4.)*sin(a3) + else + ! Place vortex at panel quarter chord + RV(2,idx_vor) = (mesh_surf(2,idx_node_yp1) + & + mesh_surf(2,idx_node))/2. + RV(1,idx_vor) = (mesh_surf(1,idx_node_yp1) + & + mesh_surf(1,idx_node))/2.+ (DXV(idx_vor)/4.)*cos(a3) + RV(3,idx_vor) = (mesh_surf(3,idx_node_yp1) + & + mesh_surf(3,idx_node))/2. + (DXV(idx_vor)/4.)*sin(a3) + + ! Make a copy in the true mesh array for post processing + RVMSH(2,idx_vor) = RV(2,idx_vor) + RVMSH(1,idx_vor) = RV(1,idx_vor) + RVMSH(3,idx_vor) = RV(3,idx_vor) + end if + + + ! Panel Control points + ! Y- point + ! is just the panel midpoint + RC(2,idx_vor) = RV(2,idx_vor) + ! Place the control point at the quarter chord + half chord*clafc + ! note that clafc is a scaler so is 1. is for 2pi + ! use data from vortex mid-point computation + if (LMESHFLAT(isurf)) then + RC(1,idx_vor) = RV(1,idx_vor) + clafc*(DXV(idx_vor)/2.) + RC(3,idx_vor) = RV(3,idx_vor) + + RCMSH(1,idx_vor) = RVMSH(1,idx_vor) + & + clafc*(DXV(idx_vor)/2.)*cos(a3) + RCMSH(3,idx_vor) = RVMSH(3,idx_vor) + & + clafc*(DXV(idx_vor)/2.)*sin(a3) + RCMSH(2,idx_vor) = RVMSH(2,idx_vor) + else + RC(1,idx_vor) = RV(1,idx_vor) + & + clafc*(DXV(idx_vor)/2.)*cos(a3) + RC(3,idx_vor) = RV(3,idx_vor) + & + clafc*(DXV(idx_vor)/2.)*sin(a3) + + ! Make a copy in the true mesh array for post processing + RCMSH(1,idx_vor) = RC(1,idx_vor) + RCMSH(3,idx_vor) = RC(3,idx_vor) + RCMSH(2,idx_vor) = RC(2,idx_vor) + end if + + ! Source points + ! Y- point + RS(2,idx_vor) = RV(2,idx_vor) + ! Place the source point at the half chord + ! use data from vortex mid-point computation + ! add another quarter chord to the quarter chord + if (LMESHFLAT(isurf)) then + RS(1,idx_vor) = RV(1,idx_vor) + (DXV(idx_vor)/4.) + RS(3,idx_vor) = RV(3,idx_vor) + (DXV(idx_vor)/4.) + else + RS(1,idx_vor) = RV(1,idx_vor) + (DXV(idx_vor)/4.)*cos(a3) + RS(3,idx_vor) = RV(3,idx_vor) + (DXV(idx_vor)/4.)*sin(a3) + end if + + + ! Set the camber slopes for the panel + + ! Camber slope at control point + CALL AKIMA(XASEC(1,iptl, isurf),SASEC(1,iptl, isurf), + & NSL,(RC(1,idx_vor)-RLE(1,idx_strip)) + & /CHORD(idx_strip),SLOPEL, DSDX) + CALL AKIMA(XASEC(1,iptr,isurf),SASEC(1,iptr,isurf), + & NSR,(RC(1,idx_vor)-RLE(1,idx_strip)) + & /CHORD(idx_strip),SLOPER, DSDX) + + ! Alternative for nonlinear sections per Hal Youngren + ! SLOPEC(idx_vor) = (1.-fc)*SLOPEL + fc*SLOPER + ! The original line is valid for interpolation over a strip + SLOPEC(idx_vor) = (1.-fc)*(CHORDL/CHORD(idx_strip))*SLOPEL + & + fc *(CHORDR/CHORD(idx_strip))*SLOPER + + ! Camber slope at vortex mid-point + CALL AKIMA(XASEC(1,iptl, isurf),SASEC(1,iptl, isurf), + & NSL,(RV(1,idx_vor)-RLE(1,idx_strip)) + & /CHORD(idx_strip),SLOPEL, DSDX) + CALL AKIMA(XASEC(1,iptr,isurf),SASEC(1,iptr,isurf), + & NSR,(RV(1,idx_vor)-RLE(1,idx_strip)) + & /CHORD(idx_strip),SLOPER, DSDX) + + ! Alternative for nonlinear sections per Hal Youngren + ! SLOPEV(idx_vor) = (1.-fc)*SLOPEL + fc*SLOPER + ! The original line is valid for interpolation over a strip + SLOPEV(idx_vor) = (1.-fc)*(CHORDL/CHORD(idx_strip))*SLOPEL + & + fc *(CHORDR/CHORD(idx_strip))*SLOPER + + ! Associate the panel with strip chord and component + CHORDV(idx_vor) = CHORD(idx_strip) + LVCOMP(idx_vor) = LNCOMP(isurf) + + ! Enforce no penetration at the control point + LVNC(idx_vor) = .true. + + ! element inherits alpha,beta flag from surface + LVALBE(idx_vor) = LFALBE(isurf) + + ! We need to scale the control surface gains by the fraction + ! of the element on the control surface + do N = 1, NCONTROL + !scale control gain by factor 0..1, (fraction of element on control surface) + xpt = ((mesh_surf(1,idx_node)+mesh_surf(1,idx_node_yp1))/2 + & - RLE(1,idx_strip))/CHORD(idx_strip) + + FRACLE = (XLED(N)/CHORD(idx_strip)-xpt) / + & (DXV(idx_vor)/CHORD(idx_strip)) + + FRACTE = (XTED(N)/CHORD(idx_strip)-xpt) / + & (DXV(idx_vor)/CHORD(idx_strip)) + + FRACLE = MIN( 1.0 , MAX( 0.0 , FRACLE ) ) + FRACTE = MIN( 1.0 , MAX( 0.0 , FRACTE ) ) + + DCONTROL(idx_vor,N) = GAINDA(N)*(FRACTE-FRACLE) + end do + + ! TE control point used only if surface sheds a wake + LVNC(idx_vor) = LFWAKE(isurf) + + ! Use the cross sections to generate the OML + ! nodal grid associated with vortex strip (aft-panel nodes) + ! NOTE: airfoil in plane of wing, but not rotated perpendicular to dihedral; + ! retained in (x,z) plane at this point + + ! Store the panel LE mid point for the next panel in the strip + ! This gets used a lot here + ! We use the original input mesh (true mesh) to compute points for the OML + xptxind1 = ((mesh_surf(1,idx_node+1) + & + mesh_surf(1,idx_node_yp1+1))/2 + & - RLE(1,idx_strip))/CHORD(idx_strip) + + ! xptxind2 = (mesh_surf(1,idx_node_yp1+1) + ! & - RLE2(1,idx_strip))/CHORD2(idx_strip) + + ! Interpolate cross section on left side + CALL AKIMA( XLASEC(1,iptl,isurf), ZLASEC(1,iptl,isurf), + & NSL,xptxind1, ZL_L, DSDX ) + CALL AKIMA( XUASEC(1,iptl,isurf), ZUASEC(1,iptl,isurf), + & NSL,xptxind1, ZU_L, DSDX ) + + ! Interpolate cross section on right side + CALL AKIMA(XLASEC(1,iptr,isurf), + & ZLASEC(1,iptr,isurf),NSR, xptxind1, ZL_R, DSDX) + + CALL AKIMA(XUASEC(1,iptr,isurf), + & ZUASEC(1,iptr,isurf),NSR, xptxind1, ZU_R, DSDX) + + + ! Compute the left aft node of panel + ! X-point + XYN1(1,idx_vor) = RLE1(1,idx_strip) + + & xptxind1*CHORD1(idx_strip) + + ! Y-point + XYN1(2,idx_vor) = RLE1(2,idx_strip) + + ! Interpolate z from sections to left aft node of panel + ZL = (1.-f1)*ZL_L + f1 *ZL_R + ZU = (1.-f1)*ZU_L + f1 *ZU_R + + ! Store left aft z-point + ZLON1(idx_vor) = RLE1(3,idx_strip) + ZL*CHORD1(idx_strip) + ZUPN1(idx_vor) = RLE1(3,idx_strip) + ZU*CHORD1(idx_strip) + + ! Compute the right aft node of panel + ! X-point + XYN2(1,idx_vor) = RLE2(1,idx_strip) + + & xptxind1*CHORD2(idx_strip) + + ! Y-point + XYN2(2,idx_vor) = RLE2(2,idx_strip) + + ! Interpolate z from sections to right aft node of panel + ZL = (1.-f2)*ZL_L + f2 *ZL_R + ZU = (1.-f2)*ZU_L + f2 *ZU_R + + ! Store right aft z-point + ZLON2(idx_vor) = RLE2(3,idx_strip) + ZL*CHORD2(idx_strip) + ZUPN2(idx_vor) = RLE2(3,idx_strip) + ZU*CHORD2(idx_strip) + + + idx_vor = idx_vor + 1 + end do ! End vortex loop + idx_strip = idx_strip + 1 + end do ! End strip loop + + ! Compute the wetted area and cave from the true mesh + sum = 0.0 + wtot = 0.0 + DO JJ = 1, NJ(isurf) + J = JFRST(isurf) + JJ-1 + ASTRP = WSTRIP(J)*CHORD(J) + SUM = SUM + ASTRP + WTOT = WTOT + WSTRIP(J) + ENDDO + SSURF(isurf) = SUM + + IF(WTOT .EQ. 0.0) THEN + CAVESURF(isurf) = 0.0 + ELSE + CAVESURF(isurf) = sum/wtot + ENDIF + ! add number of strips to the global count + NSTRIP = NSTRIP + NJ(isurf) + ! add number of of votrices to the global count + NVOR = NVOR + NK(isurf)*NJ(isurf) + + end subroutine makesurf_mesh subroutine update_surfaces() c-------------------------------------------------------------- c Updates all surfaces, using the stored data. +c Resets the strips and vorticies so that AVL rebuilds them +c from the updated geometry. Recomputes panels normals and +c tells AVL to rebuild the AIC and other aero related data +c arrays on the next execution. c-------------------------------------------------------------- use avl_heap_inc include 'AVL.INC' @@ -660,7 +1433,11 @@ subroutine update_surfaces() c up the size information as we make each surface do ii=1,(NSURF-NSURFDUPL) if (lverbose) write(*,*) 'Updating surface ',ISURF - call makesurf(ISURF) + if (lsurfmsh(isurf)) then + call makesurf_mesh(ISURF) + else + call makesurf(ISURF) + end if if(ldupl(isurf)) then if (lverbose) write(*,*) ' reduplicating ',ISURF @@ -954,6 +1731,8 @@ SUBROUTINE SDUPL(NN, Ypt,MSG) LFLOAD(NNI) = LFLOAD(NN) LRANGE(NNI) = LRANGE(NN) LSURFSPACING(NNI) = LSURFSPACING(NN) + LMESHFLAT(NNI) = LMESHFLAT(NN) + LSURFMSH(NNI) = LSURFMSH(NN) C---- accumulate stuff for new image surface ! IFRST(NNI) = NVOR + 1 @@ -1013,6 +1792,7 @@ SUBROUTINE SDUPL(NN, Ypt,MSG) RLE(2,JJI) = -RLE(2,JJ) + YOFF RLE(3,JJI) = RLE(3,JJ) CHORD(JJI) = CHORD(JJ) + GINCSTRIP(JJI) = GINCSTRIP(JJ) WSTRIP(JJI) = WSTRIP(JJ) TANLE(JJI) = -TANLE(JJ) AINC (JJI) = AINC(JJ) @@ -1078,10 +1858,27 @@ SUBROUTINE SDUPL(NN, Ypt,MSG) SLOPEC(III) = SLOPEC(II) SLOPEV(III) = SLOPEV(II) DXV(III) = DXV(II) + DXSTRPV(III) = DXSTRPV(II) CHORDV(III) = CHORDV(II) LVCOMP(III) = LNCOMP(NNI) LVALBE(III) = LVALBE(II) LVNC(III) = LVNC(II) + ! Duplicate mesh data if we are using a mesh + if (lsurfmsh(NN)) then + RV1MSH(1,III) = RV2MSH(1,II) + RV1MSH(2,III) = -RV2MSH(2,II) + YOFF + RV1MSH(3,III) = RV2MSH(3,II) + RV2MSH(1,III) = RV1MSH(1,II) + RV2MSH(2,III) = -RV1MSH(2,II) + YOFF + RV2MSH(3,III) = RV1MSH(3,II) + RVMSH(1,III) = RVMSH(1,II) + RVMSH(2,III) = -RVMSH(2,II) + YOFF + RVMSH(3,III) = RVMSH(3,II) + RCMSH(1,III) = RCMSH(1,II) + RCMSH(2,III) = -RCMSH(2,II) + YOFF + RCMSH(3,III) = RCMSH(3,II) + end if + C DO N = 1, NCONTROL ccc RSGN = SIGN( 1.0 , VREFL(JJ,N) ) @@ -1189,6 +1986,8 @@ SUBROUTINE ENCALC C...PURPOSE To calculate the normal vectors for the strips, C the horseshoe vortices, and the control points. C Incorporates surface deflections. + ! Also checks if surface has been assigned a point cloud mesh + ! and uses the real mesh to compute normals if it is C C...INPUT NVOR Number of vortices C X1 Coordinates of endpoint #1 of the vortices @@ -1212,10 +2011,46 @@ SUBROUTINE ENCALC C REAL EP(3), EQ(3), ES(3), EB(3), EC(3), ECXB(3) REAL EC_G(3,NDMAX), ECXB_G(3) + real(kind=avl_real) :: dchstrip, DXT, DYT, DZT, ec_msh(3) C C...Calculate the normal vector at control points and bound vortex midpoints C DO 10 J = 1, NSTRIP + + ! Since we cannot seperate the encalc routine for direct mesh assignment we have to make it a branch here + if (lsurfmsh(lssurf(J))) then + + ! Calculate normal vector for the strip (normal to X axis) + ! we can't just interpolate this anymore given that + ! the strip is no longer necessarily linear chordwise + + ! We want the spanwise unit vector for the strip at the + ! chordwise location specified by SAXFR (usually set to 0.25) + ! Loop over all panels in the strip until we find the one that contains + ! the SAXFR position in it's projected chord. Since the panels themselves are still linear + ! we can just use the bound vortex unit vector of that panel as + ! the spanwise unit vector of the strip at SAXFR + + ! SAB: This is slow, find a better way to do this + dchstrip = 0.0 + searchSAXFR: do i = IJFRST(J),IJFRST(J) + (NVSTRP(J)-1) + dchstrip = dchstrip+DXSTRPV(i) + if (dchstrip .ge. CHORD(J)*SAXFR) then + exit searchSAXFR + end if + end do searchSAXFR + + + ! compute the spanwise unit vector for Vperp def + DXT = RV2MSH(1,I)-RV1MSH(1,I) + DYT = RV2MSH(2,I)-RV1MSH(2,I) + DZT = RV2MSH(3,I)-RV1MSH(3,I) + XSREF(J) = RVMSH(1,I) + YSREF(J) = RVMSH(2,I) + ZSREF(J) = RVMSH(3,I) + + else + ! original encalc routine for standard AVL geometry C C...Calculate normal vector for the strip (normal to X axis) I = IJFRST(J) @@ -1243,19 +2078,21 @@ SUBROUTINE ENCALC DXT = (1.0-SAXFR)*DXLE + SAXFR*DXTE DYT = (1.0-SAXFR)*DYLE + SAXFR*DYTE DZT = (1.0-SAXFR)*DZLE + SAXFR*DZTE -C - ESS(1,J) = DXT/SQRT(DXT*DXT + DYT*DYT + DZT*DZT) - ESS(2,J) = DYT/SQRT(DXT*DXT + DYT*DYT + DZT*DZT) - ESS(3,J) = DZT/SQRT(DXT*DXT + DYT*DYT + DZT*DZT) -C - ENSY(J) = -DZT/SQRT(DYT*DYT + DZT*DZT) - ENSZ(J) = DYT/SQRT(DYT*DYT + DZT*DZT) C XSREF(J) = (1.0-SAXFR)*AXLE + SAXFR*AXTE YSREF(J) = (1.0-SAXFR)*AYLE + SAXFR*AYTE ZSREF(J) = (1.0-SAXFR)*AZLE + SAXFR*AZTE + end if C C + ESS(1,J) = DXT/SQRT(DXT*DXT + DYT*DYT + DZT*DZT) + ESS(2,J) = DYT/SQRT(DXT*DXT + DYT*DYT + DZT*DZT) + ESS(3,J) = DZT/SQRT(DXT*DXT + DYT*DYT + DZT*DZT) + + ! Treffz plane normals + ENSY(J) = -DZT/SQRT(DYT*DYT + DZT*DZT) + ENSZ(J) = DYT/SQRT(DYT*DYT + DZT*DZT) + ES(1) = 0. ES(2) = ENSY(J) ES(3) = ENSZ(J) @@ -1284,11 +2121,19 @@ SUBROUTINE ENCALC ENC_G(2,I,N) = 0. ENC_G(3,I,N) = 0. ENDDO + + if (lsurfmsh(lssurf(J))) then + ! Define unit vector along bound leg + DXB = RV2MSH(1,I)-RV1MSH(1,I) ! right h.v. pt - left h.v. pt + DYB = RV2MSH(2,I)-RV1MSH(2,I) + DZB = RV2MSH(3,I)-RV1MSH(3,I) + else C C...Define unit vector along bound leg DXB = RV2(1,I)-RV1(1,I) ! right h.v. pt - left h.v. pt DYB = RV2(2,I)-RV1(2,I) DZB = RV2(3,I)-RV1(3,I) + end if EMAG = SQRT(DXB**2 + DYB**2 + DZB**2) EB(1) = DXB/EMAG EB(2) = DYB/EMAG @@ -1297,6 +2142,11 @@ SUBROUTINE ENCALC C...Define direction of normal vector at control point C The YZ projection of the normal vector matches the camber slope C + section local incidence in the YZ defining plane for the section + ! First start by combining the contributions to the panel + ! incidence from AVL incidence and camberline slope variables + ! these are not actual geometric transformations of the mesh + ! but rather further modifications to the chordwise vector that + ! will get used to compute normals ANG = AINC(J) - ATAN(SLOPEC(I)) cc IF(LDES) THEN C--------- add design-variable contribution to angle @@ -1307,14 +2157,71 @@ SUBROUTINE ENCALC C SINC = SIN(ANG) COSC = COS(ANG) + + if (lsurfmsh(lssurf(J))) then + ! direct mesh assignemnt branch + ! now we compute the chordwise panel vector + ! note that panel`s chordwise vector has contributions + ! from both the geometry itself and the incidence modification + ! from the AVL AINC and camber slope variables + + ! Get the geometric chordwise vector using RVMSH and RCMSH which should + ! be located in the same plane given that each individual panel is a + ! plane + + EMAG = SQRT((RCMSH(1,I)-RVMSH(1,I))**2 + & + (RCMSH(2,I)-RVMSH(2,I))**2 + & + (RCMSH(3,I)-RVMSH(3,I))**2) + ec_msh(1) = (RCMSH(1,I)-RVMSH(1,I))/EMAG + ec_msh(2) = (RCMSH(2,I)-RVMSH(2,I))/EMAG + ec_msh(3) = (RCMSH(3,I)-RVMSH(3,I))/EMAG + + ! Now we have to rotate this vector by the incidence contribution from AINC and CAMBER + ! However, this rotation needs to be done about the local y-axis of the wing + ! Earlier we computed ES the normal vector of the strip projected to the Trefftz plane + ! The axis we need to rotate about is the one purpendicular to this ES. + ! As a result all panel normals in a given strip will be rotated about the same axis defined by the that strip + ! The components of the rotation axis are obtained from ES as follows + ! rot_axis(1) = 0 + ! rot_axis(2) = -ES(3) + ! rot_axis(3) = ES(2) + ! We can then multiply ec_msh by the rotation matrix for a rotation about an arbitrary axis + ! see https://pubs.aip.org/aapt/ajp/article/44/1/63/1050167/Formalism-for-the-rotation-matrix-of-rotations + ! Note that standard AVL also does this exact same thing but since they always rotate the vector [1,0,0] + ! the result collapses into the ridiculously simple expression for EC that you see in the other branch + + EC(1) = COSC*ec_msh(1) + ES(2)*SINC*ec_msh(2) + & + ES(3)*SINC*ec_msh(3) + EC(2) = -ES(2)*SINC + ((ES(3)**2)*(1-COSC)+COSC)*ec_msh(2) + & - (ES(2)*ES(3)*(1-COSC))*ec_msh(3) + EC(3) = -ES(3)*SINC*ec_msh(1) - + & (ES(2)*ES(3)*(1-COSC))*ec_msh(2) + + & ((ES(2)**2)*(1-COSC) + COSC)*ec_msh(3) + + else EC(1) = COSC EC(2) = -SINC*ES(2) EC(3) = -SINC*ES(3) - ! EC = rotation of strip normal vector? or along chord? + end if + DO N = 1, NDESIGN + ! The derivative here also changes if we use a custom mesh + ! Note the derivative is only wrt to AVL incidence vars + ! as those are the vars AVL DVs can support + if (lsurfmsh(lssurf(J))) then + EC_G(1,N) = (-SINC*ec_msh(1) + ES(2)*COSC*ec_msh(2) + & + ES(3)*COSC*ec_msh(3))*AINC_G(J,N) + EC_G(2,N) = (-ES(2)*COSC + ((ES(3)**2)*(1+SINC)-SINC) + & *ec_msh(2) - (ES(2)*ES(3)*(1+SINC))*ec_msh(3))*AINC_G(J,N) + EC_G(3,N) = (-ES(3)*COSC*ec_msh(1) - + & (ES(2)*ES(3)*(1+SINC))*ec_msh(2) + + & ((ES(2)**2)*(1+SINC) - SINC)*ec_msh(3))*AINC_G(J,N) + + else EC_G(1,N) = -SINC *AINC_G(J,N) EC_G(2,N) = -COSC*ES(2)*AINC_G(J,N) EC_G(3,N) = -COSC*ES(3)*AINC_G(J,N) + end if ENDDO C C...Normal vector is perpendicular to camberline vector and to the bound leg @@ -1343,6 +2250,11 @@ SUBROUTINE ENCALC C...Define direction of normal vector at vortex mid-point. C The YZ projection of the normal vector matches the camber slope C + section local incidence in the YZ defining plane for the section + ! This section is identical to the normal vector at the control + ! point. The only different is that the AVL camberline slope + ! is taken at the bound vortex point rather than the control point + ! the geometric contributions to the normal vector at both of these + ! point is identical as the lie in the plane of the same panel. ANG = AINC(J) - ATAN(SLOPEV(I)) cc IF(LDES) THEN C--------- add design-variable contribution to angle @@ -1353,13 +2265,40 @@ SUBROUTINE ENCALC C SINC = SIN(ANG) COSC = COS(ANG) + if (lsurfmsh(lssurf(J))) then + ! direct mesh assignment branch + ! see explanation in section above for control point normals + ! ec_msh was already computed in that section + EC(1) = COSC*ec_msh(1) + ES(2)*SINC*ec_msh(2) + & + ES(3)*SINC*ec_msh(3) + EC(2) = -ES(2)*SINC + ((ES(3)**2)*(1-COSC)+COSC)*ec_msh(2) + & - (ES(2)*ES(3)*(1-COSC))*ec_msh(3) + EC(3) = -ES(3)*SINC*ec_msh(1) - + & (ES(2)*ES(3)*(1-COSC))*ec_msh(2) + + & ((ES(2)**2)*(1-COSC) + COSC)*ec_msh(3) + + else EC(1) = COSC EC(2) = -SINC*ES(2) EC(3) = -SINC*ES(3) + end if + DO N = 1, NDESIGN + if (lsurfmsh(lssurf(J))) then + ! Direct mesh assignment branch + EC_G(1,N) = (-SINC*ec_msh(1) + ES(2)*COSC*ec_msh(2) + & + ES(3)*COSC*ec_msh(3))*AINC_G(J,N) + EC_G(2,N) = (-ES(2)*COSC + ((ES(3)**2)*(1+SINC)-SINC) + & *ec_msh(2) - (ES(2)*ES(3)*(1+SINC))*ec_msh(3))*AINC_G(J,N) + EC_G(3,N) = (-ES(3)*COSC*ec_msh(1) - + & (ES(2)*ES(3)*(1+SINC))*ec_msh(2) + + & ((ES(2)**2)*(1+SINC) - SINC)*ec_msh(3))*AINC_G(J,N) + + else EC_G(1,N) = -SINC *AINC_G(J,N) EC_G(2,N) = -COSC*ES(2)*AINC_G(J,N) EC_G(3,N) = -COSC*ES(3)*AINC_G(J,N) + end if ENDDO C C...Normal vector is perpendicular to camberline vector and to the bound leg @@ -1389,6 +2328,8 @@ SUBROUTINE ENCALC C C======================================================= C-------- rotate normal vectors for control surface + ! this is a pure rotation of the normal vector + ! the geometric contribution from the mesh is already accounted for DO 100 N = 1, NCONTROL C C---------- skip everything if this element is unaffected by control variable N @@ -1450,4 +2391,3 @@ SUBROUTINE ENCALC C RETURN END ! ENCALC - diff --git a/src/avl.f b/src/avl.f index 4e1804f..90d55f0 100644 --- a/src/avl.f +++ b/src/avl.f @@ -479,11 +479,11 @@ SUBROUTINE loadGEO(geom_file) C----- initialize state C CALL VARINI C - LAIC = .FALSE. - LSRD = .FALSE. - LVEL = .FALSE. - LSOL = .FALSE. - LSEN = .FALSE. + LAIC = .FALSE. ! Tell AVL that the AIC is no longer valid and to regenerate it + LSRD = .FALSE. ! Tell AVL that unit source+doublet strengths are no longer valid and to regenerate them + LVEL = .FALSE. ! Tell AVL that the induced velocity matrix is no longer valid and to regenerate it + LSOL = .FALSE. ! Tell AVL that a valid solution no longer exists + LSEN = .FALSE. ! Tell AVL that valid sensitives no longer exists c---- initialize heap storage arrays for AIC's call avlheap_init(NVOR) diff --git a/src/build/Makefile b/src/build/Makefile index c3c0e1c..6a72ab0 100644 --- a/src/build/Makefile +++ b/src/build/Makefile @@ -38,7 +38,7 @@ OFILES=$(f90FilesNoDir:%.f90=%.o) $(f77FilesNoDir:%.f=%.o) $(cFilesNoDir:%.c=%.o default: lib ../f2py/libavl.pyf # Generate Python inlude directory - $(eval PYTHON_INCLUDES = $(shell python-config --includes)) + $(eval PYTHON_INCLUDES = $(shell python3-config --includes)) @echo "#------------------------------------------------------#" @echo Python Inclue Flags $(PYTHON_INCLUDES) @echo "#------------------------------------------------------#" diff --git a/src/f2py/libavl.pyf b/src/f2py/libavl.pyf index 97262d1..c5c9157 100644 --- a/src/f2py/libavl.pyf +++ b/src/f2py/libavl.pyf @@ -137,27 +137,9 @@ python module libavl ! in real*8, intent(inout) :: rsys(jemax) end subroutine get_system_matrices - !subroutine getcam(x, y, n, xc, yc, tc, nc, lnorm) ! in :libavl:airutil.f - !integer, intent(in) :: n - !integer, intent(inout) :: nc - !real, dimension(nc), intent(out) :: xc, yc, tc - !real, dimension(n), intent(in) :: x, y - !logical, intent(in) :: lnorm - !end subroutine getcam - - !subroutine getcam(x, y, n, xc, yc, tc, nc, lnorm) ! in :libavl:airutil.f - !integer, intent(inout) :: nc - !integer n - !real*8 x(n), y(n), xc(nc), yc(nc), tc(nc) - !logical lnorm - !end subroutine getcam - - !subroutine akima(x, y, n, xx, yy, slp) ! in :libavl:sgutil.f - !integer, intent(in) :: n - !real, dimension(n), intent(in) :: x, y - !real, dimension(*), intent(in) :: xx - !real, dimension(*), intent(out) :: yy, slp - !end subroutine akima + subroutine makesurf_mesh(isurf) ! in :libavl:amake.f + integer :: isurf + end subroutine makesurf_mesh subroutine set_section_coordinates(isec, isurf, x, y, n, nin, xfmin, xfmax, storecoords) ! in :libavl:amake.f integer :: isec, isurf, n, nin @@ -170,5 +152,6 @@ python module libavl ! in real*8 :: xb(nb), yb(nb) logical :: storecoords end subroutine set_body_coordinates + end interface end python module libavl diff --git a/src/includes/ADIMEN.INC b/src/includes/ADIMEN.INC index 49daf22..cf2a343 100644 --- a/src/includes/ADIMEN.INC +++ b/src/includes/ADIMEN.INC @@ -57,9 +57,3 @@ PARAMETER (IBX=200) ! max number of airfoil coordinates ! PARAMETER (IBX=300) ! max number of airfoil coordinates - - - - - - diff --git a/src/includes/AVL.INC b/src/includes/AVL.INC index b0a7639..6ed712b 100644 --- a/src/includes/AVL.INC +++ b/src/includes/AVL.INC @@ -505,10 +505,32 @@ c & VHINGED(3, ICONX, NSECMAX, NFMAX), ! hinge vector & GAIND(ICONX, NSECMAX, NFMAX), ! control surface gain & REFLD(ICONX, NSECMAX, NFMAX), ! control surface reflection - & GAING(ICONX, NSECMAX, NFMax) ! desgin variable gain + & GAING(ICONX, NSECMAX, NFMAX) ! desgin variable gain + COMMON /SURF_MESH_I/ + & MFRST(NFMAX), ! stores the index in the MSHBLK where each surface's mesh begins + & IPTSEC(NSMAX,NFMAX) ! stores the iptloc vector for each surface -C !!--- end added variables for python geometry minipulation --- + REAL(kind=avl_real) MSHBLK + REAL(kind=avl_real) RV1MSH + REAL(kind=avl_real) RV2MSH + REAL(kind=avl_real) RVMSH + REAL(kind=avl_real) RCMSH + COMMON /SURF_MESH_R/ + & MSHBLK(3, 4*NVMAX), ! block to store all surface meshes + & RV1MSH(3,NVMAX), ! mesh h.v. vortex left points + & RV2MSH(3,NVMAX), ! mesh h.v. vortex right points + & RVMSH(3,NVMAX), ! mesh h.v. vortex center points + & RCMSH(3,NVMAX) ! mesh h.v. control points + + LOGICAL LSURFMSH + LOGICAL LMESHFLAT + COMMON /SURF_MESH_L/ + & LSURFMSH(NFMAX), ! T if surface uses a mesh cordinates for its geometry + & LMESHFLAT(NFMAX) ! T if the surface's mesh should be flattened when computing vorticies and control points + + +C !!--- end added variables for python geometry manipulation --- COMMON /STRP_I/ & LSSURF(NSMAX), ! index of surface which contains this strip @@ -528,6 +550,7 @@ C !!--- end added variables for python geometry minipulation --- REAL(kind=avl_real) RLE2, CHORD2 REAL(kind=avl_real) WSTRIP REAL(kind=avl_real) TANLE, TANTE + REAL(kind=avl_real) GINCSTRIP REAL(kind=avl_real) CLCD REAL(kind=avl_real) SAXFR REAL(kind=avl_real) ESS @@ -564,6 +587,7 @@ C !!--- end added variables for python geometry minipulation --- & RLE2(3,NSMAX), CHORD2(NSMAX), ! strip right end LE point, chord & WSTRIP(NSMAX), ! strip y-z width & TANLE(NSMAX), TANTE(NSMAX), ! strip LE,TE sweep slopes + & GINCSTRIP(NSMAX), ! strip geometric incidence angle & CLCD(6,NSMAX), ! strip viscous polar parameters (CL0,CD0,CL1,CD1,CL2,CD2) & SAXFR, ! x/c of spanwise axis for Vperp def & ESS(3,NSMAX), ! spanwise unit vector for Vperp def @@ -624,6 +648,7 @@ C REAL(kind=avl_real) RS REAL(kind=avl_real) RL, RADL REAL(kind=avl_real) DXV + REAL(kind=avl_real) DXSTRPV REAL(kind=avl_real) CHORDV REAL(kind=avl_real) SLOPEV REAL(kind=avl_real) SLOPEC @@ -670,6 +695,7 @@ C & RS(3,NVMAX), ! h.v. source points & RL(3,NLMAX), RADL(NLMAX), ! source line node points, body radius & DXV(NVMAX), ! chord of element + & DXSTRPV(NVMAX), ! chord of element projected onto chord of element-containing strip & CHORDV(NVMAX), ! chord of element-containing strip & SLOPEV(NVMAX), ! camber slopes at h.v. bound leg & SLOPEC(NVMAX), ! camber slopes at c.p. diff --git a/src/includes/AVL_ad_seeds.inc b/src/includes/AVL_ad_seeds.inc index 5049afa..dae2b9f 100644 --- a/src/includes/AVL_ad_seeds.inc +++ b/src/includes/AVL_ad_seeds.inc @@ -312,6 +312,7 @@ C real(kind=avl_real) CFS_D_DIFF real(kind=avl_real) CFS_G_DIFF real(kind=avl_real) CMSURF_DIFF + real(kind=avl_real) CMSURFBAX_DIFF real(kind=avl_real) CMS_U_DIFF real(kind=avl_real) CMS_D_DIFF real(kind=avl_real) CMS_G_DIFF @@ -345,6 +346,7 @@ C & CFS_D_DIFF(3,NFMAX,NDMAX), & CFS_G_DIFF(3,NFMAX,NGMAX), & CMSURF_DIFF(3,NFMAX), + & CMSURFBAX_DIFF(3,NFMAX), & CMS_U_DIFF(3,NFMAX,NUMAX), & CMS_D_DIFF(3,NFMAX,NDMAX), & CMS_G_DIFF(3,NFMAX,NGMAX), @@ -366,6 +368,8 @@ C real(kind=avl_real) XBOD_DIFF real(kind=avl_real) YBOD_DIFF real(kind=avl_real) TBOD_DIFF + real(kind=avl_real) XBOD_R_DIFF + real(kind=avl_real) YBOD_R_DIFF COMMON /BODY_GEOM_R_DIFF/ & XYZSCAL_B_DIFF(3, NBMAX), & XYZTRAN_B_DIFF(3, NBMAX), @@ -374,7 +378,9 @@ C & BSPACE_DIFF(NBMAX), & XBOD_DIFF(IBX, NBMAX), & YBOD_DIFF(IBX, NBMAX), - & TBOD_DIFF(IBX, NBMAX) + & TBOD_DIFF(IBX, NBMAX), + & XBOD_R_DIFF(IBX, NBMAX), + & YBOD_R_DIFF(IBX, NBMAX) C real(kind=avl_real) XYZSCAL_DIFF real(kind=avl_real) XYZTRAN_DIFF @@ -386,8 +392,8 @@ C real(kind=avl_real) XYZLES_DIFF real(kind=avl_real) XSEC_DIFF real(kind=avl_real) YSEC_DIFF - real(kind=avl_real) XFMIN_DIFF - real(kind=avl_real) XFMAX_DIFF + real(kind=avl_real) XFMIN_R_DIFF + real(kind=avl_real) XFMAX_R_DIFF real(kind=avl_real) XLASEC_DIFF real(kind=avl_real) ZLASEC_DIFF real(kind=avl_real) XUASEC_DIFF @@ -415,6 +421,10 @@ C & SSPACE_DIFF(NFMAX), & SSPACES_DIFF(NSECMAX, NFMAX), & XYZLES_DIFF(3, NSECMAX, NFMAX), + & XSEC_DIFF(IBX, NSECMAX, NFMAX), + & YSEC_DIFF(IBX, NSECMAX, NFMAX), + & XFMIN_R_DIFF(NSECMAX, NFMAX), + & XFMAX_R_DIFF(NSECMAX, NFMAX), & XLASEC_DIFF(IBX, NSECMAX, NFMAX), & ZLASEC_DIFF(IBX, NSECMAX, NFMAX), & XUASEC_DIFF(IBX, NSECMAX, NFMAX), @@ -432,7 +442,19 @@ C & VHINGED_DIFF(3, ICONX, NSECMAX, NFMAX), & GAIND_DIFF(ICONX, NSECMAX, NFMAX), & REFLD_DIFF(ICONX, NSECMAX, NFMAX), - & GAING_DIFF(ICONX, NSECMAX, NFMax) + & GAING_DIFF(ICONX, NSECMAX, NFMAX) +C + real(kind=avl_real) MSHBLK_DIFF + real(kind=avl_real) RV1MSH_DIFF + real(kind=avl_real) RV2MSH_DIFF + real(kind=avl_real) RVMSH_DIFF + real(kind=avl_real) RCMSH_DIFF + COMMON /SURF_MESH_R_DIFF/ + & MSHBLK_DIFF(3, 4*NVMAX), + & RV1MSH_DIFF(3,NVMAX), + & RV2MSH_DIFF(3,NVMAX), + & RVMSH_DIFF(3,NVMAX), + & RCMSH_DIFF(3,NVMAX) C real(kind=avl_real) RLE_DIFF real(kind=avl_real) CHORD_DIFF @@ -443,6 +465,7 @@ C real(kind=avl_real) WSTRIP_DIFF real(kind=avl_real) TANLE_DIFF real(kind=avl_real) TANTE_DIFF + real(kind=avl_real) GINCSTRIP_DIFF real(kind=avl_real) CLCD_DIFF real(kind=avl_real) SAXFR_DIFF real(kind=avl_real) ESS_DIFF @@ -502,6 +525,7 @@ C & WSTRIP_DIFF(NSMAX), & TANLE_DIFF(NSMAX), & TANTE_DIFF(NSMAX), + & GINCSTRIP_DIFF(NSMAX), & CLCD_DIFF(6,NSMAX), & SAXFR_DIFF, & ESS_DIFF(3,NSMAX), @@ -560,6 +584,7 @@ C real(kind=avl_real) RL_DIFF real(kind=avl_real) RADL_DIFF real(kind=avl_real) DXV_DIFF + real(kind=avl_real) DXSTRPV_DIFF real(kind=avl_real) CHORDV_DIFF real(kind=avl_real) SLOPEV_DIFF real(kind=avl_real) SLOPEC_DIFF @@ -607,6 +632,7 @@ C & RL_DIFF(3,NLMAX), & RADL_DIFF(NLMAX), & DXV_DIFF(NVMAX), + & DXSTRPV_DIFF(NVMAX), & CHORDV_DIFF(NVMAX), & SLOPEV_DIFF(NVMAX), & SLOPEC_DIFF(NVMAX), @@ -655,6 +681,7 @@ C real(kind=avl_real) CLBDY_DIFF real(kind=avl_real) CFBDY_DIFF real(kind=avl_real) CMBDY_DIFF + real(kind=avl_real) CMBDYBAX_DIFF COMMON /BODY_R_DIFF/ & ELBDY_DIFF(NBMAX), & SRFBDY_DIFF(NBMAX), @@ -664,7 +691,8 @@ C & CYBDY_DIFF(NBMAX), & CLBDY_DIFF(NBMAX), & CFBDY_DIFF(3,NBMAX), - & CMBDY_DIFF(3,NBMAX) + & CMBDY_DIFF(3,NBMAX), + & CMBDYBAX_DIFF(3,NBMAX) C real(kind=avl_real) AMACH_DIFF real(kind=avl_real) VC_DIFF diff --git a/src/sgutil.f b/src/sgutil.f index b869f5e..8ab763f 100644 --- a/src/sgutil.f +++ b/src/sgutil.f @@ -296,6 +296,24 @@ SUBROUTINE SPACER (N,PSPACE,X) SUBROUTINE CSPACER(NVC,CSPACE,CLAF, XPT,XVR,XSR,XCP) + ! This is a extremely important funciton that is not + ! documented for some reason. + ! Inputs: + ! NVC: NUMBER OF DESIRED POINTS IN ARRAY + ! CSPACE: SPACING PARAMETER (-3<=PSPACE<=3). + ! DEFINES POINT DISTRIBUTION + ! TO BE USED AS FOLLOWS: + ! PSPACE = 0 : EQUAL SPACING + ! PSPACE = 1 : COSINE SPACING. + ! PSPACE = 2 : SINE SPACING + ! (CONCENTRATING POINTS NEAR 0). + ! PSPACE = 3 : EQUAL SPACING. + ! CLAF: CL alfa (needed to determine control point location) + ! Outputs: + ! XPT: Array of panel leading edge x-locations + ! XVR: Array of vortex x-locations + ! XSR: Array of source x-locations + ! XCP: Array of control point x-locations REAL XPT(*), XVR(*), XSR(*), XCP(*) C PI = 4.0*ATAN(1.0) @@ -318,17 +336,21 @@ SUBROUTINE CSPACER(NVC,CSPACE,CLAF, XPT,XVR,XSR,XCP) ENDIF C C---- cosine chordwise spacing + ! Each of these provides a quarter panel chord offset for cosine, + ! sine, and uniform spacing respectively. DTH1 = PI/FLOAT(4*NVC + 2) DTH2 = 0.5*PI/FLOAT(4*NVC + 1) DXC0 = 1.0/FLOAT(4*NVC) C DO IVC = 1, NVC C------ uniform - XC0 = INT(4*IVC - 4) * DXC0 + XC0 = INT(4*IVC - 4) * DXC0 ! eqv (IVC-1)/NVC XPT0 = XC0 - XVR0 = XC0 + DXC0 - XSR0 = XC0 + 2.0*DXC0 - XCP0 = XC0 + DXC0 + 2.0*DXC0*CLAF + XVR0 = XC0 + DXC0 ! quarter-chord + XSR0 = XC0 + 2.0*DXC0 ! half-chord + XCP0 = XC0 + DXC0 + 2.0*DXC0*CLAF ! quarter-chord + half-chord*claf + ! Note: claf is a scaling factor so typically claf = 1 and the control point + ! is at the three-quarter chord position of the panel C C------ cosine TH1 = INT(4*IVC - 3) * DTH1 diff --git a/tests/test_body_axis_derivs_partial_derivs.py b/tests/test_body_axis_derivs_partial_derivs.py index b26fccc..7359402 100644 --- a/tests/test_body_axis_derivs_partial_derivs.py +++ b/tests/test_body_axis_derivs_partial_derivs.py @@ -196,7 +196,7 @@ def test_rev_gamma_u(self): # for var_key in bd_d_fwd[deriv_func]: bd_d_rev = {deriv_func: 1.0} - gamma_u_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(body_axis_derivs_seeds=bd_d_rev)[4] + gamma_u_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(body_axis_derivs_seeds=bd_d_rev)[5] rev_sum = np.sum(gamma_u_seeds_rev * gamma_u_seeds_fwd) @@ -247,7 +247,7 @@ def test_rev_ref(self): for deriv_func, var_dict in self.ovl_solver.case_body_derivs_to_fort_var.items(): body_axis_deriv_seeds_rev[deriv_func] = np.random.rand(1)[0] - ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(body_axis_derivs_seeds=body_axis_deriv_seeds_rev)[6] + ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(body_axis_derivs_seeds=body_axis_deriv_seeds_rev)[7] self.ovl_solver.clear_ad_seeds_fast() diff --git a/tests/test_consurf_partial_derivs.py b/tests/test_consurf_partial_derivs.py index 8abb73b..f1a0939 100644 --- a/tests/test_consurf_partial_derivs.py +++ b/tests/test_consurf_partial_derivs.py @@ -168,7 +168,7 @@ def test_rev_gamma_d(self): res_d_seeds_fwd = self.ovl_solver._execute_jac_vec_prod_fwd(gamma_d_seeds=gamma_d_seeds_fwd)[5] self.ovl_solver.clear_ad_seeds_fast() - gamma_d_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_d_seeds=res_d_seeds_rev)[3] + gamma_d_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_d_seeds=res_d_seeds_rev)[4] gamma_sum = np.sum(gamma_d_seeds_rev * gamma_d_seeds_fwd) res_sum = np.sum(res_d_seeds_rev * res_d_seeds_fwd) @@ -359,7 +359,7 @@ def test_rev_gamma_d(self): for deriv_func in cs_d_fwd: cs_d_rev = {deriv_func: 1.0} - gamma_d_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(consurf_derivs_seeds=cs_d_rev)[3] + gamma_d_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(consurf_derivs_seeds=cs_d_rev)[4] rev_sum = np.sum(gamma_d_seeds_rev * gamma_d_seeds_fwd) diff --git a/tests/test_input_dict.py b/tests/test_input_dict.py index 1565960..477db9a 100644 --- a/tests/test_input_dict.py +++ b/tests/test_input_dict.py @@ -71,32 +71,29 @@ surf = {"Wing": {}} cont_surfs = { - # Control Surfaces - "icontd": [np.array([0], dtype=np.int32), np.array([], dtype=np.int32)], # control variable index - "xhinged": [np.array([0.8]), np.array([], dtype=np.float64)], # x/c location of hinge - "vhinged": [ - np.array([[0.0, 0.0, 0.0]]), - np.array([], dtype=np.float64), - ], # vector giving hinge axis about which surface rotates - "gaind": [np.array([1.0]), np.array([], dtype=np.float64)], # control surface gain - "refld": [ - np.array([1.0]), - np.array([], dtype=np.float64), - ], # control surface reflection, sign of deflection for duplicated surface + "control_assignments": { + "flap" : {"assignment":np.arange(0,1), + "xhinged": 0.8, # x/c location of hinge + "vhinged": np.zeros(3), # vector giving hinge axis about which surface rotates + "gaind": 1.0, # control surface gain + "refld": 1.0 # control surface reflection, sign of deflection for duplicated surface + } + }, } des_var = { - # Design Variables (AVL) - "idestd": [np.array([0], dtype=np.int32), np.array([], dtype=np.int32)], # design variable index - "gaing": [np.array([1.0]), np.array([], dtype=np.float64)], # desgin variable gain + "design_var_assignments": { + "des" : {"assignment":np.arange(0,1), + "gaing":1.0} + }, } cont_surf_names = { - "dname": np.array(["flap"]), # Name of control input for each corresonding index + "dname": ["flap"], # Name of control input for each corresonding index } des_var_names = { - "gname": np.array(["des"]), # Name of design var for each corresonding index + "gname": ["des"], # Name of design var for each corresonding index } section_geom_naca = { diff --git a/tests/test_io.py b/tests/test_io.py index 04ee61b..877de06 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -8,6 +8,7 @@ # ============================================================================= import os import psutil +import re # ============================================================================= # External Python modules @@ -168,11 +169,7 @@ def test_ref_data(self): new_mach, mach0, err_msg=f"Mach does not match set value") - - - - class TestFortranLevelAPI(unittest.TestCase): def setUp(self): self.ovl = OVLSolver(geo_file=geom_file, mass_file=mass_file) @@ -192,6 +189,35 @@ def test_get_array(self): self.assertEqual(chords.shape, (100, 301)) np.testing.assert_array_equal(chords[0, :5], np.array([0.45, 0.45, 0.4, 0.3, 0.2])) +def parse_constants_file(filepath: str) -> dict[str, int]: + constants = {} + with open(filepath, 'r') as f: + for line in f: + line = line.strip() + # Skip comments and blank lines + if not line or line.startswith('!'): + continue + # Skip commented-out PARAMETER lines + if line.startswith('!'): + continue + # Match active PARAMETER lines + match = re.match(r'PARAMETER\s*\(\s*(\w+)\s*=\s*(\d+)\s*\)', line) + if match: + name = match.group(1) + value = int(match.group(2)) + constants[name] = value + return constants + + +class TestConstants(unittest.TestCase): + def setUp(self): + self.ovl = OVLSolver(geo_file=geom_file, mass_file=mass_file) + + def test_constants(self): + # read the constants from src + constants = parse_constants_file(os.path.join(base_dir, "..", "src", "includes", "ADIMEN.INC")) + for var in constants: + assert getattr(self.ovl, var) == constants[var] if __name__ == "__main__": unittest.main() diff --git a/tests/test_mesh_input.py b/tests/test_mesh_input.py new file mode 100644 index 0000000..2554e85 --- /dev/null +++ b/tests/test_mesh_input.py @@ -0,0 +1,262 @@ +# ============================================================================= +# Standard modules +# ============================================================================= +import os + +# ============================================================================= +# Extension modules +# ============================================================================= +from optvl import OVLSolver + +# ============================================================================= +# External Python modules +# ============================================================================= +import unittest +import numpy as np +import pickle + + +base_dir = os.path.dirname(os.path.abspath(__file__)) # Path to current folder +geom_dir = os.path.join(base_dir, '..', 'geom_files') + +mesh_file = os.path.join(geom_dir, "wing_mesh.pkl") + + +with open(mesh_file, 'rb') as f: + mesh = pickle.load(f) + +surf = { + "Wing": { + # General + "component": np.int32(1), # logical surface component index (for grouping interacting surfaces, see AVL manual) + "yduplicate": np.float64(0.0), # surface is duplicated over the ysymm plane + # "wake": np.int32( + # 1 + # ), # specifies that this surface is to NOT shed a wake, so that its strips will not have their Kutta conditions imposed + # "albe": np.int32( + # 1 + # ), # specifies that this surface is unaffected by freestream direction changes specified by the alpha,beta angles and p,q,r rotation rates + # "load": np.int32( + # 1 + # ), # specifies that the force and moment on this surface is to NOT be included in the overall forces and moments of the configuration + # "clcdsec": np.array( + # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + # ), # profile-drag CD(CL) function for each section in this surface (provide a single entry and OptVL applies to all strips, otherwise provide a vector corresponding to each strip) + # "cdcl": np.array( + # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + # ), # profile-drag CD(CL) function for all sections in this surface, overrides Tahnks. + "claf": 1.0, # CL alpha (dCL/da) scaling factor per section (provide a single entry and OptVL applies to all strips, otherwise provide a vector corresponding to each strip) + + # Geometry + "scale": np.array( + [1.0, 1.0, 1.0], dtype=np.float64 + ), # scaling factors applied to all x,y,z coordinates (chords arealso scaled by Xscale) + "translate": np.array( + [0.0, 0.0, 0.0], dtype=np.float64 + ), # offset added on to all X,Y,Z values in this surface + "angle": np.float64(0.0), # offset added on to the Ainc values for all the defining sections in this surface + "aincs": np.ones(mesh.shape[1]), # incidence angle vector (provide a single entry and OptVL applies to all strips, otherwise provide a vector corresponding to each strip) + + # Geometry: Mesh + "mesh": np.float64(mesh), # (nx,ny,3) numpy array containing mesh coordinates + "flatten mesh": True, # True by default so can be turned off or just excluded (not recommended) + + # Geometry: Cross Sections (provide a single entry and OptVL applies to all strips, otherwise provide a vector corresponding to each strip) + # "xfminmax": np.array([[0.0, 1.0]]), # airfoil x/c limits + # NACA + # 'naca' : '2412', # 4-digit NACA airfoil + # Direct Assignment of camberline/thickness + # 'xasec': np.array([[0., 1.]]), # the x coordinate aifoil section + # 'casec': np.array([[0., 0.]]), # camber line at xasec + # 'tasec': np.array([[0., 0.]]), # thickness at xasec + # 'xuasec': np.array([[0., 0.]]), # airfoil upper surface x-coords (alternative to specifying camber line) + # 'xlasec': np.array([[0., 0.]]), # airfoil lower surface x-coords (alternative to specifying camber line) + # 'zuasec': np.array([[0., 0.]]), # airfoil upper surface z-coords (alternative to specifying camber line) + # 'zlasec': np.array([[0., 0.]]), # airfoil lower surface z-coords (alternative to specifying camber line) + # Airfoil Files + 'afiles': 'airfoils/ag40d.dat', # airfoil file names + + + # Control Surface Specification + "control_assignments": { + "flap" : {"assignment":np.arange(0,mesh.shape[1]), + "xhinged": 0.8, # x/c location of hinge + "vhinged": np.zeros(3), # vector giving hinge axis about which surface rotates + "gaind": 1.0, # control surface gain + "refld": 1.0 # control surface reflection, sign of deflection for duplicated surface + } + }, + + # Design Variables (AVL) Specification + "design_var_assignments": { + "des" : {"assignment":np.arange(0,mesh.shape[1]), + "gaing":1.0} + }, + } +} + + +surf_avl = { + "Wing": { + # General + "num_sections": np.int32(2), + "component": np.int32(1), # logical surface component index (for grouping interacting surfaces, see AVL manual) + "yduplicate": np.float64(0.0), # surface is duplicated over the ysymm plane + # "wake": np.int32( + # 1 + # ), # specifies that this surface is to NOT shed a wake, so that its strips will not have their Kutta conditions imposed + # "albe": np.int32( + # 1 + # ), # specifies that this surface is unaffected by freestream direction changes specified by the alpha,beta angles and p,q,r rotation rates + # "load": np.int32( + # 1 + # ), # specifies that the force and moment on this surface is to NOT be included in the overall forces and moments of the configuration + # "clcdsec": np.array( + # [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] + # ), # profile-drag CD(CL) function for each section in this surface + # "cdcl": np.array( + # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + # ), # profile-drag CD(CL) function for all sections in this surface, overrides Tahnks. + "claf": np.array([1.0, 1.0]), # CL alpha (dCL/da) scaling factor per section + + # Geometry + "scale": np.array( + [1.0, 1.0, 1.0], dtype=np.float64 + ), # scaling factors applied to all x,y,z coordinates (chords arealso scaled by Xscale) + "translate": np.array( + [0.0, 0.0, 0.0], dtype=np.float64 + ), # offset added on to all X,Y,Z values in this surface + # "angle": np.float64(0.0), # offset added on to the Ainc values for all the defining sections in this surface + "xles": np.array([0.0, 0.0]), # leading edge cordinate vector(x component) + "yles": np.array([-5.0, 0.0]), # leading edge cordinate vector(y component) + "zles": np.array([0.0, 0.0]), # leading edge cordinate vector(z component) + "chords": np.array([1.0, 1.0]), # chord length vector + "aincs": np.ones(2),#np.array([0.0, 0.0]), # incidence angle vector + + # Geometry: Cross Sections + # "xfminmax": np.array([[0.0, 1.0], [0.0, 1.0]]), # airfoil x/c limits + # NACA + # 'naca' : np.array(['2412','2412']), # 4-digit NACA airfoil + # Coordinates + # 'xasec': np.array([[0., 1.], [0., 1.]]), # the x coordinate aifoil section + # 'casec': np.array([[0., 0.], [0., 0.]]), # camber line at xasec + # 'tasec': np.array([[0., 0.], [0., 0.]]), # thickness at xasec + # 'xuasec': np.array([[0., 0.], [0., 0.]]), # airfoil upper surface x-coords (alternative to specifying camber line) + # 'xlasec': np.array([[0., 0.], [0., 0.]]), # airfoil lower surface x-coords (alternative to specifying camber line) + # 'zuasec': np.array([[0., 0.], [0., 0.]]), # airfoil upper surface z-coords (alternative to specifying camber line) + # 'zlasec': np.array([[0., 0.], [0., 0.]]), # airfoil lower surface z-coords (alternative to specifying camber line) + # Airfoil Files + 'afiles': np.array(['airfoils/ag40d.dat','airfoils/ag40d.dat']), # airfoil file names + + # Paneling + "nchordwise": np.int32(10), # number of chordwise horseshoe vortice s placed on the surface + "cspace": np.float64(0.0), # chordwise vortex spacing parameter + "nspan": np.int32(6), # number of spanwise horseshoe vortices placed on the entire surface + "sspace": np.float64(0.0), # spanwise vortex spacing parameter for entire surface + # "nspans": np.array([5, 5], dtype=np.int32), # number of spanwise elements vector + # "sspaces": np.array([3.0, 3.0], dtype=np.float64), # spanwise spacing vector (for each section) + "use surface spacing": np.int32( + 1 + ), # surface spacing set under the surface heeading (known as LSURFSPACING in AVL) + + # Control Surfaces + "control_assignments": { + "flap" : {"assignment":np.array([0, 1],dtype=np.int32), + "xhinged": 0.8, # x/c location of hinge + "vhinged": np.zeros(3), # vector giving hinge axis about which surface rotates + "gaind": 1.0, # control surface gain + "refld": 1.0 # control surface reflection, sign of deflection for duplicated surface + } + }, + + # Design Variables (AVL) + "design_var_assignments": { + "des" : {"assignment":np.array([0, 1],dtype=np.int32), + "gaing":1.0} + }, + + } +} + + +geom_mesh = { + "title": "Aircraft", + "mach": np.float64(0.0), + "iysym": np.int32(0), + "izsym": np.int32(0), + "zsym": np.float64(0.0), + "Sref": np.float64(10.0), + "Cref": np.float64(1.0), + "Bref": np.float64(10.0), + "XYZref": np.array([0.25, 0, 0],dtype=np.float64), + "CDp": np.float64(0.0), + "surfaces": surf, + # Global Control and DV info + "dname": ["flap"], # Name of control input for each corresonding index + "gname": ["des"], # Name of design var for each corresonding index +} + +geom_avl = { + "title": "Aircraft", + "mach": np.float64(0.0), + "iysym": np.int32(0), + "izsym": np.int32(0), + "zsym": np.float64(0.0), + "Sref": np.float64(10.0), + "Cref": np.float64(1.0), + "Bref": np.float64(10.0), + "XYZref": np.array([0.25, 0, 0],dtype=np.float64), + "CDp": np.float64(0.0), + "surfaces": surf_avl, + # Global Control and DV info + "dname": ["flap"], # Name of control input for each corresonding index + "gname": ["des"], # Name of design var for each corresonding index +} + +keys_forces = ["CL", "CD"] + +class TestMesh(unittest.TestCase): + def setUp(self): + self.ovl_mesh = OVLSolver(input_dict=geom_mesh) + self.ovl_avl = OVLSolver(input_dict=geom_avl) + + def test_forces(self): + self.ovl_mesh.set_variable("alpha", 2.0) + self.ovl_avl.set_variable("alpha", 2.0) + + self.ovl_mesh.execute_run() + self.ovl_avl.execute_run() + + forces_mesh = self.ovl_mesh.get_total_forces() + forces_avl = self.ovl_avl.get_total_forces() + + for key in keys_forces: + np.testing.assert_allclose( + forces_mesh[key], + forces_avl[key], + rtol=1e-8, + ) + + def test_control_surfaces(self): + + self.ovl_mesh.set_variable("alpha", 0.0) + self.ovl_avl.set_variable("alpha", 0.0) + + self.ovl_mesh.set_control_deflection("flap", 2.0) + self.ovl_avl.set_control_deflection("flap", 2.0) + + self.ovl_mesh.execute_run() + self.ovl_avl.execute_run() + + forces_mesh = self.ovl_mesh.get_total_forces() + forces_avl = self.ovl_avl.get_total_forces() + + for key in keys_forces: + np.testing.assert_allclose( + forces_mesh[key], + forces_avl[key], + rtol=1e-8, + ) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_mesh_partials.py b/tests/test_mesh_partials.py new file mode 100644 index 0000000..8cf1940 --- /dev/null +++ b/tests/test_mesh_partials.py @@ -0,0 +1,375 @@ +# ============================================================================= +# Extension modules +# ============================================================================= +from optvl import OVLSolver + +# ============================================================================= +# Standard Python Modules +# ============================================================================= +import os +import psutil + +# ============================================================================= +# External Python modules +# ============================================================================= +import unittest +import numpy as np +import pickle + + +base_dir = os.path.dirname(os.path.abspath(__file__)) # Path to current folder +geom_dir = os.path.join(base_dir, '..', 'geom_files') +rect_file = os.path.join(geom_dir, 'rect_with_body.pkl') +with open(rect_file, 'rb') as f: + input_dict = pickle.load(f) + + +class TestFunctionPartials(unittest.TestCase): + def setUp(self): + # self.ovl_solver = OVLSolver(geo_file=rect_file) + self.ovl_solver = OVLSolver(input_dict=input_dict) + self.ovl_solver.set_variable("alpha", 25.0) + self.ovl_solver.set_variable("beta", 5.0) + self.ovl_solver.execute_run() + + def tearDown(self): + # Get the memory usage of the current process using psutil + process = psutil.Process() + mb_memory = process.memory_info().rss / (1024 * 1024) # Convert bytes to MB + print(f"{self.id():80} Memory usage: {mb_memory:.2f} MB") + + def test_fwd_mesh(self): + np.random.seed(111) + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds = np.random.rand(*arr.shape) + + func_seeds = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}} + )[0] + + func_seeds_FD = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}}, mode="FD", step=1e-7 + )[0] + + for func_key in func_seeds: + rel_error = np.linalg.norm(func_seeds[func_key] - func_seeds_FD[func_key]) / np.linalg.norm( + func_seeds_FD[func_key] + 1e-15 + ) + + # print( + # f"{func_key:10} wrt {surf_key}:{geom_key} AD:{func_seeds[func_key]: .5e} FD:{func_seeds_FD[func_key]: .5e} rel_error:{rel_error: .3e}" + # ) + + tol = 1e-13 + if np.abs(func_seeds[func_key]) < tol or np.abs(func_seeds_FD[func_key]) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + func_seeds[func_key], + func_seeds_FD[func_key], + atol=1e-6, + err_msg=f"func_key {func_key} w.r.t. {mesh_key}", + ) + else: + np.testing.assert_allclose( + func_seeds[func_key], + func_seeds_FD[func_key], + rtol=5e-4, + err_msg=f"func_key {func_key} w.r.t. {mesh_key}", + ) + + def test_rev_mesh(self): + np.random.seed(111) + + sens_dict_rev = {} + for func_key in self.ovl_solver.case_var_to_fort_var: + sens_dict_rev[func_key] = self.ovl_solver._execute_jac_vec_prod_rev(func_seeds={func_key: 1.0})[2] + self.ovl_solver.clear_ad_seeds_fast() + + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds = np.random.rand(*arr.shape) + + func_seeds_fwd = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}} + )[0] + + for func_key in func_seeds_fwd: + # use dot product test as design variables maybe arrays + rev_sum = np.sum(sens_dict_rev[func_key][surf_key][mesh_key] * mesh_seeds) + fwd_sum = np.sum(func_seeds_fwd[func_key]) + + # # print(mesh_seeds_rev) + tol = 1e-13 + # print(f"{func_key} wrt {surf_key}:{mesh_key}", "fwd", fwd_sum, "rev", rev_sum) + if np.abs(fwd_sum) < tol or np.abs(rev_sum) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + fwd_sum, + rev_sum, + atol=1e-14, + err_msg=f"func_key {func_key} w.r.t. {surf_key}:{mesh_key}", + ) + else: + np.testing.assert_allclose( + fwd_sum, + rev_sum, + rtol=1e-12, + err_msg=f"func_key {func_key} w.r.t. {surf_key}:{mesh_key}", + ) + +class TestResidualPartials(unittest.TestCase): + def setUp(self): + # self.ovl_solver = OVLSolver(geo_file=geom_file) + self.ovl_solver = OVLSolver(input_dict=input_dict) + self.ovl_solver.set_variable("alpha", 25.0) + self.ovl_solver.set_variable("beta", 5.0) + self.ovl_solver.execute_run() + + def tearDown(self): + # Get the memory usage of the current process using psutil + process = psutil.Process() + mb_memory = process.memory_info().rss / (1024 * 1024) # Convert bytes to MB + print(f"{self.id():80} Memory usage: {mb_memory:.2f} MB") + + def test_fwd_mesh(self): + np.random.seed(111) + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds = np.random.rand(*arr.shape) + + res_seeds = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}} + )[1] + + res_seeds_FD = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}}, mode="FD", step=1e-8 + )[1] + + abs_error = np.abs(res_seeds - res_seeds_FD) + rel_error = (res_seeds - res_seeds_FD) / (res_seeds + 1e-15) + idx_max_rel_error = np.argmax(np.abs(rel_error)) + idx_max_abs_error = np.argmax(np.abs(abs_error)) + + # print( + # f"{surf_key:10} {mesh_key:10} AD:{np.linalg.norm(res_seeds): .5e} FD:{np.linalg.norm(res_seeds_FD): .5e} max rel err:{(rel_error[idx_max_rel_error]): .3e} max abs err:{(np.max(abs_error)): .3e}" + # ) + np.testing.assert_allclose( + res_seeds, + res_seeds_FD, + atol=3e-5, + err_msg=f"func_key res w.r.t. {surf_key}:{mesh_key}", + ) + + def test_rev_mesh(self): + num_res = self.ovl_solver.get_mesh_size() + res_seeds_rev = np.random.seed(111) + res_seeds_rev = np.random.rand(num_res) + mesh_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_seeds=res_seeds_rev)[2] + + self.ovl_solver.clear_ad_seeds_fast() + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds = np.random.rand(*arr.shape) + + res_seeds = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}} + )[1] + + # do dot product + res_sum = np.sum(res_seeds_rev * res_seeds) + geom_sum = np.sum(mesh_seeds_rev[surf_key][mesh_key] * mesh_seeds) + + # print(f"res wrt {surf_key}:{mesh_key}", "rev", geom_sum, "fwd", res_sum) + + np.testing.assert_allclose( + res_sum, + geom_sum, + atol=1e-14, + err_msg=f"func_key res w.r.t. {surf_key}:{mesh_key}", + ) + +class TestResidualUPartials(unittest.TestCase): + def setUp(self): + # self.ovl_solver = OVLSolver(geo_file=geom_file) + self.ovl_solver = OVLSolver(input_dict=input_dict) + self.ovl_solver.set_variable("alpha", 25.0) + self.ovl_solver.set_variable("beta", 5.0) + self.ovl_solver.execute_run() + + def tearDown(self): + # Get the memory usage of the current process using psutil + process = psutil.Process() + mb_memory = process.memory_info().rss / (1024 * 1024) # Convert bytes to MB + print(f"{self.id()} Memory usage: {mb_memory:.2f} MB") + + def test_fwd_mesh(self): + np.random.seed(111) + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds = np.random.rand(*arr.shape) + + res_u_seeds = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}} + )[6] + + res_u_seeds_FD = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}}, mode="FD", step=1e-8 + )[6] + + abs_error = np.abs(res_u_seeds.flatten() - res_u_seeds_FD.flatten()) + rel_error = np.abs((res_u_seeds.flatten() - res_u_seeds_FD.flatten()) / (res_u_seeds.flatten() + 1e-15)) + + idx_max_rel_error = np.argmax(rel_error) + idx_max_abs_error = np.argmax(abs_error) + # print( + # f"{surf_key:10} {mesh_key:10} AD:{np.linalg.norm(res_u_seeds): .5e} FD:{np.linalg.norm(res_u_seeds_FD): .5e} max rel err:{(rel_error[idx_max_rel_error]): .3e} max abs err:{(np.max(abs_error)): .3e}" + # ) + np.testing.assert_allclose( + res_u_seeds, + res_u_seeds_FD, + atol=1e-4, + err_msg=f" res_u w.r.t. {surf_key}:{mesh_key}", + ) + + def test_rev_mesh(self): + np.random.seed(111) + num_gamma = self.ovl_solver.get_mesh_size() + res_u_seeds_rev = np.random.rand(self.ovl_solver.NUMAX, num_gamma) + + self.ovl_solver.clear_ad_seeds_fast() + + mesh_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_u_seeds=res_u_seeds_rev)[2] + self.ovl_solver.clear_ad_seeds_fast() + + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds = np.random.rand(*arr.shape) + + res_u_seeds_fwd = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds}} + )[6] + + # do dot product + res_sum = np.sum(res_u_seeds_rev * res_u_seeds_fwd) + mesh_sum = np.sum(mesh_seeds_rev[surf_key][mesh_key] * mesh_seeds) + + # print(f"res wrt {surf_key}:{mesh_key}", "rev", geom_sum, "fwd", res_sum) + + np.testing.assert_allclose( + res_sum, + mesh_sum, + atol=1e-14, + err_msg=f"res_u w.r.t. {surf_key}:{mesh_key}", + ) + self.ovl_solver.clear_ad_seeds_fast() + +class TestStabDerivDerivsPartials(unittest.TestCase): + def setUp(self): + # self.ovl_solver = OVLSolver(geo_file=geom_file, mass_file=mass_file) + # self.ovl_solver = OVLSolver(geo_file=geom_file) + self.ovl_solver = OVLSolver(input_dict=input_dict) + # self.ovl_solver = OVLSolver(geo_file="geom_files/rect.avl") + self.ovl_solver.set_variable("alpha", 45.0) + self.ovl_solver.set_variable("beta", 45.0) + self.ovl_solver.execute_run() + self.ovl_solver.clear_ad_seeds_fast() + + def tearDown(self): + # Get the memory usage of the current process using psutil + process = psutil.Process() + mb_memory = process.memory_info().rss / (1024 * 1024) # Convert bytes to MB + print(f"{self.id()} Memory usage: {mb_memory:.2f} MB") + + def test_fwd_mesh(self): + # this one is broken start here + np.random.seed(111) + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds = np.random.rand(*arr.shape) + + sd_d = self.ovl_solver._execute_jac_vec_prod_fwd(mesh_seeds={surf_key: {mesh_key: mesh_seeds}})[3] + + sd_d_fd = self.ovl_solver._execute_jac_vec_prod_fwd( + mesh_seeds={surf_key: {mesh_key: mesh_seeds}}, mode="FD", step=5e-8 + )[3] + + for deriv_func in sd_d: + sens_label = f"{deriv_func} wrt {surf_key}:{mesh_key:5}" + + # print(f"{sens_label} AD:{sd_d[deriv_func]} FD:{sd_d_fd[deriv_func]}") + # quit() + tol = 1e-10 + # print(f"{deriv_func} wrt {surf_key}:{mesh_key}", "fwd", fwd_sum, "rev", rev_sum) + if np.abs(sd_d[deriv_func]) < tol or np.abs(sd_d_fd[deriv_func]) < tol: + # If either value is basically zero, use an absolute tolerance + # this is basiccally saying if one is less than 1e-10 the other must be less than 5e-7 + np.testing.assert_allclose( + sd_d[deriv_func], + sd_d_fd[deriv_func], + atol=5e-7, + err_msg=sens_label, + ) + else: + np.testing.assert_allclose( + sd_d[deriv_func], + sd_d_fd[deriv_func], + rtol=5e-3, + err_msg=sens_label, + ) + + def test_rev_mesh(self): + np.random.seed(111) + sd_d_rev = {} + for deriv_func in self.ovl_solver.case_stab_derivs_to_fort_var: + sd_d_rev[deriv_func] = np.random.rand(1)[0] + + mesh_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(stab_derivs_seeds=sd_d_rev)[2] + self.ovl_solver.clear_ad_seeds_fast() + + for surf_key in self.ovl_solver.surf_mesh_to_fort_var: + for mesh_key in self.ovl_solver.surf_mesh_to_fort_var[surf_key]: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + mesh_seeds_fwd = np.random.rand(*arr.shape) + + sd_d_fwd = self.ovl_solver._execute_jac_vec_prod_fwd( + con_seeds={}, mesh_seeds={surf_key: {mesh_key: mesh_seeds_fwd}} + )[3] + + for deriv_func in self.ovl_solver.case_stab_derivs_to_fort_var: + # use dot product test as design variables maybe arrays + rev_sum = np.sum(mesh_seeds_rev[surf_key][mesh_key] * mesh_seeds_fwd) + + fwd_sum = 0.0 + for deriv_func in sd_d_fwd: + fwd_sum += sd_d_rev[deriv_func] * sd_d_fwd[deriv_func] + + # # print(mesh_seeds_rev) + tol = 1e-13 + # print(f"{deriv_func} wrt {surf_key}:{mesh_key}", "fwd", fwd_sum, "rev", rev_sum) + if np.abs(fwd_sum) < tol or np.abs(rev_sum) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + fwd_sum, + rev_sum, + atol=1e-14, + err_msg=f"deriv_func {deriv_func} w.r.t. {surf_key}:{mesh_key}", + ) + else: + np.testing.assert_allclose( + fwd_sum, + rev_sum, + rtol=1e-12, + err_msg=f"deriv_func {deriv_func} w.r.t. {surf_key}:{mesh_key}", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_mesh_totals.py b/tests/test_mesh_totals.py new file mode 100644 index 0000000..d12ae04 --- /dev/null +++ b/tests/test_mesh_totals.py @@ -0,0 +1,485 @@ +# ============================================================================= +# Extension modules +# ============================================================================= +from optvl import OVLSolver + +# ============================================================================= +# Standard Python Modules +# ============================================================================= +import os +import psutil + +# ============================================================================= +# External Python modules +# ============================================================================= +import unittest +import numpy as np +import pickle + + +base_dir = os.path.dirname(os.path.abspath(__file__)) # Path to current folder +geom_dir = os.path.join(base_dir, "..", "geom_files") +rect_file = os.path.join(geom_dir, 'rect_with_body.pkl') +with open(rect_file, 'rb') as f: + input_dict = pickle.load(f) + +class TestTotals(unittest.TestCase): + # TODO: beta derivatives likely wrong + + def setUp(self): + self.ovl_solver = OVLSolver(input_dict=input_dict) + self.ovl_solver.set_variable("alpha", 5.0) + self.ovl_solver.set_variable("beta", 0.0) + self.ovl_solver.set_parameter("Mach", 0.8) + self.ovl_solver.execute_run() + + def tearDown(self): + # Get the memory usage of the current process using psutil + process = psutil.Process() + mb_memory = process.memory_info().rss / (1024 * 1024) # Convert bytes to MB + print(f"{self.id()} Memory usage: {mb_memory:.2f} MB") + + def finite_dif(self, con_list, geom_seeds, mesh_seeds, param_seeds, ref_seeds, step=1e-7): + con_seeds = {} + + for con in con_list: + con_seeds[con] = 1.0 + self.ovl_solver.set_variable_ad_seeds(con_seeds, mode="FD", scale=step) + self.ovl_solver.set_geom_ad_seeds(geom_seeds, mode="FD", scale=step) + self.ovl_solver.set_mesh_ad_seeds(mesh_seeds, mode="FD", scale=step) + self.ovl_solver.set_parameter_ad_seeds(param_seeds, mode="FD", scale=step) + self.ovl_solver.set_reference_ad_seeds(ref_seeds, mode="FD", scale=step) + + self.ovl_solver.avl.update_surfaces() + self.ovl_solver.avl.get_res() + self.ovl_solver.avl.exec_rhs() + self.ovl_solver.avl.get_res() + self.ovl_solver.avl.velsum() + self.ovl_solver.avl.aero() + # self.ovl_solver.execute_run() + coef_data_peturb = self.ovl_solver.get_total_forces() + consurf_derivs_peturb = self.ovl_solver.get_control_stab_derivs() + stab_deriv_derivs_peturb = self.ovl_solver.get_stab_derivs() + body_axis_deriv_petrub = self.ovl_solver.get_body_axis_derivs() + body_forces_peturb = self.ovl_solver.get_body_forces() + + self.ovl_solver.set_variable_ad_seeds(con_seeds, mode="FD", scale=-1 * step) + self.ovl_solver.set_geom_ad_seeds(geom_seeds, mode="FD", scale=-1 * step) + self.ovl_solver.set_mesh_ad_seeds(mesh_seeds, mode="FD", scale=-1 * step) + self.ovl_solver.set_parameter_ad_seeds(param_seeds, mode="FD", scale=-1 * step) + self.ovl_solver.set_reference_ad_seeds(ref_seeds, mode="FD", scale=-1 * step) + + self.ovl_solver.avl.update_surfaces() + self.ovl_solver.avl.get_res() + self.ovl_solver.avl.exec_rhs() + self.ovl_solver.avl.get_res() + self.ovl_solver.avl.velsum() + self.ovl_solver.avl.aero() + # self.ovl_solver.execute_run() + + coef_data = self.ovl_solver.get_total_forces() + consurf_derivs = self.ovl_solver.get_control_stab_derivs() + stab_deriv_derivs = self.ovl_solver.get_stab_derivs() + body_axis_deriv = self.ovl_solver.get_body_axis_derivs() + body_forces = self.ovl_solver.get_body_forces() + + body_func_seeds = {} + for body in body_forces: + body_func_seeds[body] = {} + for key in body_forces[body]: + body_func_seeds[body][key] = (body_forces_peturb[body][key] - body_forces[body][key]) / step + + + func_seeds = {} + for func_key in coef_data: + func_seeds[func_key] = (coef_data_peturb[func_key] - coef_data[func_key]) / step + + consurf_derivs_seeds = {} + for func_key in consurf_derivs: + consurf_derivs_seeds[func_key] = (consurf_derivs_peturb[func_key] - consurf_derivs[func_key]) / step + + stab_derivs_seeds = {} + for func_key in stab_deriv_derivs: + stab_derivs_seeds[func_key] = (stab_deriv_derivs_peturb[func_key] - stab_deriv_derivs[func_key]) / step + + body_axis_derivs_seeds = {} + for deriv_func in body_axis_deriv: + body_axis_derivs_seeds[deriv_func] = ( + body_axis_deriv_petrub[deriv_func] - body_axis_deriv[deriv_func] + ) / step + + return func_seeds, consurf_derivs_seeds, stab_derivs_seeds, body_axis_derivs_seeds + + def test_aero_constraint(self): + # compare the analytical gradients with finite difference for each constraint and function + func_vars = self.ovl_solver.case_var_to_fort_var + stab_derivs = self.ovl_solver.case_stab_derivs_to_fort_var + body_axis_derivs = self.ovl_solver.case_body_derivs_to_fort_var + sens_funcs = self.ovl_solver.execute_run_sensitivities(func_vars) + sens_sd = self.ovl_solver.execute_run_sensitivities([], stab_derivs=stab_derivs, print_timings=False) + sens_bd = self.ovl_solver.execute_run_sensitivities([], body_axis_derivs=body_axis_derivs, print_timings=False) + + for con_key in self.ovl_solver.con_var_to_fort_var: + # for con_key in ['beta']: + func_seeds, consurf_deriv_seeds, stab_derivs_seeds, body_axis_derivs_seeds = self.finite_dif( + [con_key], {}, {}, {}, {}, step=1.0e-5 + ) + + # for func_key in func_vars: + for func_key in ['CX']: + ad_dot = sens_funcs[func_key][con_key] + fd_dot = func_seeds[func_key] + + # print(f"{func_key} wrt {con_key}", "AD", ad_dot, "FD", fd_dot) + rel_err = np.abs((ad_dot - fd_dot) / (fd_dot + 1e-20)) + + # print(f"{func_key:5} wrt {con_key:5} | AD:{ad_dot: 5e} FD:{fd_dot: 5e} rel err:{rel_err:.2e}") + + tol = 1e-8 + if np.abs(ad_dot) < tol or np.abs(fd_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + ad_dot, + fd_dot, + atol=1e-9, + err_msg=f"func_key {func_key} w.r.t. {con_key}", + ) + else: + np.testing.assert_allclose( + ad_dot, + fd_dot, + rtol=5e-5, + err_msg=f"func_key {func_key} w.r.t. {con_key}", + ) + + for func_key in stab_derivs: + ad_dot = sens_sd[func_key][con_key] + func_dot = stab_derivs_seeds[func_key] + + rel_err = np.abs(ad_dot - func_dot) / np.abs(func_dot + 1e-20) + + # print( + # f"{func_key} wrt {con_key} | AD:{ad_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + + tol = 1e-8 + if np.abs(ad_dot) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + ad_dot, + func_dot, + atol=5e-8, + err_msg=f"{func_key} wrt {con_key}", + ) + else: + np.testing.assert_allclose( + ad_dot, + func_dot, + rtol=5e-4, + err_msg=f"{func_key} wrt {con_key}", + ) + + for func_key in body_axis_derivs_seeds: + ad_dot = sens_bd[func_key][con_key] + func_dot = body_axis_derivs_seeds[func_key] + + rel_err = np.abs(ad_dot - func_dot) / np.abs(func_dot + 1e-20) + + # print( + # f"{func_key} wrt {con_key} | AD:{ad_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + + tol = 1e-7 # SAB: Had to increase this a bit to get test to pass for MESHES (dCn/dv wrt beta) + if np.abs(ad_dot) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + ad_dot, + func_dot, + atol=2e-8, + err_msg=f"{func_key} wrt {con_key}", + ) + else: + np.testing.assert_allclose( + ad_dot, + func_dot, + rtol=1e-3, + err_msg=f"{func_key} wrt {con_key}", + ) + + def test_mesh(self): + # compare the analytical gradients with finite difference for each + # geometric variable and function + + surf_key = list(self.ovl_solver.surf_mesh_to_fort_var.keys())[0] + mesh_vars = self.ovl_solver.surf_mesh_to_fort_var[surf_key] + # geom_vars += self.ovl_solver.surf_mesh_to_fort_var[surf_key] + cs_names = self.ovl_solver.get_control_names() + + consurf_vars = [] + for func_key in self.ovl_solver.case_derivs_to_fort_var: + consurf_vars.append(self.ovl_solver._get_deriv_key(cs_names[0], func_key)) + + func_vars = self.ovl_solver.case_var_to_fort_var + stab_derivs = self.ovl_solver.case_stab_derivs_to_fort_var + body_axis_derivs = self.ovl_solver.case_body_derivs_to_fort_var + + sens = self.ovl_solver.execute_run_sensitivities( + func_vars, + consurf_derivs=consurf_vars, + stab_derivs=stab_derivs, + body_axis_derivs=body_axis_derivs, + print_timings=False, + ) + + # for con_key in self.ovl_solver.con_var_to_fort_var: + sens_FD = {} + for surf_key in self.ovl_solver.surf_geom_to_fort_var: + sens_FD[surf_key] = {} + for mesh_key in mesh_vars: + arr = self.ovl_solver.get_mesh(self.ovl_solver.get_surface_index(surf_key)).reshape(-1,3) + np.random.seed(arr.size) + rand_arr = np.random.rand(*arr.shape) + rand_arr /= np.linalg.norm(rand_arr) + + func_seeds, consurf_deriv_seeds, stab_derivs_seeds, body_axis_derivs_seeds = self.finite_dif( + [], {}, {surf_key: {mesh_key: rand_arr}}, {}, {}, step=1.0e-7 + ) + + for func_key in func_vars: + mesh_dot = np.sum(sens[func_key][surf_key][mesh_key] * rand_arr) + func_dot = func_seeds[func_key] + + rel_err = np.abs(mesh_dot - func_dot) / np.abs(func_dot + 1e-20) + + # print( + # f"{func_key:5} wrt {surf_key}:{mesh_key:10} | AD:{mesh_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + tol = 1e-7 + if np.abs(mesh_dot) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + mesh_dot, + func_dot, + atol=1e-4, + err_msg=f"{func_key:5} wrt {surf_key}:{mesh_key:10}", + ) + else: + np.testing.assert_allclose( + mesh_dot, + func_dot, + rtol=5e-3, + err_msg=f"{func_key:5} wrt {surf_key}:{mesh_key:10}", + ) + + for func_key in consurf_vars: + # for cs_key in consurf_vars[func_key]: + mesh_dot = np.sum(sens[func_key][surf_key][mesh_key] * rand_arr) + func_dot = consurf_deriv_seeds[func_key] + + # rel_err = np.abs(mesh_dot - func_dot) / np.abs(func_dot + 1e-20) + # print( + # f"{func_key} wrt {surf_key}:{mesh_key:10} | AD:{mesh_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + + tol = 1e-8 + if np.abs(mesh_dot) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + mesh_dot, + func_dot, + atol=1e-4, + err_msg=f"{func_key} wrt {surf_key}:{mesh_key:10}", + ) + else: + np.testing.assert_allclose( + mesh_dot, + func_dot, + rtol=6e-3, + err_msg=f"{func_key} wrt {surf_key}:{mesh_key:10}", + ) + + for func_key in stab_derivs_seeds: + mesh_dot = np.sum(sens[func_key][surf_key][mesh_key] * rand_arr) + func_dot = stab_derivs_seeds[func_key] + + rel_err = np.abs(mesh_dot - func_dot) / np.abs(func_dot + 1e-20) + + # print( + # f"{func_key} wrt {surf_key}:{mesh_key:10} | AD:{mesh_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + + tol = 5e-7 + if np.abs(mesh_dot) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + mesh_dot, + func_dot, + atol=5e-9, + err_msg=f"{func_key} wrt {surf_key}:{mesh_key:10}", + ) + else: + np.testing.assert_allclose( + mesh_dot, + func_dot, + rtol=6e-3, + err_msg=f"{func_key} wrt {surf_key}:{mesh_key:10}", + ) + + for func_key in body_axis_derivs_seeds: + mesh_dot = np.sum(sens[func_key][surf_key][mesh_key] * rand_arr) + func_dot = body_axis_derivs_seeds[func_key] + + rel_err = np.abs(mesh_dot - func_dot) / np.abs(func_dot + 1e-20) + + # print( + # f"{func_key} wrt {surf_key}:{mesh_key:10} | AD:{mesh_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + + tol = 1e-6 + if np.abs(mesh_dot) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + mesh_dot, + func_dot, + atol=5e-8, + err_msg=f"{func_key} wrt {surf_key}:{mesh_key:10}", + ) + else: + np.testing.assert_allclose( + mesh_dot, + func_dot, + rtol=6e-3, + err_msg=f"{func_key} wrt {surf_key}:{mesh_key:10}", + ) + + def test_params(self): + # compare the analytical gradients with finite difference for each constraint and function + func_vars = self.ovl_solver.case_var_to_fort_var + stab_derivs = self.ovl_solver.case_stab_derivs_to_fort_var + + sens = self.ovl_solver.execute_run_sensitivities(func_vars, stab_derivs=stab_derivs) + + for param_key in self.ovl_solver.param_idx_dict: + func_seeds, consurf_deriv_seeds, stab_derivs_seeds, body_axis_derivs_seeds = self.finite_dif( + [], {}, {}, {param_key: 1.0}, {}, step=1.0e-6 + ) + + for func_key in func_vars: + ad_dot = sens[func_key][param_key] + fd_dot = func_seeds[func_key] + + # rel_err = np.abs((ad_dot - fd_dot) / (fd_dot + 1e-20)) + # print(f"{func_key:5} wrt {param_key:5} | AD:{ad_dot: 5e} FD:{fd_dot: 5e} rel err:{rel_err:.2e}") + + tol = 1e-13 + if np.abs(ad_dot) < tol or np.abs(fd_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + ad_dot, + fd_dot, + atol=1e-5, + err_msg=f"func_key {func_key} w.r.t. {param_key}", + ) + else: + np.testing.assert_allclose( + ad_dot, + fd_dot, + rtol=5e-4, + err_msg=f"func_key {func_key} w.r.t. {param_key}", + ) + + for func_key in stab_derivs_seeds: + ad_dot = sens[func_key][param_key] + func_dot = stab_derivs_seeds[func_key] + + # rel_err = np.abs(ad_dot - func_dot) / np.abs(func_dot + 1e-20) + # print( + # f"{func_key:20} wrt {param_key:10} | AD:{ad_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + + tol = 1e-8 + if np.abs(ad_dot) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + ad_dot, + func_dot, + atol=1e-7, # SAB: Needed to reduce this a bit for test to pass for MESHES (lateral parameter wrt Z cg) + err_msg=f"{func_key} wrt {param_key}", + ) + else: + np.testing.assert_allclose( + ad_dot, + func_dot, + rtol=1e-4, + err_msg=f"{func_key} wrt {param_key}", + ) + + def test_ref(self): + # compare the analytical gradients with finite difference for each constraint and function + func_vars = self.ovl_solver.case_var_to_fort_var + stab_derivs = self.ovl_solver.case_stab_derivs_to_fort_var + + sens = self.ovl_solver.execute_run_sensitivities(func_vars, stab_derivs=stab_derivs) + + for ref_key in self.ovl_solver.ref_var_to_fort_var: + # for con_key in ['beta']: + func_seeds, consurf_deriv_seeds, stab_derivs_seeds, body_axis_derivs_seeds = self.finite_dif( + [], {}, {}, {}, {ref_key: 1.0}, step=1.0e-5 + ) + + for func_key in func_vars: + ad_dot = sens[func_key][ref_key] + fd_dot = func_seeds[func_key] + + # print(f"{func_key} wrt {con_key}", "AD", ad_dot, "FD", fd_dot) + rel_err = np.abs((ad_dot - fd_dot) / (fd_dot + 1e-20)) + + # print(f"{func_key:5} wrt {ref_key:5} | AD:{ad_dot: 5e} FD:{fd_dot: 5e} rel err:{rel_err:.2e}") + + tol = 1e-13 + if np.abs(np.linalg.norm(ad_dot)) < tol or np.abs(fd_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + ad_dot, + fd_dot, + atol=1e-5, + err_msg=f"func_key {func_key} w.r.t. {ref_key}", + ) + else: + np.testing.assert_allclose( + ad_dot, + fd_dot, + rtol=5e-4, + err_msg=f"func_key {func_key} w.r.t. {ref_key}", + ) + + for func_key in stab_derivs_seeds: + ad_dot = sens[func_key][ref_key] + func_dot = stab_derivs_seeds[func_key] + + # rel_err = np.abs(ad_dot - func_dot) / np.abs(func_dot + 1e-20) + + # print( + # f"{func_key} wrt {var_key:5} wrt {ref_key} | AD:{ad_dot: 5e} FD:{func_dot: 5e} rel err:{rel_err:.2e}" + # ) + tol = 1e-8 + if np.abs(np.linalg.norm(ad_dot)) < tol or np.abs(func_dot) < tol: + # If either value is basically zero, use an absolute tolerance + np.testing.assert_allclose( + ad_dot, + func_dot, + atol=1e-9, + err_msg=f"{func_key} wrt {ref_key}", + ) + else: + np.testing.assert_allclose( + ad_dot, + func_dot, + rtol=1e-4, + err_msg=f"{func_key} wrt {ref_key}", + ) + + +if __name__ == "__main__": + unittest.main() + diff --git a/tests/test_partial_derivs.py b/tests/test_partial_derivs.py index ba2b94e..2edbda4 100644 --- a/tests/test_partial_derivs.py +++ b/tests/test_partial_derivs.py @@ -16,6 +16,7 @@ import numpy as np + base_dir = os.path.dirname(os.path.abspath(__file__)) # Path to current folder geom_dir = os.path.join(base_dir, '..', 'geom_files') @@ -215,7 +216,7 @@ def test_rev_gamma(self): self.ovl_solver.clear_ad_seeds_fast() for func_key in self.ovl_solver.case_var_to_fort_var: - gamma_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(func_seeds={func_key: 1.0})[2] + gamma_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(func_seeds={func_key: 1.0})[3] rev_sum = np.sum(gamma_seeds_rev * gamma_seeds_fwd) fwd_sum = np.sum(func_seeds_fwd[func_key]) @@ -262,7 +263,7 @@ def test_rev_param(self): self.ovl_solver.clear_ad_seeds_fast() for func_key in self.ovl_solver.case_var_to_fort_var: - param_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(func_seeds={func_key: 1.0})[5] + param_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(func_seeds={func_key: 1.0})[6] # print(f"{func_key} wrt {param_key}", "fwd ", func_seeds_fwd[func_key], "rev", param_seeds_rev[param_key]) tol = 1e-14 @@ -316,7 +317,7 @@ def test_rev_ref(self): self.ovl_solver.clear_ad_seeds_fast() for func_key in self.ovl_solver.case_var_to_fort_var: - ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(func_seeds={func_key: 1.0})[6] + ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(func_seeds={func_key: 1.0})[7] # print(f"{func_key} wrt {ref_key}", "fwd ", func_seeds_fwd[func_key], "rev", ref_seeds_rev[ref_key]) tol = 1e-14 @@ -465,7 +466,7 @@ def test_fwd_param(self): def test_rev_param(self): num_res = self.ovl_solver.get_mesh_size() res_seeds_rev = np.random.rand(num_res) - param_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_seeds=res_seeds_rev)[5] + param_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_seeds=res_seeds_rev)[6] self.ovl_solver.clear_ad_seeds_fast() @@ -498,7 +499,7 @@ def test_fwd_ref(self): def test_rev_ref(self): num_res = self.ovl_solver.get_mesh_size() res_seeds_rev = np.random.rand(num_res) - ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_seeds=res_seeds_rev)[6] + ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_seeds=res_seeds_rev)[7] self.ovl_solver.clear_ad_seeds_fast() diff --git a/tests/test_stab_derivs_partial_derivs.py b/tests/test_stab_derivs_partial_derivs.py index d549cc2..e404a84 100644 --- a/tests/test_stab_derivs_partial_derivs.py +++ b/tests/test_stab_derivs_partial_derivs.py @@ -165,7 +165,7 @@ def test_rev_gamma_u(self): res_u_seeds_fwd = self.ovl_solver._execute_jac_vec_prod_fwd(gamma_u_seeds=gamma_u_seeds_fwd)[6] self.ovl_solver.clear_ad_seeds_fast() - gamma_u_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_u_seeds=res_u_seeds_rev)[4] + gamma_u_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(res_u_seeds=res_u_seeds_rev)[5] gamma_sum = np.sum(gamma_u_seeds_rev * gamma_u_seeds_fwd) res_sum = np.sum(res_u_seeds_rev * res_u_seeds_fwd) @@ -378,7 +378,7 @@ def test_rev_gamma_u(self): # for var_key in sd_d_fwd[deriv_func]: sd_d_rev = {deriv_func: 1.0} - gamma_u_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(stab_derivs_seeds=sd_d_rev)[4] + gamma_u_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(stab_derivs_seeds=sd_d_rev)[5] rev_sum = np.sum(gamma_u_seeds_rev * gamma_u_seeds_fwd) @@ -425,7 +425,7 @@ def test_rev_ref(self): for deriv_func, var_dict in self.ovl_solver.case_stab_derivs_to_fort_var.items(): stab_deriv_seeds_rev[deriv_func] = np.random.rand(1)[0] - ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(stab_derivs_seeds=stab_deriv_seeds_rev)[6] + ref_seeds_rev = self.ovl_solver._execute_jac_vec_prod_rev(stab_derivs_seeds=stab_deriv_seeds_rev)[7] self.ovl_solver.clear_ad_seeds_fast() diff --git a/tests/test_total_derivs.py b/tests/test_total_derivs.py index 5d562ad..e7b87c3 100644 --- a/tests/test_total_derivs.py +++ b/tests/test_total_derivs.py @@ -211,6 +211,7 @@ def test_geom(self): surf_key = list(self.ovl_solver.surf_geom_to_fort_var.keys())[0] geom_vars = self.ovl_solver.surf_geom_to_fort_var[surf_key] + # geom_vars += self.ovl_solver.surf_mesh_to_fort_var[surf_key] cs_names = self.ovl_solver.get_control_names() consurf_vars = []