def test_outputs(self):
        surfaces = get_default_surfaces()

        group = Group()

        comp = VLMGeometry(surface=surfaces[0])

        indep_var_comp = IndepVarComp()

        indep_var_comp.add_output('def_mesh',
                                  val=surfaces[0]['mesh'],
                                  units='m')

        group.add_subsystem('geom', comp, promotes=['*'])
        group.add_subsystem('indep_var_comp', indep_var_comp, promotes=['*'])

        prob = Problem()
        prob.model.add_subsystem('group', group, promotes=['*'])
        prob.setup()
        prob.run_model()

        assert_rel_error(self, prob['widths'],
                         np.array([11.95624787, 11.90425878, 11.44086572]),
                         1e-6)
        assert_rel_error(self, prob['cos_sweep'],
                         np.array([9.7938336, 9.79384207, 9.79385053]), 1e-6)
        assert_rel_error(self, prob['S_ref'], np.array([415.02211208]), 1e-6)
        assert_rel_error(self, prob['chords'],
                         np.array([2.72796, 5.1252628, 7.8891638, 13.6189974]),
                         1e-6)
        assert_rel_error(self, prob['lengths'],
                         np.array([2.72796, 5.1252628, 7.8891638, 13.6189974]),
                         1e-6)
Example #2
0
    def setup(self):
        surface = self.options['surface']

        promotes = []
        if surface['struct_weight_relief']:
            promotes = promotes + list(
                set(['nodes', 'element_weights', 'load_factor']))
        if surface['distributed_fuel_weight']:
            promotes = promotes + list(set(['nodes', 'load_factor']))

        self.add_subsystem('struct_states',
                           SpatialBeamStates(surface=surface),
                           promotes_inputs=['K', 'forces', 'loads'] + promotes,
                           promotes_outputs=['disp'])

        self.add_subsystem('def_mesh',
                           DisplacementTransferGroup(surface=surface),
                           promotes_inputs=['nodes', 'mesh', 'disp'],
                           promotes_outputs=['def_mesh'])

        self.add_subsystem('aero_geom',
                           VLMGeometry(surface=surface),
                           promotes_inputs=['def_mesh'],
                           promotes_outputs=[
                               'b_pts', 'widths', 'cos_sweep', 'lengths',
                               'chords', 'normals', 'S_ref'
                           ])

        self.linear_solver = LinearRunOnce()
    def setup(self):
        surface = self.options['surface']

        self.add_subsystem('struct_states',
                           SpatialBeamStates(surface=surface),
                           promotes_inputs=[
                               'K', 'forces', 'loads', 'element_weights',
                               'nodes'
                           ],
                           promotes_outputs=['disp'])

        self.add_subsystem('def_mesh',
                           DisplacementTransfer(surface=surface),
                           promotes_inputs=['mesh', 'disp'],
                           promotes_outputs=['def_mesh'])

        self.add_subsystem('aero_geom',
                           VLMGeometry(surface=surface),
                           promotes_inputs=['def_mesh'],
                           promotes_outputs=[
                               'b_pts', 'c_pts', 'widths', 'cos_sweep',
                               'lengths', 'chords', 'normals', 'S_ref'
                           ])

        self.linear_solver = LinearRunOnce()
