How to specify an external aero deck
Desired documentation content.
Hello,
Apologies if I've missed it somewhere, but is there an example on how to use an existing aero deck? Some examples come close, but never actually detail how to use the files. I believe I have formatted the aero deck as it should be, e.g.
Mach,Altitude (ft),CL,CD,Angle of Attack (deg),
0.1,0,0,0.1,-3
0.1,0,0.1,0.1,-2
...
But then how do I specify that in an input file or even level 2 script? e.g. I figured you could do something like this
aircraft:design:aero_data_file,aero.csv,unitless
My existing research / what I tried
- Using external tabular formats is mentioned here but I can find no discussion of how to actually use it.
- The example here gets closer, but it does not mention how or what the
ExternalAeroshould return. i.e. do I have to write acomputeorlookupmethod?- In addition this method seems overly complicated. Based on the language elsewhere in the docs it seems as you should just be able to specify the path to the .csv.
- The Level 2 guide summary mentions that we saw how to "import additional files (e.g.
aero_data_file);", yet that is the only time that is mentioned in the guide - Also, I tried
ripgreping the repo for places that might assignaero_data_file,drag_polarorlift_polar, but didn't find any examples.
Thanks!
Is there any existing documentation on your requested topic? Please describe.
https://openmdao.github.io/Aviary/user_guide/aerodynamics.html#user-specified-tabular-drag-polars https://openmdao.github.io/Aviary/user_guide/external_aero.html https://openmdao.github.io/Aviary/getting_started/onboarding_level2.html#summary
After some ripgreping I found the Jupyter notebook that Externally Computed Aero is based off of. This was great because apparently page was hiding the code I needed to see. Namely the definition of ExternalAero and the definition of the compute method (look at that, I guessed right!).
I'm understanding better now, but this example doesn't actually run/solve the Aviary problem. When I added a prob.run_aviary_problem('history.db') to the end of the Jupyter notebook, it fails with the error shown below.
Traceback (most recent call last):
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\linear\direct.py", line 320, in _linearize
self._lu = scipy.sparse.linalg.splu(matrix)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\scipy\sparse\linalg\_dsolve\linsolve.py", line 438, in splu
return _superlu.gstrf(N, A.nnz, A.data, indices, indptr,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: Factor is exactly singular
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\UserA\projects\aviary\working_dir\plane\run_level2_external_aero.py", line 149, in <module>
prob.run_aviary_problem(record_filename)
File "C:\Users\UserA\projects\aviary\aviary\interface\methods_for_level2.py", line 2314, in run_aviary_problem
failed = dm.run_problem(self, run_driver=run_driver, simulate=simulate, make_plots=make_plots,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\dymos\run_problem.py", line 96, in run_problem
failed = _refine_iter(problem, refine_iteration_limit, refine_method, case_prefix=case_prefix,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\dymos\grid_refinement\refinement.py", line 40, in _refine_iter
failed = problem.run_driver(case_prefix=case_prefix if refine_iteration_limit > 0 else _case_prefix,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\utils\hooks.py", line 372, in __call__
ret = self.func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\problem.py", line 745, in run_driver
return driver._run()
^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\driver.py", line 791, in _run
self.result.success = not self.run()
^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\drivers\scipy_optimizer.py", line 268, in run
self._run_solve_nonlinear()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\driver.py", line 177, in wrapper
ret = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\driver.py", line 1237, in _run_solve_nonlinear
return self._problem().model.run_solve_nonlinear()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\system.py", line 4676, in run_solve_nonlinear
self._solve_nonlinear()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\group.py", line 3484, in _solve_nonlinear
self._nonlinear_solver._solve_with_cache_check()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 26, in _solve_with_cache_check
self.solve() # don't use caching
^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 45, in solve
self._gs_iter()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 865, in _gs_iter
subsys._solve_nonlinear()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\group.py", line 3484, in _solve_nonlinear
self._nonlinear_solver._solve_with_cache_check()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 26, in _solve_with_cache_check
self.solve() # don't use caching
^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 45, in solve
self._gs_iter()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 865, in _gs_iter
subsys._solve_nonlinear()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\group.py", line 3484, in _solve_nonlinear
self._nonlinear_solver._solve_with_cache_check()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 26, in _solve_with_cache_check
self.solve() # don't use caching
^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 45, in solve
self._gs_iter()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 865, in _gs_iter
subsys._solve_nonlinear()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\group.py", line 3484, in _solve_nonlinear
self._nonlinear_solver._solve_with_cache_check()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 26, in _solve_with_cache_check
self.solve() # don't use caching
^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\nonlinear_runonce.py", line 45, in solve
self._gs_iter()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 865, in _gs_iter
subsys._solve_nonlinear()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\group.py", line 3484, in _solve_nonlinear
self._nonlinear_solver._solve_with_cache_check()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 907, in _solve_with_cache_check
self.solve()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 669, in solve
raise err
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 665, in solve
self._solve()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 712, in _solve
norm0, norm = self._iter_initialize()
^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\newton.py", line 196, in _iter_initialize
self._gs_iter()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 865, in _gs_iter
subsys._solve_nonlinear()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\core\group.py", line 3484, in _solve_nonlinear
self._nonlinear_solver._solve_with_cache_check()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 907, in _solve_with_cache_check
self.solve()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 669, in solve
raise err
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 665, in solve
self._solve()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\solver.py", line 752, in _solve
self._single_iteration()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\newton.py", line 233, in _single_iteration
self._linearize()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\nonlinear\newton.py", line 161, in _linearize
self.linear_solver._linearize()
File "C:\Users\UserA\Anaconda3\envs\aviary\Lib\site-packages\openmdao\solvers\linear\direct.py", line 322, in _linearize
raise RuntimeError(format_singular_error(system, matrix))
RuntimeError: Singular entry found in 'traj.phases.climb.rhs_all.core_aerodynamics' <class SolvedAlphaGroup> for column associated with
state/residual 'balance.alpha' ('traj.phases.climb.rhs_all.core_aerodynamics.balance.alpha') index 0.
I saw the note about the compute function doing nothing in this case, just returning the table values. If I understand correctly, that should look up CL/CD based on in inputs to the function correct? I'm a little confused on the different between inputs and options. At the risk of seeming dumb, it seems to me they are switched? In the majority of cases area and span are constant, and you want to lookup on mach, alpha, and altitude. I may be misunderstanding the purpose of this block though.
def setup(self):
altitude = self.options['altitude']
mach = self.options['mach']
angle_of_attack = self.options['angle_of_attack']
self.add_input(Aircraft.Wing.AREA, 1.0, units='ft**2')
self.add_input(Aircraft.Wing.SPAN, 1.0, units='ft')
shape = (len(altitude), len(mach), len(angle_of_attack))
self.add_output('drag_table', shape=shape, units='unitless')
self.add_output('lift_table', shape=shape, units='unitless')
def compute(self, inputs, outputs):
"""
This component doesn't do anything, except set the drag and lift
polars from the file we read in.
"""
outputs['drag_table'] = CD
outputs['lift_table'] = CL
Thanks for your time!
Hey, iiwolf. Yeah, that drag polar component is purely notional. A real one would actually calculate lift and drag using inputs like area and span, but this was meant to be a simple test and example, so it basically "cheats".
As for the NANs -- I think there must be something else wrong in this model. We will have to take a look. We have done some recent refactoring, so this might be an example we missed.
Yeah, looks like there's a hole in our docs on available options for aero. Let me try and fill that gap here.
Aerodynamics has many methodologies you can use during a given mission phase. For example, for FLOPS-based aero you can select computed, low_speed, or tabular in your phase_info file.
What's missing is that there are additional options that can be selected for each of these methods. For tabular areo, I can point you to the part of the code that reads in that table. We have some options for that component related to passing data. You want CD0_data (that's a zero, not an "o"), and CDI_data, which can be either a filepath or a NamedValues object with the data in memory.
So your phase info might have something like this for a given phase:
{'core_aerodynamics': {'method': 'tabular',
'CD0_table': 'path/to/data/table'
'CDI_table': 'path/to/other/table'}
Sorry I'm just seeing these now - in the future if you post general questions in discussions I will see them much faster
Ok great this is helping clear things up. I have a few follow-ups but I will post them in discussions as suggested.
Thank you!