Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI refactoring #405

Merged
merged 17 commits into from
Dec 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ certifi==2021.10.8; python_version >= "3.6" and python_full_version < "3.0.0" or
cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.6"
cfgv==3.3.1; python_full_version >= "3.6.1"
charset-normalizer==2.0.7; python_full_version >= "3.6.0" and python_version >= "3.6"
click==8.0.3; python_version >= "3.6" and python_full_version >= "3.6.2"
colorama==0.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" and python_version < "4.0" and platform_system == "Windows" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.5.0" and python_version < "4.0" and platform_system == "Windows" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
click==8.0.3; python_version >= "3.6"
colorama==0.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" and sys_platform == "win32" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") or platform_system == "Windows" and python_version >= "3.7" and python_full_version >= "3.5.0" and sys_platform == "win32" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
coverage==5.5; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0" and python_version < "4")
cycler==0.10.0; python_version >= "3.7"
debugpy==1.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
Expand Down
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ jupyter-client = "!=7.0.0, !=7.0.1, !=7.0.2, !=7.0.3, !=7.0.4, !=7.0.5" # v7.0.
notebook = "^6.0"
stdatm = "^0.1.0"
Deprecated = "^1.2.13"
click = "^8.0.3"

[tool.poetry.dev-dependencies]
pytest = "^6.2"
Expand All @@ -110,8 +111,8 @@ flake8 = "^4.0.1"
matplotlib = "^3.1.2"

[tool.poetry.scripts]
fastoad = "fastoad.cmd.fast:main"
fast-oad = "fastoad.cmd.fast:main"
fastoad = "fastoad.cmd.cli:fast_oad"
fast-oad = "fastoad.cmd.cli:fast_oad"

[tool.poetry.plugins."fastoad_model"]
"internal_models" = "fastoad.models"
Expand Down
78 changes: 52 additions & 26 deletions src/fastoad/cmd/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
import os.path as pth
import sys
import textwrap as tw
from collections import Iterable
from time import time
from typing import IO, Union, List
from typing import IO, List, Union