Example #4
0
    def setup(self):
        surfaces = self.options['surfaces']

        for surface in surfaces:
            name = surface['name']

            self.connect(name + '.normals', 'aero_states.' + name + '_normals')
            # self.connect(name + '.b_pts', 'aero_states.' + name + '_b_pts')
            # self.connect(name + '.c_pts', 'aero_states.' + name + '_c_pts')
            # self.connect(name + '.cos_sweep', 'aero_states.' + name + '_cos_sweep')
            # self.connect(name + '.widths', 'aero_states.' + name + '_widths')

            # Connect the results from 'aero_states' to the performance groups
            self.connect('aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces')

            # Connect S_ref for performance calcs
            self.connect(name + '.S_ref', name + '_perf.S_ref')
            self.connect(name + '.widths', name + '_perf.widths')
            self.connect(name + '.chords', name + '_perf.chords')
            self.connect(name + '.lengths', name + '_perf.lengths')
            self.connect(name + '.cos_sweep', name + '_perf.cos_sweep')

            # Connect S_ref for performance calcs
            self.connect(name + '.S_ref', 'total_perf.' + name + '_S_ref')
            self.connect(name + '.widths', 'total_perf.' + name + '_widths')
            self.connect(name + '.chords', 'total_perf.' + name + '_chords')
            self.connect(name + '.b_pts', 'total_perf.' + name + '_b_pts')
            self.connect(name + '_perf' + '.CL', 'total_perf.' + name + '_CL')
            self.connect(name + '_perf' + '.CD', 'total_perf.' + name + '_CD')
            self.connect('aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces')

            self.add_subsystem(name, VLMGeometry(surface=surface))

        # Add a single 'aero_states' component that solves for the circulations
        # and forces from all the surfaces.
        # While other components only depends on a single surface,
        # this component requires information from all surfaces because
        # each surface interacts with the others.
        aero_states = VLMStates(surfaces=surfaces)
        aero_states.linear_solver = LinearRunOnce()

        self.add_subsystem('aero_states',
                 aero_states,
                 promotes_inputs=['v', 'alpha', 'rho'],
                 promotes_outputs=['circulations'])

        # Explicitly connect parameters from each surface's group and the common
        # 'aero_states' group.
        # This is necessary because the VLMStates component requires information
        # from each surface, but this information is stored within each
        # surface's group.
        for surface in surfaces:
            self.add_subsystem(surface['name'] +'_perf', VLMFunctionals(surface=surface),
                    promotes_inputs=["v", "alpha", "M", "re", "rho"])

        self.add_subsystem('total_perf',
            TotalAeroPerformance(surfaces=surfaces),
            promotes_inputs=['v', 'rho', 'cg', 'S_ref_total'],
            promotes_outputs=['CM', 'CL', 'CD'])
Example #5
0
    def setup(self):
        surface = self.options['surface']

        promotes = []
        if surface['struct_weight_relief']:
            promotes = promotes + list(
                set(['nodes', 'element_mass', 'load_factor']))
        if surface['distributed_fuel_weight']:
            promotes = promotes + list(set(['nodes', 'load_factor']))
        if 'n_point_masses' in surface.keys():
            promotes = promotes + list(
                set([
                    'point_mass_locations', 'point_masses', 'nodes',
                    'load_factor', 'engine_thrusts'
                ]))

        self.add_subsystem(
            'struct_states',
            SpatialBeamStates(surface=surface),
            promotes_inputs=['local_stiff_transformed', 'forces', 'loads'] +
            promotes,
            promotes_outputs=['disp'])

        self.add_subsystem('def_mesh',
                           DisplacementTransferGroup(surface=surface),
                           promotes_inputs=['nodes', 'mesh', 'disp'],
                           promotes_outputs=['def_mesh'])

        normals_name = 'undeflected_normals'
        self.add_subsystem('aero_geom',
                           VLMGeometry(surface=surface),
                           promotes_inputs=['def_mesh'],
                           promotes_outputs=[
                               'b_pts', 'widths', 'cos_sweep', 'lengths',
                               'chords', ('normals', normals_name), 'S_ref'
                           ])

        # Add control surfaces
        if 'control_surfaces' in surface:
            for ctrl_surf in surface['control_surfaces']:
                #TODO: fixed name only works with one control surface
                deflected_normals_name = "normals"  #"{}_normals".format(ctrl_surf['name'])
                self.add_subsystem(
                    ctrl_surf['name'],
                    ControlSurface(surface=surface,
                                   panels=ctrl_surf['panels']),
                    promotes_inputs=[('normals', normals_name), 'delta_aileron'
                                     ],  #,f"delta_{control_surf['name']}"],
                    promotes_outputs=[('deflected_normals',
                                       deflected_normals_name)])
                #iterate through names
                normals_name = deflected_normals_name
        else:
            pass  #No control surface to add

        #self.connect(normals_name,'normals')

        self.linear_solver = LinearRunOnce()
