def test_rotate_symmetry(self): symmetry = True mesh = get_mesh(symmetry) prob = Problem() group = prob.model val = np.random.random(NY) comp = Rotate(val=val, mesh_shape=mesh.shape, symmetry=symmetry) group.add_subsystem('comp', comp) prob.setup() prob['comp.in_mesh'] = mesh prob.run_model() check = prob.check_partials(compact_print=True, abs_err_tol=1e-5, rel_err_tol=1e-5) assert_check_partials(check, atol=1e-6, rtol=1e-6)
def setup(self): surface = self.options['surface'] mesh = surface['mesh'] ny = mesh.shape[1] mesh_shape = mesh.shape symmetry = surface['symmetry'] # This flag determines whether or not changes in z (dihedral) add an # additional rotation matrix to modify the twist direction self.rotate_x = True # 1. Taper if 'taper' in surface: val = surface['taper'] promotes = ['taper'] else: val = 1. promotes = [] self.add_subsystem('taper', Taper(val=val, mesh=mesh, symmetry=symmetry), promotes_inputs=promotes) # 2. Scale X val = np.ones(ny) if 'chord_cp' in surface: promotes = ['chord'] else: promotes = [] self.add_subsystem('scale_x', ScaleX(val=val, mesh_shape=mesh_shape), promotes_inputs=promotes) # 3. Sweep if 'sweep' in surface: val = surface['sweep'] promotes = ['sweep'] else: val = 0. promotes = [] self.add_subsystem('sweep', Sweep(val=val, mesh_shape=mesh_shape, symmetry=symmetry), promotes_inputs=promotes) # 4. Shear X val = np.zeros(ny) if 'xshear_cp' in surface: promotes = ['xshear'] else: promotes = [] self.add_subsystem('shear_x', ShearX(val=val, mesh_shape=mesh_shape), promotes_inputs=promotes) # 5. Stretch if 'span' in surface: promotes = ['span'] val = surface['span'] else: # Compute span. We need .real to make span to avoid OpenMDAO warnings. quarter_chord = 0.25 * mesh[-1, :, :] + 0.75 * mesh[0, :, :] span = max(quarter_chord[:, 1]).real - min(quarter_chord[:, 1]).real if symmetry: span *= 2. val = span promotes = [] self.add_subsystem('stretch', Stretch(val=val, mesh_shape=mesh_shape, symmetry=symmetry), promotes_inputs=promotes) # 6. Shear Y val = np.zeros(ny) if 'yshear_cp' in surface: promotes = ['yshear'] else: promotes = [] self.add_subsystem('shear_y', ShearY(val=val, mesh_shape=mesh_shape), promotes_inputs=promotes) # 7. Dihedral if 'dihedral' in surface: val = surface['dihedral'] promotes = ['dihedral'] else: val = 0. promotes = [] self.add_subsystem('dihedral', Dihedral(val=val, mesh_shape=mesh_shape, symmetry=symmetry), promotes_inputs=promotes) # 8. Shear Z val = np.zeros(ny) if 'zshear_cp' in surface: promotes = ['zshear'] else: promotes = [] self.add_subsystem('shear_z', ShearZ(val=val, mesh_shape=mesh_shape), promotes_inputs=promotes) #Ajout Rémy : # 10. Dihedral Distrib val = np.zeros(ny - 1) if 'dihedral_distrib_cp' in surface: promotes = ['dihedral_distrib'] else: val = np.zeros(ny - 1) promotes = [] self.add_subsystem('dihedral_distrib', Dihedral_distrib(val=val, mesh_shape=mesh_shape, symmetry=symmetry), promotes_inputs=promotes) # 9. Rotate val = np.zeros(ny) if 'twist_cp' in surface: promotes = ['twist'] else: val = np.zeros(ny) promotes = [] self.add_subsystem('rotate', Rotate(val=val, mesh_shape=mesh_shape, symmetry=symmetry), promotes_inputs=promotes, promotes_outputs=['mesh']) # #Ajout Rémy : # # 10. Dihedral Distrib # val = np.zeros(ny) # if 'dihedral_distrib_cp' in surface: # promotes = ['dihedral_distrib'] # else: # val = np.zeros(ny) # promotes = [] # self.add_subsystem('dihedral_distrib', Dihedral_distrib(val=val, mesh_shape=mesh_shape, symmetry=symmetry), # promotes_inputs=promotes, promotes_outputs=['mesh']) # names = ['taper', 'scale_x', 'sweep', 'shear_x', 'stretch', 'shear_y', 'dihedral', # 'shear_z', 'rotate'] #Ajout rémy : names = [ 'taper', 'scale_x', 'sweep', 'shear_x', 'stretch', 'shear_y', 'dihedral', 'shear_z', 'dihedral_distrib', 'rotate' ] for j in np.arange(len(names) - 1): self.connect(names[j] + '.mesh', names[j + 1] + '.in_mesh')
def setup(self): nx = int(self.options["num_x"]) ny = int(self.options["num_y"]) n_twist = int(self.options["num_twist"]) # ================================================================= # Set up mesh # ================================================================= self.add_subsystem( "mesh", PlanformMesh(num_x=nx, num_y=ny), promotes_inputs=[ ("S", "ac|geom|wing|S_ref"), ("AR", "ac|geom|wing|AR"), ("taper", "ac|geom|wing|taper"), ("sweep", "ac|geom|wing|c4sweep"), ], ) # Add bspline component for twist x_interp = np.linspace(0.0, 1.0, ny) comp = self.add_subsystem( "twist_bsp", om.SplineComp(method="bsplines", x_interp_val=x_interp, num_cp=n_twist, interp_options={"order": min(n_twist, 4)}), promotes_inputs=[("twist_cp", "ac|geom|wing|twist")], ) comp.add_spline(y_cp_name="twist_cp", y_interp_name="twist", y_units="deg") # Apply twist spline to mesh self.add_subsystem( "twist_mesh", Rotate(val=np.zeros(ny), mesh_shape=(nx, ny, 3), symmetry=True)) self.connect("twist_bsp.twist", "twist_mesh.twist") self.connect("mesh.mesh", "twist_mesh.in_mesh") # ================================================================= # Compute atmospheric and fluid properties # ================================================================= self.add_subsystem( "temp", TemperatureComp(num_nodes=1), promotes_inputs=["fltcond|h", "fltcond|TempIncrement"]) self.add_subsystem("pressure", PressureComp(num_nodes=1), promotes_inputs=["fltcond|h"]) self.add_subsystem("density", DensityComp(num_nodes=1)) self.connect("temp.fltcond|T", "density.fltcond|T") self.connect("pressure.fltcond|p", "density.fltcond|p") self.add_subsystem("sound_speed", SpeedOfSoundComp(num_nodes=1)) self.connect("temp.fltcond|T", "sound_speed.fltcond|T") self.add_subsystem( "airspeed", om.ExecComp("Utrue = Mach * a", Utrue={ "units": "m/s", "val": 200.0 }, a={ "units": "m/s", "val": 300.0 }), promotes_inputs=[("Mach", "fltcond|M")], ) self.connect("sound_speed.fltcond|a", "airspeed.a") # Compute dimensionalized Reynolds number (use linear interpolation from standard atmosphere up # to 35k ft to estimate dynamic viscosity) self.add_subsystem( "Re_calc", om.ExecComp( "re = rho * u / (-3.329134*10**(-10) * h + 1.792398*10**(-5))", re={ "units": "1/m", "val": 1e6 }, rho={ "units": "kg/m**3", "val": 1.0 }, u={ "units": "m/s", "val": 100.0 }, h={ "units": "m", "val": 1.0 }, ), promotes_inputs=[("h", "fltcond|h")], ) self.connect("density.fltcond|rho", "Re_calc.rho") self.connect("airspeed.Utrue", "Re_calc.u") # ================================================================= # Call OpenAeroStruct # ================================================================= surf_dict = { "name": "wing", "mesh": np.zeros((nx, ny, 3)), # this must be defined # because the VLMGeometry component uses the shape of the mesh in this # dictionary to determine the size of the mesh; the values don't matter "symmetry": True, # if true, model one half of wing # reflected across the plane y = 0 "S_ref_type": "projected", # how we compute the wing area, # can be 'wetted' or 'projected' # Aerodynamic performance of the lifting surface at # an angle of attack of 0 (alpha=0). # These CL0 and CD0 values are added to the CL and CD # obtained from aerodynamic analysis of the surface to get # the total CL and CD. # These CL0 and CD0 values do not vary wrt alpha. "CL0": 0.0, # CL of the surface at alpha=0 "CD0": 0.0, # CD of the surface at alpha=0 # Airfoil properties for viscous drag calculation "k_lam": 0.05, # percentage of chord with laminar # flow, used for viscous drag "t_over_c": np.array([0.12]), # thickness over chord ratio (NACA SC2-0612) "c_max_t": 0.37, # chordwise location of maximum (NACA SC2-0612) # thickness "with_viscous": True, # if true, compute viscous drag "with_wave": True, # if true, compute wave drag } # Overwrite any options in the surface dict with those provided in the options if self.options["surf_options"] is not None: for key in self.options["surf_options"]: surf_dict[key] = self.options["surf_options"][key] self.add_subsystem( "aero_point", AeroPoint(surfaces=[surf_dict]), promotes_inputs=[("Mach_number", "fltcond|M"), ("alpha", "fltcond|alpha")], promotes_outputs=[ (f"{surf_dict['name']}_perf.CD", "fltcond|CD"), (f"{surf_dict['name']}_perf.CL", "fltcond|CL"), ], ) self.connect( "twist_mesh.mesh", [ f"aero_point.{surf_dict['name']}.def_mesh", f"aero_point.aero_states.{surf_dict['name']}_def_mesh" ], ) self.connect("airspeed.Utrue", "aero_point.v") self.connect("density.fltcond|rho", "aero_point.rho") self.connect("Re_calc.re", "aero_point.re") # Set input defaults for inputs that go to multiple locations self.set_input_defaults("fltcond|M", 0.1) self.set_input_defaults("fltcond|alpha", 0.0) # Set the thickness to chord ratio for wave and viscous drag calculation. # It must have a thickness to chord ratio for each panel, so there must be # ny-1 elements. Allow either one value (and duplicate it ny-1 times) or # an array of length ny-1, but nothing else. # NOTE: for aerostructural cases, this should be a design variable with control points over a spline if isinstance(surf_dict["t_over_c"], (int, float)) or surf_dict["t_over_c"].size == 1: self.set_input_defaults( f"aero_point.{surf_dict['name']}_perf.t_over_c", val=surf_dict["t_over_c"] * np.ones(ny - 1)) elif surf_dict["t_over_c"].size == ny - 1: self.set_input_defaults( f"aero_point.{surf_dict['name']}_perf.t_over_c", val=surf_dict["t_over_c"]) else: raise ValueError( f"t_over_c in the surface dict must be either a number or an ndarray " f"with either one or ny-1 elements, not {surf_dict['t_over_c']}" )