diff --git a/.gitignore b/.gitignore index c5ae4b4934..3a8bb5d15e 100644 --- a/.gitignore +++ b/.gitignore @@ -135,7 +135,6 @@ reports/ aviary/reports/ *.openmdao_out *.out -*.png *.sql *.db *.pdf diff --git a/aviary/core/aviary_problem.py b/aviary/core/aviary_problem.py index f84ca0e6bc..6967deb0a8 100644 --- a/aviary/core/aviary_problem.py +++ b/aviary/core/aviary_problem.py @@ -1,20 +1,23 @@ import csv import json import os +import subprocess import warnings from copy import deepcopy from datetime import datetime from enum import Enum from pathlib import Path +from packaging import version + +import numpy as np from itertools import count import dymos as dm -import numpy as np import openmdao import openmdao.api as om from openmdao.utils.reports_system import _default_reports from openmdao.utils.units import convert_units -from packaging import version +import openmdao.utils.hooks as hooks from aviary.core.aviary_group import AviaryGroup from aviary.interface.utils import set_warning_format @@ -1176,6 +1179,7 @@ def run_aviary_problem( simulate=False, make_plots=True, verbosity=None, + rt=False, ): """ This function actually runs the Aviary problem, which could be a simulation, @@ -1211,13 +1215,44 @@ def run_aviary_problem( else: verbosity = self.verbosity # defaults to BRIEF - if verbosity >= Verbosity.VERBOSE: # VERBOSE, DEBUG + if ( + verbosity >= Verbosity.VERBOSE or rt + ): # rt needs a driver recorder file to run the realtime plot server + recorder = om.SqliteRecorder('optimization_history.db') + self.driver.add_recorder(recorder) self.final_setup() + + if verbosity >= Verbosity.VERBOSE: # VERBOSE, DEBUG with open(self.get_reports_dir() / 'input_list.txt', 'w') as outfile: self.model.list_inputs(out_stream=outfile) - recorder = om.SqliteRecorder('optimization_history.db') - self.driver.add_recorder(recorder) + def _view_realtime_plot_hook(driver): + case_recorder_file = str(driver._rec_mgr._recorders[0]._filepath) + + cmd = ['openmdao', 'realtime_plot', '--pid', str(os.getpid()), case_recorder_file] + cp = subprocess.Popen(cmd) # nosec: trusted input + + # Do a quick non-blocking check to see if it immediately failed + # This will catch immediate failures but won't wait for the process to finish + quick_check = cp.poll() + if quick_check is not None and quick_check != 0: + # Process already terminated with an error + stderr = cp.stderr.read().decode() + raise RuntimeError( + f'Failed to start up the realtime plot server with code {quick_check}: {stderr}.' + ) + + # register the hook to stat up the real-time plot server + if rt: + if not self.driver: + raise RuntimeError( + 'Unable to run realtime optimization progress plot because no Driver' + ) + + hooks._register_hook( + '_setup_recording', 'Driver', post=_view_realtime_plot_hook, ncalls=1 + ) + hooks._setup_hooks(self.driver) if suppress_solver_print: self.set_solver_print(level=0) diff --git a/aviary/docs/examples_unreviewed/deconstructed_example.ipynb b/aviary/docs/examples_unreviewed/deconstructed_example.ipynb index e92c9bb80f..6304210db9 100644 --- a/aviary/docs/examples_unreviewed/deconstructed_example.ipynb +++ b/aviary/docs/examples_unreviewed/deconstructed_example.ipynb @@ -889,7 +889,7 @@ "outputs": [], "source": [ "# Testing Cell\n", - "from aviary.interface.download_models import get_model\n", + "from aviary.utils.functions import get_model\n", "\n", "get_model('advanced_single_aisle/advanced_single_aisle_data.py')" ] @@ -937,7 +937,7 @@ "# Testing Cell\n", "import os\n", "\n", - "from aviary.interface.download_models import get_model\n", + "from aviary.utils.functions import get_model\n", "\n", "all_files = {\n", " 'large_single_aisle_1': [''],\n", diff --git a/aviary/docs/user_guide/images/level1_realtime_plot_example.png b/aviary/docs/user_guide/images/level1_realtime_plot_example.png new file mode 100644 index 0000000000..2df3fd0159 Binary files /dev/null and b/aviary/docs/user_guide/images/level1_realtime_plot_example.png differ diff --git a/aviary/docs/user_guide_unreviewed/aviary_commands.ipynb b/aviary/docs/user_guide_unreviewed/aviary_commands.ipynb index 20002ad605..02178a212a 100644 --- a/aviary/docs/user_guide_unreviewed/aviary_commands.ipynb +++ b/aviary/docs/user_guide_unreviewed/aviary_commands.ipynb @@ -121,7 +121,7 @@ "execution_count": null, "metadata": { "tags": [ - "remove-cell" + "remove-input" ] }, "outputs": [], @@ -215,7 +215,9 @@ "{glue:md}`--phase_info` is the path to phase info file. If it is missing, it depends on the mission method (`equations_of_motion` with value of {glue:md}`2DOF` or {glue:md}`energy_state`) which is defined in the .csv input file.\n", "{glue:md}`--max_iter` is the maximum number of iterations. The default is {glue:md}`max_iter`.\n", "\n", - "More detailed information and examples can be found in the Level 1 interface." + "More detailed information and examples can be found in the [Level 1 interface](../getting_started/onboarding_level1.ipynb).\n", + "\n", + "Information on the real-time optimization plot feature can be found on the [Postprocessing and Visualizing Results from Aviary page](postprocessing_and_visualizing_results.ipynb#level-1-real-time-optimization-progress-plot)." ] }, { @@ -231,8 +233,8 @@ "# Testing Cell\n", "from aviary.utils.doctape import glue_actions\n", "\n", - "# glue all the options of 'aviary fortran_to_aviary'\n", - "glue_actions('fortran_to_aviary', current_glued_vars, md_code=True)" + "# glue all the options of 'aviary convert fortran_to_aviary'\n", + "glue_actions('convert', current_glued_vars, md_code=True, glue_choices=True)" ] }, { @@ -241,9 +243,9 @@ "metadata": {}, "source": [ "(aviary-fortran_to_aviary-command)=\n", - "### aviary fortran_to_aviary\n", + "### aviary convert fortran_to_aviary\n", "\n", - "The {glue:md}`aviary fortran_to_aviary` command will convert a Fortran input deck to an Aviary csv.\n", + "The {glue:md}`aviary convert fortran_to_aviary` command will convert a Fortran input deck to an Aviary csv.\n", "\n" ] }, @@ -252,7 +254,7 @@ "metadata": {}, "source": [ "```\n", - "aviary fortran_to_aviary -h\n", + "aviary convert fortran_to_aviary -h\n", "```" ] }, @@ -266,14 +268,14 @@ }, "outputs": [], "source": [ - "!aviary fortran_to_aviary -h 2>/dev/null" + "!aviary convert fortran_to_aviary -h 2>/dev/null" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The only two required inputs are the filepath to the input deck and {glue:md}`-l` (for {glue:md}`--legacy_code` with options `FLOPS` and `GASP`).\n", + "The only two required inputs are the filepath to the input deck and {glue:md}`-f` (for {glue:md}`--format` with options `FLOPS` and `GASP`).\n", "When this command is run, a brief message is printed. To print more messages, one can set verbosity level higher. For example, `-v 3` will result in debug messages being printed. See [Controlling Display Levels](../developer_guide/coding_standards.ipynb) for more details.\n", "If an invalid filepath is given, pre-packaged resources will be checked for input decks with a matching name.\n", "If the output file name is not specified, a detault name is assumed to be the trunk of the input file name with `csv` as file extension. For example, an input file `sample.dat` will result in `sample_converted.csv`.\n", @@ -298,118 +300,7 @@ "```\n", "\n", "Example usage:\n", - "`aviary fortran_to_aviary --legacy_code GASP --force GASP_test.dat` Converts the GASP input deck to Aviary (even if a file with the name GASP_test.dat already exists)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [], - "source": [ - "# Testing Cell\n", - "\n", - "# glue all the options of 'aviary convert_engine'\n", - "glue_actions('convert_engine', current_glued_vars, md_code=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(aviary-hangar-command)=\n", - "### aviary hangar\n", - "\n", - "The {glue:md}`aviary hangar` command will copy files and folders from the Aviary hangar to an accessible directory.\n", - "This is useful for users that have pip installed Aviary, but want to experiment with the included examples.\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```\n", - "aviary hangar -h\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "remove-input" - ] - }, - "outputs": [], - "source": [ - "!aviary hangar -h 2>/dev/null" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The only required input is the name of an input deck.\n", - "This can be specified as the name of the file, the path from [aviary/models](https://github.com/OpenMDAO/Aviary/tree/main/aviary/models), the name of a folder in aviary/models. Multiple files and folders can be copied at once.\n", - "Optionally, the output directory can be specified; if it is not, the files will be copied into the current working directory in a folder called `aviary_models`.\n", - "If specified, the output directory will be created as needed.\n", - "\n", - "Example usage:\n", - "```\n", - "# Copy all files in the engines folder\n", - "aviary hangar engines\n", - "# Copy the 22k turbofan deck\n", - "aviary hangar turbofan_22k.txt\n", - "# Copy the N3CC data\n", - "aviary hangar advanced_single_aisle/advanced_single_aisle_data.py\n", - "# Copy the Fortran and Aviary input decks for the small single aisle\n", - "aviary hangar small_single_aisle_GwGm.dat small_single_aisle_GwGm.csv\n", - "# Copy the engine model into ~/example_files\n", - "aviary hangar turbofan_22k.txt -o ~/example_files\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [], - "source": [ - "# Testing Cell\n", - "import os\n", - "import subprocess\n", - "import tempfile\n", - "\n", - "commands = [\n", - " 'engines',\n", - " 'turbofan_22k.csv',\n", - " 'advanced_single_aisle/advanced_single_aisle_data.py',\n", - " 'small_single_aisle_GASP.dat small_single_aisle_GASP.csv',\n", - " 'turbofan_22k.csv -o ~/example_files',\n", - "]\n", - "# Save the current directory\n", - "original_cwd = os.getcwd()\n", - "\n", - "try:\n", - " with tempfile.TemporaryDirectory() as tempdir:\n", - " os.chdir(tempdir)\n", - " for command in commands:\n", - " command = 'aviary hangar ' + command\n", - " subprocess.run(command.split()).check_returncode()\n", - "finally:\n", - " # Always restore the original directory\n", - " os.chdir(original_cwd)" + "`aviary convert fortran_to_aviary --format GASP --force GASP_test.dat` Converts the GASP input deck to Aviary (even if a file with the name GASP_test.dat already exists)." ] }, { @@ -417,9 +308,9 @@ "metadata": {}, "source": [ "(aviary-EDC-command)=\n", - "### aviary convert_engine\n", + "### aviary convert engine_deck\n", "\n", - "The {glue:md}`aviary convert_engine` command will convert a legacy formatted (FLOPS or GASP) engine deck into an Aviary formatted engine deck." + "The {glue:md}`aviary convert engine_deck` command will convert a legacy formatted (FLOPS or GASP) engine deck into an Aviary formatted engine deck." ] }, { @@ -427,7 +318,7 @@ "metadata": {}, "source": [ "```\n", - "aviary convert_engine -h\n", + "aviary convert engine_deck -h\n", "```" ] }, @@ -441,7 +332,7 @@ }, "outputs": [], "source": [ - "!aviary convert_engine -h 2>/dev/null" + "!aviary convert engine_deck -h 2>/dev/null" ] }, { @@ -456,7 +347,7 @@ "\n", "If the output file exists, it will be overwritten.\n", "\n", - "The engine format is specified by {glue:md}`-f` or {glue:md}`--data_format` with one of `FLOPS`, `GASP`, and `GASP_TS` string (`TS` stands for turboshaft). If multiple are specified, the last one will be used.\n", + "The engine format is specified by {glue:md}`-f` or {glue:md}`--format` with one of `FLOPS`, `GASP`, and `GASP_TS` string (`TS` stands for turboshaft). If multiple are specified, the last one will be used.\n", "\n", "Notes for input decks:\n", "- Turbofan decks for both FLOPS and GASP can be converted\n", @@ -468,11 +359,11 @@ "\n", "```\n", "# Convert a GASP based turbofan\n", - "aviary convert_engine turbofan_23k_1.eng turbofan_23k_1.csv -f GASP \n", + "aviary convert engine_deck turbofan_23k_1.eng turbofan_23k_1.csv -f GASP \n", "# Convert a FLOPS based turbofan\n", - "aviary convert_engine turbofan_22k.eng turbofan_22k.csv -f FLOPS \n", + "aviary convert engine_deck turbofan_22k.eng turbofan_22k.csv -f FLOPS \n", "# Convert a GASP based turboshaft\n", - "aviary convert_engine turboshaft_4465hp.eng turboshaft_4465hp.csv --data_format GASP_TS\n", + "aviary convert engine_deck turboshaft_4465hp.eng turboshaft_4465hp.csv --format GASP_TS\n", "```" ] }, @@ -495,23 +386,7 @@ " 'utils/test/data/turboshaft_4465hp.eng turboshaft_4465hp.csv -f GASP_TS',\n", "]\n", "for command in commands:\n", - " run_command_no_file_error('aviary convert_engine ' + command)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [], - "source": [ - "# Testing Cell\n", - "\n", - "# glue all the options of 'aviary convert_aero_table'\n", - "glue_actions('convert_aero_table', current_glued_vars, md_code=True)" + " run_command_no_file_error('aviary convert engine_deck ' + command)" ] }, { @@ -519,9 +394,9 @@ "metadata": {}, "source": [ "(aviary-ATC-command)=\n", - "### aviary convert_aero_table\n", + "### aviary convert aero_table\n", "\n", - "The {glue:md}`aviary convert_aero_table` command will convert a legacy formatted (FLOPS or GASP) aero table into an Aviary formatted aero table.\n" + "The {glue:md}`aviary convert aero_table` command will convert a legacy formatted (FLOPS or GASP) aero table into an Aviary formatted aero table.\n" ] }, { @@ -529,7 +404,7 @@ "metadata": {}, "source": [ "```\n", - "aviary convert_aero_table -h\n", + "aviary convert aero_table -h\n", "```" ] }, @@ -543,7 +418,7 @@ }, "outputs": [], "source": [ - "!aviary convert_aero_table -h 2>/dev/null" + "!aviary convert aero_table -h 2>/dev/null" ] }, { @@ -552,7 +427,7 @@ "source": [ "Users must provide the path or name of an input deck and the data format being converted.\n", "Optionally, the path to the output file can also be specified, otherwise the default output file will be in the same location and have the same name as input file, but with '_aviary' appended to the end of filename trunk while the file extension is preserved. For example, an input file `sample.txt` will result in `sample_aviary.txt` unless the user specifies the output file name.\n", - "If both {glue:md}`-f` and {glue:md}`--data_format` are specified, the later one is taken.\n", + "If both {glue:md}`-f` and {glue:md}`--format` are specified, the later one is taken.\n", "If an existing file has the same name as output file name, the existing file will be overwritten.\n", "If an invalid filepath is given for the input file, pre-packaged resources will be checked for input decks with a matching name.\n", "\n", @@ -567,11 +442,11 @@ "Example usage:\n", "```\n", "# Converts a GASP based aero table \n", - "aviary convert_aero_table -f GASP_ALT utils/test/data/aero_free_GASP.txt large_single_aisle_1_aero_flaps.csv\n", + "aviary convert aero_table -f GASP_ALT utils/test/data/aero_free_GASP.txt large_single_aisle_1_aero_flaps.csv\n", "# Converts a GASP based alternative aero table \n", - "aviary convert_aero_table -f GASP utils/test/data/aero_BWB_modified_GASP.txt generic_BWB_GASP_aero.csv\n", + "aviary convert aero_table -f GASP utils/test/data/aero_BWB_modified_GASP.txt generic_BWB_GASP_aero.csv\n", "# Converts a FLOPS based aero table\n", - "aviary convert_aero_table -f FLOPS utils/test/data/flops_test_polar.txt aviary_flops_polar.txt\n", + "aviary convert aero_table -f FLOPS utils/test/data/flops_test_polar.txt aviary_flops_polar.txt\n", "```" ] }, @@ -594,7 +469,7 @@ " '-f FLOPS utils/test/data/flops_test_polar.txt aviary_flops_polar.txt',\n", "]\n", "for command in commands:\n", - " run_command_no_file_error('aviary convert_aero_table ' + command)" + " run_command_no_file_error('aviary convert aero_table ' + command)" ] }, { @@ -602,9 +477,9 @@ "metadata": {}, "source": [ "(aviary-PMC-command)=\n", - "### aviary convert_prop_table\n", + "### aviary convert propeller_table\n", "\n", - "The {glue:md}`aviary convert_prop_table` command converts a legacy formatted (GASP) propeller map into an Aviary formatted propeller map. Currently, GASP is the only format supported." + "The {glue:md}`aviary convert propeller_table` command converts a legacy formatted (GASP) propeller map into an Aviary formatted propeller map. Currently, GASP is the only format supported." ] }, { @@ -612,7 +487,7 @@ "metadata": {}, "source": [ "```\n", - "aviary convert_prop_table -h\n", + "aviary convert propeller_table -h\n", "```" ] }, @@ -626,7 +501,7 @@ }, "outputs": [], "source": [ - "!aviary convert_prop_table -h 2>/dev/null" + "!aviary convert propeller_table -h 2>/dev/null" ] }, { @@ -645,11 +520,11 @@ "\n", "```\n", "# Convert GASP based propeller map PropFan.map to Aviary data table PropFan.csv\n", - "aviary convert_prop_table PropFan.map PropFan.csv\n", + "aviary convert propeller_table PropFan.map PropFan.csv\n", "# Convert GASP based propeller map general_aviation.map to Aviary data table general_aviation.csv\n", - "aviary convert_prop_table general_aviation.map general_aviation.csv\n", + "aviary convert propeller_table general_aviation.map general_aviation.csv\n", "# Convert GASP based propeller map general_aviation.map to Aviary data table general_aviation.csv\n", - "aviary convert_prop_table general_aviation.map\n", + "aviary convert propeller_table general_aviation.map\n", "```\n", "The first example uses Mach number and the second example uses helical Mach number. \n", "Note that the output file name was not specified in the third example." @@ -674,21 +549,7 @@ " 'models/engines/propellers/general_aviation.map',\n", "]\n", "for command in commands:\n", - " run_command_no_file_error('aviary convert_prop_table ' + command)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [], - "source": [ - "# glue all the options of 'aviary convert_prop_table'\n", - "glue_actions('convert_prop_table', current_glued_vars, md_code=True)" + " run_command_no_file_error('aviary convert propeller_table ' + command)" ] }, { @@ -824,7 +685,11 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "tags": [ + "remove-input" + ] + }, "outputs": [], "source": [ "!aviary rtplot -h 2>/dev/null" @@ -843,7 +708,7 @@ ], "metadata": { "kernelspec": { - "display_name": "aviary", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -857,7 +722,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.12" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb b/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb index ff480bcf04..a125193e75 100644 --- a/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb +++ b/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb @@ -207,7 +207,11 @@ "source": [ "## Build AviaryProblem for the same Model\n", "\n", - "We create a Python script to reproduce the {glue:md}`aircraft_for_bench_GwGm` model run that was used as an example in the level 1 document (this time we won’t use the level 1 functionality). The methods listed above are defined in level 3 (namely, [core/aviary_problem.py](https://github.com/OpenMDAO/Aviary/blob/main/aviary/core/aviary_problem.py)). You can run the code as follows:" + "We create a level 2 Python script to reproduce the {glue:md}`aircraft_for_bench_GwGm` model run that was used as an example in the level 1 document (this time we won’t use the level 1 functionality). The methods listed above are defined in level 3 (namely, [core/aviary_problem.py](https://github.com/OpenMDAO/Aviary/blob/main/aviary/core/aviary_problem.py)). You can run the code as follows:\n", + "\n", + "```{note}\n", + "For these examples we have set `max_iter = 0`, which means that the optimization will not run. This is done to reduce the computational time for the examples. If you want to run the optimization, you can set `max_iter = 100` or some similar value.\n", + "```" ] }, { diff --git a/aviary/docs/user_guide_unreviewed/reserve_missions.ipynb b/aviary/docs/user_guide_unreviewed/reserve_missions.ipynb index 2bb39ee917..79579039fb 100644 --- a/aviary/docs/user_guide_unreviewed/reserve_missions.ipynb +++ b/aviary/docs/user_guide_unreviewed/reserve_missions.ipynb @@ -202,7 +202,7 @@ "\n", "import aviary.api as av\n", "from aviary.models.missions.two_dof_default import phase_info\n", - "from aviary.interface.download_models import get_model\n", + "from aviary.utils.functions import get_model\n", "from aviary.core.aviary_problem import AviaryProblem\n", "\n", "prob = AviaryProblem()\n", @@ -243,7 +243,7 @@ "from copy import deepcopy\n", "\n", "from aviary.models.missions.two_dof_default import phase_info\n", - "from aviary.interface.download_models import get_model\n", + "from aviary.utils.functions import get_model\n", "from aviary.core.aviary_problem import AviaryProblem\n", "from aviary.utils.doctape import check_value\n", "\n", diff --git a/aviary/docs/user_guide_unreviewed/troubleshooting.ipynb b/aviary/docs/user_guide_unreviewed/troubleshooting.ipynb index c0128247a6..ac7f1fa0a1 100644 --- a/aviary/docs/user_guide_unreviewed/troubleshooting.ipynb +++ b/aviary/docs/user_guide_unreviewed/troubleshooting.ipynb @@ -62,7 +62,7 @@ "import argparse\n", "\n", "from aviary.api import Settings\n", - "from aviary.interface.cmd_entry_points import _command_map\n", + "from aviary.interface.cmd_entry_points import _command_map, _convert_command_map\n", "from aviary.utils.doctape import check_contains, check_value, glue_variable\n", "\n", "_command_map['run_mission']\n", @@ -70,7 +70,7 @@ "\n", "for command in ['fortran_to_aviary']:\n", " parser = argparse.ArgumentParser()\n", - " _command_map[command][0](parser)\n", + " _convert_command_map[command][0](parser)\n", " actions = [*parser._get_optional_actions(), *parser._get_positional_actions()]\n", " check_contains(\n", " 'verbosity',\n", @@ -117,7 +117,7 @@ ], "metadata": { "kernelspec": { - "display_name": "av1", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -131,9 +131,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.12.3" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/aviary/docs/user_guide_unreviewed/visualizing_results.ipynb b/aviary/docs/user_guide_unreviewed/visualizing_results.ipynb index 196baba936..7a28771582 100644 --- a/aviary/docs/user_guide_unreviewed/visualizing_results.ipynb +++ b/aviary/docs/user_guide_unreviewed/visualizing_results.ipynb @@ -376,11 +376,94 @@ "If you want to add additional outputs, call `add_timeseries_output` on the phases.\n", "Please refer to the [Dymos documentation on timeseries outputs](https://openmdao.org/dymos/docs/latest/features/phases/timeseries.html) for more information on how to do this." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Real-time Optimization Progress Plot\n", + "\n", + "The user can generate a real-time plot of the progress of an optimization. This capability makes use of this feature in OpenMDAO. There are two ways the user can do this. \n", + "\n", + "## Level 1 Real-time Optimization Progress Plot\n", + "\n", + "The real-time optimization plot can be generate using the `-rt` option to the `aviary run_mission` command. For example, \n", + "\n", + "```\n", + "aviary run_mission --rt models/aircraft/small_single_aisle/small_single_aisle_GASP.csv\n", + "```\n", + "\n", + "Here is the resulting plot. The user can control which variables are plotted using the buttons on the right. \n", + "\n", + "![small_single_aisle_GASP real-time optimization plot](images/level1_realtime_plot_example.png)\n", + "\n", + "For more information about the real-time optimization progress plot see the [OpenMDAO documentation](https://openmdao.org/newdocs/versions/latest/features/model_visualization/real_time_optimization_progress_plot.html#real-time-optimization-progress-plot-user-interface). \n", + "\n", + "## Level 2 and Level 3 Real-time Optimization Progress Plot\n", + "\n", + "Generating the real-time optimization progress plot can also be done programmatically using the `rt` argument of the method `run_aviary_problem`. Here is a Level 2 example of how to do that. A Level 3 script would work similarly. Note the last call to `run_aviary_problem`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip-execution" + ] + }, + "outputs": [], + "source": [ + "from copy import deepcopy\n", + "\n", + "import aviary.api as av\n", + "\n", + "# inputs that run_aviary() requires\n", + "aircraft_data = 'models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv'\n", + "optimizer = 'IPOPT'\n", + "objective_type = None\n", + "restart_filename = None\n", + "# max_iter = 0\n", + "max_iter = 100\n", + "phase_info = deepcopy(av.default_2DOF_phase_info)\n", + "\n", + "# Build problem\n", + "prob = av.AviaryProblem()\n", + "\n", + "# Load aircraft and options data from user\n", + "# Allow for user overrides here\n", + "prob.load_inputs(aircraft_data, phase_info)\n", + "\n", + "prob.check_and_preprocess_inputs()\n", + "# check the aircraft_data and phase_info for errors\n", + "\n", + "# adds a pre-mission group (propulsion, geometry, aerodynamics, and mass)\n", + "# adds a sequence of core mission phases.\n", + "# adds a landing phase\n", + "# Link phases and variables\n", + "prob.build_model()\n", + "\n", + "# adds an optimizer to the driver\n", + "prob.add_driver(optimizer, max_iter=max_iter)\n", + "\n", + "# adds relevant design variables\n", + "prob.add_design_variables()\n", + "\n", + "# Load optimization problem formulation\n", + "# Detail which variables the optimizer can control\n", + "prob.add_objective(objective_type=objective_type)\n", + "\n", + "# setup the problem and set initial guesses of states and controls variables\n", + "prob.setup()\n", + "\n", + "# run the problem we just set up and display the real-time optimization progress plot\n", + "prob.run_aviary_problem(restart_filename=restart_filename, rt=True)" + ] } ], "metadata": { "kernelspec": { - "display_name": "aviary", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -394,7 +477,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.9" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/aviary/interface/cmd_entry_points.py b/aviary/interface/cmd_entry_points.py index 66c9d27fae..7d0e912898 100644 --- a/aviary/interface/cmd_entry_points.py +++ b/aviary/interface/cmd_entry_points.py @@ -2,7 +2,6 @@ import os import sys -from aviary.interface.download_models import _exec_hangar, _setup_hangar_parser from aviary.interface.graphical_input import _exec_flight_profile, _setup_flight_profile_parser from aviary.interface.plot_drag_polar import _exec_plot_drag_polar, _setup_plot_drag_polar_parser from aviary.interface.run_aviary import _exec_run_aviary, _setup_run_aviary_parser @@ -43,16 +42,67 @@ def _load_and_exec(script_name, user_args): exec(code, globals_dict) # nosec: private, internal use only +# Conversion subcommands map (for 'aviary convert' sub-sub-commands) +_convert_command_map = { + 'aero_table': ( + _setup_ATC_parser, + _exec_ATC, + 'Convert FLOPS- or GASP-formatted aero data files into Aviary csv format.', + ), + 'engine_deck': ( + _setup_EDC_parser, + _exec_EDC, + 'Convert FLOPS- or GASP-formatted engine decks into Aviary csv format.', + ), + 'fortran_to_aviary': ( + _setup_F2A_parser, + _exec_F2A, + 'Convert legacy Fortran (FLOPS or GASP) input file to Aviary input file.', + ), + 'propeller_table': ( + _setup_PMC_parser, + _exec_PMC, + 'Convert GASP-formatted propeller map file into Aviary csv format.', + ), +} + + +def _setup_convert_parser(parser): + """Set up parser for the 'convert' subcommand with sub-sub-commands.""" + subparsers = parser.add_subparsers( + title='Conversion Types', metavar='', dest='convert_type', help='Type of data to convert' + ) + + for name, (parser_setup_func, executor, help_str) in sorted(_convert_command_map.items()): + subparser = subparsers.add_parser(name, help=help_str) + parser_setup_func(subparser) + subparser.set_defaults(executor=executor) + + +def _exec_convert(options, user_args): + """Execute the appropriate conversion command based on sub-sub-command.""" + if not hasattr(options, 'executor'): + # No sub-sub-command was specified, show help + print( + 'Error: Please specify a conversion type (aero_table, engine_deck, fortran_to_aviary, or propeller_table)' + ) + print("Use 'aviary convert -h' for more information.") + sys.exit(1) + + # Execute the appropriate conversion function + options.executor(options, user_args) + + _command_map = { 'check': ( _setup_installation_test, _exec_installation_test, 'Verify Aviary installation', ), - 'fortran_to_aviary': ( - _setup_F2A_parser, - _exec_F2A, - 'Convert legacy Fortran (FLOPS OR GASP) input file to Aviary input file.', + 'convert': ( + _setup_convert_parser, + _exec_convert, + 'Convert legacy formatted data files (aero_table, engine_deck, fortran_to_aviary, propeller_table) to Aviary format.', ), 'run_mission': ( _setup_run_aviary_parser, @@ -69,27 +119,6 @@ def _load_and_exec(script_name, user_args): _dashboard_cmd, 'Open the results dashboard for a provided Aviary run.', ), - 'hangar': ( - _setup_hangar_parser, - _exec_hangar, - 'Copy aircraft and engine models included with Aviary to specified folder. Allows users ' - 'who did not install Aviary locally to still access model files.', - ), - 'convert_engine': ( - _setup_EDC_parser, - _exec_EDC, - 'Convert FLOPS- or GASP-formatted engine decks into Aviary csv format.', - ), - 'convert_aero_table': ( - _setup_ATC_parser, - _exec_ATC, - 'Convert FLOPS- or GASP-formatted aero data files into Aviary csv format.', - ), - 'convert_prop_table': ( - _setup_PMC_parser, - _exec_PMC, - 'Convert GASP-formatted propeller map file into Aviary csv format.', - ), 'plot_drag_polar': ( _setup_plot_drag_polar_parser, _exec_plot_drag_polar, @@ -142,7 +171,8 @@ def aviary_cmd(): if len(args) == 1 and len(user_args) == 0: # if command requires arguments but is run without any, return help for that command - if args[0] not in ('check', 'draw_mission', 'run_mission', 'plot_drag_polar'): + # 'convert' is a special case as it requires a sub-sub-command + if args[0] not in ('check', 'draw_mission', 'run_mission', 'plot_drag_polar', 'convert'): parser.parse_args([args[0], '-h']) if not set(args).intersection(subs.choices) and len(args) == 1 and os.path.isfile(cmdargs[0]): diff --git a/aviary/interface/download_models.py b/aviary/interface/download_models.py deleted file mode 100644 index da9aa38e01..0000000000 --- a/aviary/interface/download_models.py +++ /dev/null @@ -1,58 +0,0 @@ -import argparse -import os -import shutil -from pathlib import Path - -from aviary.utils.functions import get_model - - -def save_file(aviary_path: Path, outdir: Path, verbose=False) -> Path: - """Saves the file or folder specified into the output directory, creating directories as needed.""" - outdir.mkdir(parents=True, exist_ok=True) - if aviary_path.is_dir(): - if verbose: - print(aviary_path, 'is a directory, getting all files') - outdir = outdir.joinpath(aviary_path.stem) - outdir.mkdir(exist_ok=True) - for file in next(os.walk(aviary_path))[-1]: - if verbose: - print('copying', str(aviary_path / file), 'to', str(outdir / file)) - shutil.copy2(aviary_path / file, outdir) - else: - if verbose: - print('copying', str(aviary_path), 'to', str(outdir / aviary_path.name)) - shutil.copy2(aviary_path, outdir) - return outdir - - -def _setup_hangar_parser(parser: argparse.ArgumentParser): - def_outdir = os.path.join(os.getcwd(), 'aviary_models') - parser.add_argument( - 'input_decks', - metavar='indecks', - type=str, - nargs='+', - help='Name of file or folder to download from Aviary/models', - ) - parser.add_argument( - '-o', - '--outdir', - default=def_outdir, - help='Directory to write outputs. Defaults to aviary_models in the current directory.', - ) - parser.add_argument( - '-v', - '--verbose', - action='store_true', - help='Enable verbose outputs', - ) - - -def _exec_hangar(args, user_args): - input_decks = [] - - for input_deck in args.input_decks: - input_decks.append(get_model(input_deck)) - - for input_deck in input_decks: - save_file(input_deck, Path(args.outdir), args.verbose) diff --git a/aviary/interface/run_aviary.py b/aviary/interface/run_aviary.py index 9047641a9a..c178eb7df7 100644 --- a/aviary/interface/run_aviary.py +++ b/aviary/interface/run_aviary.py @@ -1,7 +1,10 @@ """This file contains functions needed to run Aviary using the Level 1 interface.""" +from importlib.util import spec_from_file_location, module_from_spec +import os +from pathlib import Path +import subprocess import sys -from importlib.util import module_from_spec, spec_from_file_location from pathlib import Path from aviary.utils.functions import get_path @@ -20,6 +23,7 @@ def run_aviary( make_plots=True, phase_info_parameterization=None, verbosity=None, + rt=False, ): """ Run the Aviary optimization problem for a specified aircraft configuration and mission. @@ -112,19 +116,25 @@ def run_aviary( run_driver=run_driver, make_plots=make_plots, verbosity=verbosity, + rt=rt, ) return prob def run_aviary_cmd( - input_deck, optimizer='IPOPT', phase_info=None, max_iter=50, verbosity=Verbosity.BRIEF + input_deck, optimizer='IPOPT', phase_info=None, max_iter=50, verbosity=Verbosity.BRIEF, rt=False ): """ This file enables running aviary from the command line with a user specified input deck. usage: aviary run_mission [input_deck] [opt_args]. """ - kwargs = {'max_iter': max_iter, 'optimizer': optimizer, 'verbosity': Verbosity(verbosity)} + kwargs = { + 'max_iter': max_iter, + 'optimizer': optimizer, + 'verbosity': Verbosity(verbosity), + 'rt': rt, + } if isinstance(phase_info, str): phase_info_path = get_path(phase_info) @@ -167,6 +177,11 @@ def _setup_run_aviary_parser(parser): help='verbosity settings: 0=quiet, 1=brief, 2=verbose, 3=debug', choices=(0, 1, 2, 3), ) + parser.add_argument( + '--rt', + action='store_true', + help='Enable realtime plotting option', + ) def _exec_run_aviary(args, user_args): @@ -183,4 +198,5 @@ def _exec_run_aviary(args, user_args): phase_info=args.phase_info, max_iter=args.max_iter, verbosity=args.verbosity, + rt=args.rt, ) diff --git a/aviary/interface/test/test_cmd_entry_points.py b/aviary/interface/test/test_cmd_entry_points.py index 7454bdf20e..5d287414fe 100644 --- a/aviary/interface/test/test_cmd_entry_points.py +++ b/aviary/interface/test/test_cmd_entry_points.py @@ -21,7 +21,7 @@ def run_and_test_cmd(self, cmd): def get_file(self, filename): filepath = get_aviary_resource_path(filename) if not Path(filepath).exists(): - self.skipTest(f"couldn't find {filepath}") + raise self.fail(f"couldn't find {filepath}") return filepath @@ -55,7 +55,7 @@ class fortran_to_aviaryTestCases(CommandEntryPointsTestCases): def test_diff_configuration_conversion(self): filepath = get_aviary_resource_path('utils/test/data/configuration_test_data_GASP.dat') outfile = Path.cwd() / 'utils/test/data/configuration_test_data_GASP' / 'output.dat' - cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l GASP' + cmd = f'aviary convert fortran_to_aviary {filepath} {outfile} -f GASP' self.run_and_test_cmd(cmd) def test_small_single_aisle_conversion(self): @@ -63,7 +63,7 @@ def test_small_single_aisle_conversion(self): 'models/aircraft/small_single_aisle/small_single_aisle_GASP.dat' ) outfile = Path.cwd() / 'small_single_aisle' / 'output.dat' - cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l GASP' + cmd = f'aviary convert fortran_to_aviary {filepath} {outfile} -f GASP' self.run_and_test_cmd(cmd) def test_FLOPS_conversion(self): @@ -71,7 +71,7 @@ def test_FLOPS_conversion(self): 'models/aircraft/advanced_single_aisle/N3CC_generic_low_speed_polars_FLOPS.txt' ) outfile = Path.cwd() / 'advanced_single_aisle' / 'output.dat' - cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l FLOPS' + cmd = f'aviary convert fortran_to_aviary {filepath} {outfile} -f FLOPS' self.run_and_test_cmd(cmd) def test_force_conversion(self): @@ -79,71 +79,40 @@ def test_force_conversion(self): 'models/aircraft/small_single_aisle/small_single_aisle_GASP.dat' ) outfile = Path.cwd() / 'output.dat' - cmd1 = f'aviary fortran_to_aviary {filepath} -o {outfile} -l GASP' + cmd1 = f'aviary convert fortran_to_aviary {filepath} {outfile} -f GASP' self.run_and_test_cmd(cmd1) filepath = get_aviary_resource_path( 'models/aircraft/small_single_aisle/small_single_aisle_GASP.dat' ) - cmd2 = f'aviary fortran_to_aviary {filepath} -o {outfile} --force -l GASP' + cmd2 = f'aviary convert fortran_to_aviary {filepath} {outfile} --force -f GASP' self.run_and_test_cmd(cmd2) -class hangarTestCases(CommandEntryPointsTestCases): - def test_copy_folder(self): - cmd = 'aviary hangar engines' - self.run_and_test_cmd(cmd) - - def test_copy_deck(self): - cmd = 'aviary hangar turbofan_22k.csv' - self.run_and_test_cmd(cmd) - - def test_copy_n3cc_data(self): - cmd = 'aviary hangar advanced_single_aisle/advanced_single_aisle_data.py' - self.run_and_test_cmd(cmd) - - def test_copy_multiple(self): - cmd = 'aviary hangar small_single_aisle_GASP.dat small_single_aisle_GASP.csv' - self.run_and_test_cmd(cmd) - - def test_copy_to(self): - outfile = Path.cwd() / 'example_files' - cmd = f'aviary hangar small_single_aisle_GASP.dat -o {outfile}' - self.run_and_test_cmd(cmd) - - class convert_engineTestCases(CommandEntryPointsTestCases): def test_GASP_conversion(self): - filepath = self.get_file('utils/test/data/GASP_turbofan_23k_1.eng') + filepath = self.get_file('utils/test/data/turbofan_23k_1.eng') outfile = Path.cwd() / 'turbofan_23k_1.csv' - cmd = f'aviary convert_engine {filepath} {outfile} -f GASP' + cmd = f'aviary convert engine_deck {filepath} {outfile} -f GASP' self.run_and_test_cmd(cmd) def test_FLOPS_conversion(self): - filepath = self.get_file('utils/test/data/FLOPS_turbofan_22k.txt') + filepath = self.get_file('utils/test/data/turbofan_22k.txt') outfile = Path.cwd() / 'turbofan_22k.csv' - cmd = f'aviary convert_engine {filepath} {outfile} -f FLOPS' + cmd = f'aviary convert engine_deck {filepath} {outfile} -f FLOPS' self.run_and_test_cmd(cmd) def test_GASP_TS_conversion(self): - filepath = self.get_file('models/engines/turboshaft_4465hp.eng') + filepath = self.get_file('utils/test/data/turboshaft_4465hp.eng') outfile = Path.cwd() / 'turboshaft_4465hp.eng' - cmd = f'aviary convert_engine {filepath} {outfile} --data_format GASP_TS' + cmd = f'aviary convert engine_deck {filepath} {outfile} --format GASP_TS' self.run_and_test_cmd(cmd) class convert_aero_tableTestCases(CommandEntryPointsTestCases): def test_GASP_conversion(self): - filepath = self.get_file('subsystems/aerodynamics/gasp_based/data/GASP_aero_flaps.txt') + filepath = self.get_file('utils/test/data/aero_flaps_GASP.txt') outfile = Path.cwd() / 'output.dat' - cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l GASP' - self.run_and_test_cmd(cmd) - - def test_FLOPS_conversion(self): - filepath = self.get_file( - 'models/aircraft/advanced_single_aisle/N3CC_generic_low_speed_polars_FLOPSinp.txt' - ) - outfile = Path.cwd() / 'advanced_single_aisle' / 'output.dat' - cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l FLOPS' + cmd = f'aviary convert aero_table {filepath} {outfile} -f GASP_ALT' self.run_and_test_cmd(cmd) @@ -151,9 +120,9 @@ class convert_propeller_tableTestCases(CommandEntryPointsTestCases): """aviary convert_prop_table test. The only option is from GASP propeller map to Aviary table.""" def test_GASP_conversion(self): - filepath = self.get_file('models/propellers/PropFan.csv') + filepath = self.get_file('models/engines/propellers/PropFan.map') outfile = Path.cwd() / 'output.dat' - cmd = f'aviary convert_prop_table {filepath} {outfile} -f GASP' + cmd = f'aviary convert propeller_table {filepath} {outfile}' self.run_and_test_cmd(cmd) diff --git a/aviary/interface/test/test_download_models.py b/aviary/interface/test/test_download_models.py deleted file mode 100644 index 91f3ae359f..0000000000 --- a/aviary/interface/test/test_download_models.py +++ /dev/null @@ -1,55 +0,0 @@ -import shutil -import unittest -from pathlib import Path - -from openmdao.utils.testing_utils import use_tempdirs - -from aviary.interface.download_models import save_file -from aviary.utils.functions import get_aviary_resource_path, get_model - - -@use_tempdirs -class TestDownloadModels(unittest.TestCase): - def run_and_test_hangar(self, filename, out_dir=''): - # tests that the function runs successfully and that the files are generated - if out_dir: - out_dir = Path(out_dir) - else: - out_dir = Path.cwd() / 'aviary_models' - - path = get_model(filename) - save_file(path, outdir=out_dir) - path = out_dir / filename.split('/')[-1] - self.assertTrue(path.exists()) - - def test_single_file_without_path(self): - filename = 'turbofan_22k.csv' - self.run_and_test_hangar(filename) - - def test_single_file_with_path(self): - filename = 'engines/turbofan_22k.csv' - self.run_and_test_hangar(filename) - - def test_folder(self): - filename = 'engines' - self.run_and_test_hangar(filename) - - def test_single_file_custom_outdir(self): - filename = 'small_single_aisle_GASP.csv' - out_dir = 'test_hangar' - self.run_and_test_hangar(filename, out_dir) - shutil.rmtree(out_dir) - - def test_expected_path(self): - aviary_path = get_model('large_single_aisle_1_GASP.dat') - - expected_path = get_aviary_resource_path( - 'models/aircraft/large_single_aisle_1/large_single_aisle_1_GASP.dat' - ) - self.assertTrue(str(aviary_path) == str(expected_path)) - - -if __name__ == '__main__': - unittest.main() - # test = CommandEntryPointsTestCases() - # test.test_single_file_with_path() diff --git a/aviary/utils/aero_table_conversion.py b/aviary/utils/aero_table_conversion.py index 65432d0d3b..89bd530c3a 100644 --- a/aviary/utils/aero_table_conversion.py +++ b/aviary/utils/aero_table_conversion.py @@ -57,7 +57,7 @@ def convert_aero_table(input_file=None, output_file=None, data_format=None): """This is a utility class to convert a legacy aero data file to Aviary format. There are two options for the legacy aero data file format: FLOPS and GASP. As an Aviary command, the usage is: - aviary convert_aero_table -F {FLOPS|GASP|GASP_ALT} input_file output_file. + aviary convert aero_table -F {FLOPS|GASP|GASP_ALT} input_file output_file. Note: In case of GASP, reading of a possible cd0 table is not implemented yet. """ data_format = CodeOrigin(data_format) diff --git a/aviary/utils/aero_table_conversion_cmd.py b/aviary/utils/aero_table_conversion_cmd.py index 86ff0090ff..7b4608705a 100644 --- a/aviary/utils/aero_table_conversion_cmd.py +++ b/aviary/utils/aero_table_conversion_cmd.py @@ -16,7 +16,7 @@ def _setup_ATC_parser(parser): ) parser.add_argument( '-f', - '--data_format', + '--format', type=str, choices=[origin.value for origin in CodeOrigin], help='data format used by input_file', @@ -27,5 +27,5 @@ def _exec_ATC(args, user_args): from aviary.utils.aero_table_conversion import convert_aero_table convert_aero_table( - input_file=args.input_file, output_file=args.output_file, data_format=args.data_format + input_file=args.input_file, output_file=args.output_file, data_format=args.format ) diff --git a/aviary/utils/engine_deck_conversion_cmd.py b/aviary/utils/engine_deck_conversion_cmd.py index 89521262d8..129b78a412 100644 --- a/aviary/utils/engine_deck_conversion_cmd.py +++ b/aviary/utils/engine_deck_conversion_cmd.py @@ -16,7 +16,7 @@ def _setup_EDC_parser(parser): ) parser.add_argument( '-f', - '--data_format', + '--format', type=EngineDeckType, choices=list(EngineDeckType), help='data format used by input_file', @@ -30,6 +30,6 @@ def _exec_EDC(args, user_args): convert_engine_deck( input_file=args.input_file, output_file=args.output_file, - data_format=args.data_format, + data_format=args.format, round_data=args.round, ) diff --git a/aviary/utils/fortran_to_aviary.py b/aviary/utils/fortran_to_aviary.py index 9803ffd784..76b7510195 100644 --- a/aviary/utils/fortran_to_aviary.py +++ b/aviary/utils/fortran_to_aviary.py @@ -41,7 +41,7 @@ def fortran_to_aviary( fortran_deck: str, legacy_code=None, - out_file=None, + output_file=None, force=False, verbosity=Verbosity.BRIEF, ): @@ -75,11 +75,11 @@ def fortran_to_aviary( f'# {legacy_code.value}-derived aircraft input deck converted from {fortran_deck.name}' ) - if out_file: - out_file = Path(out_file) + if output_file: + output_file = Path(output_file) else: name = fortran_deck.stem - out_file: Path = fortran_deck.parent.resolve().joinpath(name + '_converted.csv') + output_file: Path = fortran_deck.parent.resolve().joinpath(name + '_converted.csv') # create dictionary to convert legacy code variables to Aviary variables # key: variable name, value: either None or relevant historical_name @@ -139,26 +139,26 @@ def fortran_to_aviary( vehicle_data['input_values'].set_val(Settings.MASS_METHOD, mass) vehicle_data['input_values'].set_val(Settings.AERODYNAMICS_METHOD, aero) - if not out_file.is_file(): + if not output_file.is_file(): # default outputted file to be in same directory as input - out_file = fortran_deck.parent / out_file + output_file = fortran_deck.parent / output_file - if out_file.is_file(): + if output_file.is_file(): if not force: - raise RuntimeError(f'{out_file} already exists. Choose a new name or enable --force') + raise RuntimeError(f'{output_file} already exists. Choose a new name or enable --force') elif verbosity >= Verbosity.BRIEF: - print(f'Overwriting existing file: {out_file.name}') + print(f'Overwriting existing file: {output_file.name}') else: # create any directories defined by the new filename if they don't already exist - out_file.parent.mkdir(parents=True, exist_ok=True) + output_file.parent.mkdir(parents=True, exist_ok=True) if verbosity >= Verbosity.VERBOSE: - print('Writing to:', out_file) + print('Writing to:', output_file) # TODO Use the existing utilities to write this input file? It will be much more # human-readable # open the file in write mode - with open(out_file, 'w', newline='') as f: + with open(output_file, 'w', newline='') as f: writer = csv.writer(f) # Write header info and comments for comment in comments: @@ -1323,14 +1323,14 @@ def _setup_F2A_parser(parser): help='Filename of vehicle input deck, including partial or complete path.', ) parser.add_argument( - '-o', - '--out_file', - default=None, + 'output_file', + type=str, + nargs='?', help='Filename for converted input deck, including partial or complete path.', ) parser.add_argument( - '-l', - '--legacy_code', + '-f', + '--format', type=LegacyCode, help='Name of the legacy code the deck originated from', choices=set(LegacyCode), @@ -1360,4 +1360,4 @@ def _exec_F2A(args, user_args): # convert verbosity from int to enum verbosity = Verbosity(args.verbosity) - fortran_to_aviary(filepath, args.legacy_code, args.out_file, args.force, verbosity) + fortran_to_aviary(filepath, args.format, args.output_file, args.force, verbosity) diff --git a/aviary/utils/propeller_map_conversion.py b/aviary/utils/propeller_map_conversion.py index 0a23ebff7b..527f926073 100644 --- a/aviary/utils/propeller_map_conversion.py +++ b/aviary/utils/propeller_map_conversion.py @@ -39,7 +39,7 @@ def convert_propeller_map( This is a utility class to convert a propeller map file to Aviary format. Currently, there is only one option: from GASP format to Aviary format. As an Aviary command, the usage is: - aviary convert_prop_table -f GASP input_file output_file. + aviary convert propeller_table -f GASP input_file output_file. """ timestamp = datetime.now().strftime('%m/%d/%y at %H:%M') user = getpass.getuser() @@ -175,7 +175,7 @@ def _exec_PMC(args, user_args): convert_propeller_map( input_file=args.input_file, output_file=args.output_file, - # data_format=args.data_format, + # data_format=args.format, round_data=args.round, ) diff --git a/aviary/utils/test/test_aero_table_conversion.py b/aviary/utils/test/test_aero_table_conversion.py index becdcda751..6cae209726 100644 --- a/aviary/utils/test/test_aero_table_conversion.py +++ b/aviary/utils/test/test_aero_table_conversion.py @@ -165,7 +165,7 @@ def test_GASP_table_5(self): # args.input_file = 'subsystems/aerodynamics/gasp_based/data/GASP_aero_flaps.txt' # args.output_file = str(Path.cwd() / Path('TEST_' + Path(args.input_file).name)) - # args.data_format = 'GASP' + # args.format = 'GASP' # _exec_ATC(args, None) # validation_data = get_path( @@ -200,7 +200,7 @@ def test_GASP_table_5(self): # args.input_file = 'utils/test/data/flops_test_polar.txt' # args.output_file = str(Path(tempdir, 'TEST_' + Path(args.input_file).name)) - # args.data_format = 'FLOPS' + # args.format = 'FLOPS' # _exec_ATC(args, None) # # Only testing that this runs without an error, not comparing the resulting data diff --git a/aviary/utils/test/test_fortran_to_aviary.py b/aviary/utils/test/test_fortran_to_aviary.py index f5bf0d0029..5b03165df2 100644 --- a/aviary/utils/test/test_fortran_to_aviary.py +++ b/aviary/utils/test/test_fortran_to_aviary.py @@ -12,17 +12,17 @@ class TestFortranToAviary(unittest.TestCase): """Test fortran_to_aviary legacy code input file conversion utility by comparing against already converted input files.""" - def prepare_and_run(self, filepath, out_file=None, legacy_code=LegacyCode.GASP): + def prepare_and_run(self, filepath, output_file=None, legacy_code=LegacyCode.GASP): # Specify the output file filename = filepath.split('.')[0] + '.csv' - if not out_file: - out_file = Path.cwd() / Path('TEST_' + filename) + if not output_file: + output_file = Path.cwd() / Path('TEST_' + filename) else: - out_file = Path(out_file) + output_file = Path(output_file) legacy_code = legacy_code # Execute the conversion - fortran_to_aviary(filepath, legacy_code, out_file, force=True, verbosity=0) + fortran_to_aviary(filepath, legacy_code, output_file, force=True, verbosity=0) def compare_files(self, filepath, skip_list=['# created ']): """ @@ -63,7 +63,7 @@ def test_large_single_aisle(self): self.prepare_and_run( filepath, - out_file=Path.cwd() / Path('TEST_' + comparison_filepath), + output_file=Path.cwd() / Path('TEST_' + comparison_filepath), ) self.compare_files(comparison_filepath) @@ -73,7 +73,7 @@ def test_small_single_aisle(self): self.prepare_and_run( filepath, - out_file=Path.cwd() / Path('TEST_' + comparison_filepath), + output_file=Path.cwd() / Path('TEST_' + comparison_filepath), ) self.compare_files(comparison_filepath) @@ -81,7 +81,7 @@ def test_diff_configuration(self): filepath = 'utils/test/data/configuration_test_data_GASP.dat' comparison_filepath = 'utils/test/data/converter_test_configuration_GASP.csv' - self.prepare_and_run(filepath, out_file=Path.cwd() / Path('TEST_' + comparison_filepath)) + self.prepare_and_run(filepath, output_file=Path.cwd() / Path('TEST_' + comparison_filepath)) self.compare_files(comparison_filepath) def test_bwb_gasp(self): @@ -90,7 +90,7 @@ def test_bwb_gasp(self): self.prepare_and_run( filepath, - out_file=Path.cwd() / Path('TEST_' + comparison_filepath), + output_file=Path.cwd() / Path('TEST_' + comparison_filepath), ) self.compare_files(comparison_filepath) @@ -100,7 +100,7 @@ def test_bwb_detailed_flops(self): self.prepare_and_run( filepath, - out_file=Path.cwd() / Path('TEST_' + comparison_filepath), + output_file=Path.cwd() / Path('TEST_' + comparison_filepath), legacy_code=LegacyCode.FLOPS, ) self.compare_files(comparison_filepath) @@ -111,7 +111,7 @@ def test_bwb_simple_flops(self): self.prepare_and_run( filepath, - out_file=Path.cwd() / Path('TEST_' + comparison_filepath), + output_file=Path.cwd() / Path('TEST_' + comparison_filepath), legacy_code=LegacyCode.FLOPS, ) self.compare_files(comparison_filepath) @@ -125,7 +125,7 @@ def test_advanced_single_aisle(self): comparison_filepath = 'utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv' self.prepare_and_run( filepath, - out_file=Path.cwd() / Path('TEST_' + comparison_filepath), + output_file=Path.cwd() / Path('TEST_' + comparison_filepath), legacy_code=LegacyCode.FLOPS, ) self.compare_files(comparison_filepath)