import openmdao.api as om
from IPython import InteractiveShell
Expand Down Expand Up @@ -57,8 +58,10 @@ def generate_configuration_file(configuration_file_path: str, overwrite: bool =

:param configuration_file_path: the path of file to be written
:param overwrite: if True, the file will be written, even if it already exists
:return: path of generated file
:raise FastFileExistsError: if overwrite==False and configuration_file_path already exists
"""
configuration_file_path = pth.abspath(configuration_file_path)
if not overwrite and pth.exists(configuration_file_path):
raise FastFileExistsError(
"Configuration file %s not written because it already exists. "
Expand All @@ -70,6 +73,7 @@ def generate_configuration_file(configuration_file_path: str, overwrite: bool =

copy_resource(resources, SAMPLE_FILENAME, configuration_file_path)
_LOGGER.info("Sample configuration written in %s", configuration_file_path)
return configuration_file_path


def generate_inputs(
Expand Down Expand Up @@ -133,8 +137,8 @@ def list_variables(
:param tablefmt: The formatting of the requested table. Options are the same as those available
to the tabulate package. See tabulate.tabulate_formats for a complete list.
If "var_desc" the file will use the variable_descriptions.txt format.
:raise FastFileExistsError: if overwrite==False and out parameter is a file path and the file
exists
:return: path of generated file, or None if no file was generated.
:raise FastFileExistsError: if `overwrite==False` and `out` is a file path and the file exists
"""
if out is None:
out = sys.stdout
Expand Down Expand Up @@ -162,6 +166,7 @@ def list_variables(
)

if isinstance(out, str):
out = pth.abspath(out)
if not overwrite and pth.exists(out):
raise FastFileExistsError(
"File %s not written because it already exists. "
Expand All @@ -173,7 +178,7 @@ def list_variables(
else:
if out == sys.stdout and InteractiveShell.initialized() and not force_text_output:
display(HTML(variables_df.to_html(index=False)))
return
return None

# Here we continue with text output
out_file = out
Expand All @@ -187,7 +192,10 @@ def list_variables(

if isinstance(out, str):
out_file.close()
_LOGGER.info("Output list written in %s", out_file)
_LOGGER.info("Output list written in %s", out)
return out

return None


def _generate_var_desc_format(variables_df):
Expand Down Expand Up @@ -220,20 +228,21 @@ def list_modules(
force_text_output: bool = False,
):
"""
Writes list of available systems.
If source_path is given and if it defines paths where there are registered systems,
they will be listed too.

:param source_path: either a configuration file path, folder path, or list of folder path
:param out: the output stream or a path for the output file (None means sys.stdout)
:param overwrite: if True and out is a file path, the file will be written even if one already
exists
:param verbose: if True, shows detailed information for each system
if False, shows only identifier and path of each system
:param force_text_output: if True, list will be written as text, even if command is used in an
interactive IPython shell (Jupyter notebook). Has no effect in other
shells or if out parameter is not sys.stdout
:raise FastFileExistsError: if overwrite==False and out is a file path and the file exists
Writes list of available systems.
If source_path is given and if it defines paths where there are registered systems,
they will be listed too.

:param source_path: either a configuration file path, folder path, or list of folder path
:param out: the output stream or a path for the output file (None means sys.stdout)
:param overwrite: if True and out is a file path, the file will be written even if one already
exists
:param verbose: if True, shows detailed information for each system
if False, shows only identifier and path of each system
:param force_text_output: if True, list will be written as text, even if command is used in an
interactive IPython shell (Jupyter notebook). Has no effect in other
shells or if out parameter is not sys.stdout
:return: path of generated file, or None if no file was generated.
:raise FastFileExistsError: if `overwrite==False` and `out` is a file path and the file exists
"""
if out is None:
out = sys.stdout
Expand All @@ -248,7 +257,7 @@ def list_modules(
RegisterOpenMDAOSystem.explore_folder(source_path)
else:
raise FileNotFoundError("Could not find %s" % source_path)
elif isinstance(source_path, list):
elif isinstance(source_path, Iterable):
for folder_path in source_path:
if not pth.isdir(folder_path):
_LOGGER.warning("SKIPPED %s: folder does not exist.", folder_path)
Expand All @@ -263,6 +272,7 @@ def list_modules(
cell_list = _get_simple_system_list()

if isinstance(out, str):
out = pth.abspath(out)
if not overwrite and pth.exists(out):
raise FastFileExistsError(
"File %s not written because it already exists. "
Expand All @@ -280,7 +290,7 @@ def list_modules(
and not verbose
):
display(HTML(tabulate(cell_list, tablefmt="html")))
return
return None

out_file = out

Expand All @@ -289,7 +299,10 @@ def list_modules(

if isinstance(out, str):
out_file.close()
_LOGGER.info("System list written in %s", out_file)
_LOGGER.info("System list written in %s", out)
return out

return None


def _get_simple_system_list():
Expand Down Expand Up @@ -346,15 +359,21 @@ def _get_detailed_system_list():
return cell_list


def write_n2(configuration_file_path: str, n2_file_path: str = "n2.html", overwrite: bool = False):
def write_n2(configuration_file_path: str, n2_file_path: str = None, overwrite: bool = False):
"""
Write the N2 diagram of the problem in file n2.html

:param configuration_file_path:
:param n2_file_path:
:param n2_file_path: if None, will default to `n2.html`
:param overwrite:
:return: path of generated file.
:raise FastFileExistsError: if overwrite==False and n2_file_path already exists
"""

if not n2_file_path:
n2_file_path = "n2.html"
n2_file_path = pth.abspath(n2_file_path)

if not overwrite and pth.exists(n2_file_path):
raise FastFileExistsError(
"N2-diagram file %s not written because it already exists. "
Expand All @@ -370,8 +389,10 @@ def write_n2(configuration_file_path: str, n2_file_path: str = "n2.html", overwr
problem.final_setup()

om.n2(problem, outfile=n2_file_path, show_browser=False)
clear_output()
if InteractiveShell.initialized():
clear_output()
_LOGGER.info("N2 diagram written in %s", pth.abspath(n2_file_path))
return n2_file_path


def write_xdsm(
Expand All @@ -391,7 +412,8 @@ def write_xdsm(
:param wop_server_url: URL of WhatsOpt server (if None, ether.onera.fr/whatsopt will be used)
:param dry_run: if True, will run wop without sending any request to the server. Generated
XDSM will be empty. (for test purpose only)
:return:
:return: path of generated file.
:raise FastFileExistsError: if overwrite==False and xdsm_file_path already exists
"""
if not xdsm_file_path:
xdsm_file_path = pth.join(pth.dirname(configuration_file_path), "xdsm.html")
Expand All @@ -413,6 +435,7 @@ def write_xdsm(
problem.final_setup()

fastoad.openmdao.whatsopt.write_xdsm(problem, xdsm_file_path, depth, wop_server_url, dry_run)
return xdsm_file_path


def _run_problem(
Expand All @@ -430,6 +453,7 @@ def _run_problem(
:param auto_scaling: if True, automatic scaling is performed for design variables and
constraints
:return: the OpenMDAO problem after run
:raise FastFileExistsError: if overwrite==False and output data file of problem already exists
"""

conf = FASTOADProblemConfigurator(configuration_file_path)
Expand Down Expand Up @@ -472,6 +496,7 @@ def evaluate_problem(configuration_file_path: str, overwrite: bool = False) -> F
:param configuration_file_path: problem definition
:param overwrite: if True, output file will be overwritten
:return: the OpenMDAO problem after run
:raise FastFileExistsError: if overwrite==False and output data file of problem already exists
"""
return _run_problem(configuration_file_path, overwrite, "run_model")

Expand All @@ -487,6 +512,7 @@ def optimize_problem(
:param auto_scaling: if True, automatic scaling is performed for design variables and
constraints
:return: the OpenMDAO problem after run
:raise FastFileExistsError: if overwrite==False and output data file of problem already exists
"""
return _run_problem(configuration_file_path, overwrite, "run_driver", auto_scaling=auto_scaling)

Expand Down
Loading