Example #6
0
    def test(self):
        surfaces = get_default_surfaces()

        group = om.Group()

        comp = VLMGeometry(surface=surfaces[0])

        indep_var_comp = om.IndepVarComp()

        indep_var_comp.add_output('def_mesh', val=surfaces[0]['mesh'], units='m')

        group.add_subsystem('geom', comp)
        group.add_subsystem('indep_var_comp', indep_var_comp)

        group.connect('indep_var_comp.def_mesh', 'geom.def_mesh')

        run_test(self, group)
    def setup(self):
        surface = self.options['surface']

        promotes = []
        if surface['struct_weight_relief']:
            promotes = promotes + list(
                set(['nodes', 'element_mass', 'load_factor']))
        if surface['distributed_fuel_weight']:
            promotes = promotes + list(set(['nodes', 'load_factor']))
        if 'n_point_masses' in surface.keys():
            promotes = promotes + list(
                set([
                    'point_mass_locations', 'point_masses', 'nodes',
                    'load_factor', 'engine_thrusts'
                ]))

        self.add_subsystem(
            'struct_states',
            SpatialBeamStates(surface=surface),
            promotes_inputs=['local_stiff_transformed', 'forces', 'loads'] +
            promotes,
            promotes_outputs=['disp'])

        self.add_subsystem('def_mesh',
                           DisplacementTransferGroup(surface=surface),
                           promotes_inputs=['nodes', 'mesh', 'disp'],
                           promotes_outputs=['def_mesh'])

        self.add_subsystem('aero_geom',
                           VLMGeometry(surface=surface),
                           promotes_inputs=['def_mesh'],
                           promotes_outputs=[
                               'b_pts', 'widths', 'cos_sweep', 'lengths',
                               'chords', 'S_ref'
                           ])

        # Add control surfaces
        if 'control_surfaces' in surface:
            self.add_subsystem(
                'control_surfaces',
                ControlSurfacesGroup(
                    control_surfaces=surface['control_surfaces'],
                    mesh=surface['mesh']),
                promotes_inputs=['def_mesh'])

        self.linear_solver = om.LinearRunOnce()
Example #8
0
    def setup(self):
        surfaces = self.options['surfaces']
        rotational = self.options['rotational']

        # Loop through each surface and connect relevant parameters
        for surface in surfaces:
            name = surface['name']

            self.connect(name + '.normals', 'aero_states.' + name + '_normals')

            # Connect the results from 'aero_states' to the performance groups
            self.connect('aero_states.' + name + '_sec_forces',
                         name + '_perf' + '.sec_forces')

            # Connect S_ref for performance calcs
            self.connect(name + '.S_ref', name + '_perf.S_ref')
            self.connect(name + '.widths', name + '_perf.widths')
            self.connect(name + '.chords', name + '_perf.chords')
            self.connect(name + '.lengths', name + '_perf.lengths')
            self.connect(name + '.cos_sweep', name + '_perf.cos_sweep')

            # Connect S_ref for performance calcs
            self.connect(name + '.S_ref', 'total_perf.' + name + '_S_ref')
            self.connect(name + '.widths', 'total_perf.' + name + '_widths')
            self.connect(name + '.chords', 'total_perf.' + name + '_chords')
            self.connect(name + '.b_pts', 'total_perf.' + name + '_b_pts')
            self.connect(name + '_perf' + '.CL', 'total_perf.' + name + '_CL')
            self.connect(name + '_perf' + '.CD', 'total_perf.' + name + '_CD')
            self.connect('aero_states.' + name + '_sec_forces',
                         'total_perf.' + name + '_sec_forces')

            self.add_subsystem(name, VLMGeometry(surface=surface))

        # Add a single 'aero_states' component that solves for the circulations
        # and forces from all the surfaces.
        # While other components only depends on a single surface,
        # this component requires information from all surfaces because
        # each surface interacts with the others.
        if self.options['compressible'] == True:
            aero_states = CompressibleVLMStates(surfaces=surfaces,
                                                rotational=rotational)
            prom_in = ['v', 'alpha', 'beta', 'rho', 'Mach_number']
        else:
            aero_states = VLMStates(surfaces=surfaces, rotational=rotational)
            prom_in = ['v', 'alpha', 'beta', 'rho']

        aero_states.linear_solver = om.LinearRunOnce()

        if rotational:
            prom_in.extend(['omega', 'cg'])

        self.add_subsystem('aero_states',
                           aero_states,
                           promotes_inputs=prom_in,
                           promotes_outputs=['circulations'])

        # Explicitly connect parameters from each surface's group and the common
        # 'aero_states' group.
        # This is necessary because the VLMStates component requires information
        # from each surface, but this information is stored within each
        # surface's group.
        for surface in surfaces:
            self.add_subsystem(surface['name'] + '_perf',
                               VLMFunctionals(surface=surface),
                               promotes_inputs=[
                                   'v', 'alpha', 'beta', 'Mach_number', 're',
                                   'rho'
                               ])

        # Add the total aero performance group to compute the CL, CD, and CM
        # of the total aircraft. This accounts for all lifting surfaces.
        self.add_subsystem(
            'total_perf',
            TotalAeroPerformance(
                surfaces=surfaces,
                user_specified_Sref=self.options['user_specified_Sref']),
            promotes_inputs=['v', 'rho', 'cg', 'S_ref_total'],
            promotes_outputs=['CM', 'CL', 'CD'])
    def test_derivs_wetted(self):
        # This is a much richer test with the following attributes:
        # - 5x7 mesh so that each dimension of all variables is unique.
        # - Random values given as inputs.
        # - The mesh has been given a random z height at all points.
        # - S_ref_type option is "wetted"
        # Create a dictionary to store options about the mesh
        mesh_dict = {
            'num_y': 7,
            'num_x': 5,
            'wing_type': 'CRM',
            'symmetry': True,
            'num_twist_cp': 5
        }

        # Generate the aerodynamic mesh based on the previous dictionary
        mesh, twist_cp = generate_mesh(mesh_dict)

        mesh[:, :, 2] = np.random.random(mesh[:, :, 2].shape)

        # Create a dictionary with info and options about the aerodynamic
        # lifting surface
        surface = {
            # Wing definition
            'name': 'wing',  # name of the surface
            'symmetry': True,  # if true, model one half of wing
            # reflected across the plane y = 0
            'S_ref_type': 'wetted',  # how we compute the wing area,
            # can be 'wetted' or 'projected'
            'fem_model_type': 'tube',
            'twist_cp': twist_cp,
            'mesh': mesh,

            # 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.015,  # 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_cp':
            np.array([0.15]),  # thickness over chord ratio (NACA0015)
            'c_max_t': .303,  # chordwise location of maximum (NACA0015)
            # thickness
            'with_viscous': True,  # if true, compute viscous drag
            'with_wave': False,  # if true, compute wave drag
        }

        #surfaces = get_default_surfaces()
        surfaces = [surface]

        prob = Problem()
        group = prob.model

        comp = VLMGeometry(surface=surfaces[0])

        indep_var_comp = IndepVarComp()
        indep_var_comp.add_output('def_mesh',
                                  val=surfaces[0]['mesh'],
                                  units='m')

        group.add_subsystem('geom', comp)
        group.add_subsystem('indep_var_comp', indep_var_comp)

        group.connect('indep_var_comp.def_mesh', 'geom.def_mesh')

        prob.setup()

        prob['geom.def_mesh'] = np.random.random(prob['geom.def_mesh'].shape)

        prob.run_model()

        check = prob.check_partials(compact_print=True)
        assert_check_partials(check, atol=3e-5, rtol=1e-5)