コード例 #1
0
ファイル: api.py プロジェクト: christophe-david/FAST-OAD
def generate_inputs(
    configuration_file_path: str,
    source_path: str = None,
    source_path_schema="native",
    overwrite: bool = False,
) -> str:
    """
    Generates input file for the :class:`FASTOADProblem` specified in configuration_file_path.

    :param configuration_file_path: where the path of input file to write is set
    :param source_path: path of file data will be taken from
    :param source_path_schema: set to 'legacy' if the source file come from legacy FAST
    :param overwrite: if True, file will be written even if one already exists
    :return: path of generated file
    :raise FastFileExistsError: if overwrite==False and configuration_file_path already exists
    """
    conf = FASTOADProblemConfigurator(configuration_file_path)
    conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)

    input_file_path = conf.input_file_path
    if not overwrite and pth.exists(conf.input_file_path):
        raise FastFileExistsError(
            "Input file %s not written because it already exists. "
            "Use overwrite=True to bypass." % input_file_path,
            input_file_path,
        )

    if source_path_schema == "legacy":
        conf.write_needed_inputs(source_path, VariableLegacy1XmlFormatter())
    else:
        conf.write_needed_inputs(source_path)

    _LOGGER.info("Problem inputs written in %s", input_file_path)
    return input_file_path
コード例 #2
0
ファイル: api.py プロジェクト: areysset/FAST-OAD
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 overwrite:
    """

    if not n2_file_path:
        n2_file_path = pth.join(pth.dirname(configuration_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. "
            "Use overwrite=True to bypass." % n2_file_path,
            n2_file_path,
        )

    make_parent_dir(n2_file_path)
    problem = FASTOADProblemConfigurator(configuration_file_path).get_problem()
    problem.setup()
    problem.final_setup()

    om.n2(problem, outfile=n2_file_path, show_browser=False)
    _LOGGER.info("N2 diagram written in %s", n2_file_path)
コード例 #3
0
ファイル: api.py プロジェクト: christophe-david/FAST-OAD
def write_n2(configuration_file_path: str, n2_file_path: str = "n2.html", overwrite: bool = False):
    """
    Write the N2 diagram of the problem in file n2.html

    :param configuration_file_path:
    :param n2_file_path:
    :param overwrite:
    """

    if not overwrite and pth.exists(n2_file_path):
        raise FastFileExistsError(
            "N2-diagram file %s not written because it already exists. "
            "Use overwrite=True to bypass." % n2_file_path,
            n2_file_path,
        )

    make_parent_dir(n2_file_path)
    conf = FASTOADProblemConfigurator(configuration_file_path)
    conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
    problem = conf.get_problem()
    problem.setup()
    problem.final_setup()

    om.n2(problem, outfile=n2_file_path, show_browser=False)
    clear_output()
    _LOGGER.info("N2 diagram written in %s", pth.abspath(n2_file_path))
コード例 #4
0
def generate_configuration_file(configuration_file_path: str, overwrite: bool = False):
    """
    Generates a sample configuration file.

    :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
    :raise FastFileExistsError: if overwrite==False and configuration_file_path already exists
    """
    if not overwrite and pth.exists(configuration_file_path):
        raise FastFileExistsError(
            "Configuration file %s not written because it already exists. "
            "Use overwrite=True to bypass." % configuration_file_path,
            configuration_file_path,
        )

    if not pth.exists(pth.split(configuration_file_path)[0]):
        os.mkdir(pth.split(configuration_file_path)[0])
    parser = InputFileGenerator()
    root_folder = resources.__path__[0]
    for i in range(2):
        root_folder = pth.split(root_folder)[0]
    package_path = "[\"" + root_folder.replace('\\','\\\\') + "\"]"
    with path(resources, SAMPLE_FILENAME) as input_template_path:
        parser.set_template_file(str(input_template_path))
        # noinspection PyTypeChecker
        parser.set_generated_file(configuration_file_path)
        parser.reset_anchor()
        parser.mark_anchor("module_folders")
        parser.transfer_var(package_path, 0, 3)
        parser.generate()

    _LOGGER.info("Sample configuration written in %s", configuration_file_path)
コード例 #5
0
def _run_problem(
    configuration_file_path: str,
    overwrite: bool = False,
    mode="run_model",
    auto_scaling: bool = False,
) -> FASTOADProblem:
    """
    Runs problem according to provided file

    :param configuration_file_path: problem definition
    :param overwrite: if True, output file will be overwritten
    :param mode: 'run_model' or 'run_driver'
    :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)
    conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
    problem = conf.get_problem(read_inputs=True, auto_scaling=auto_scaling)

    outputs_path = pth.normpath(problem.output_file_path)
    if not overwrite and pth.exists(outputs_path):
        raise FastFileExistsError(
            "Problem not run because output file %s already exists. "
            "Use overwrite=True to bypass." % outputs_path,
            outputs_path,
        )

    problem.setup()
    start_time = time()
    if mode == "run_model":
        problem.run_model()
        problem.optim_failed = False  # Actually, we don't know
    else:
        problem.optim_failed = problem.run_driver()
    end_time = time()
    computation_time = round(end_time - start_time, 2)

    problem.write_outputs()
    if problem.optim_failed:
        _LOGGER.error("Optimization failed after " + str(computation_time) +
                      " seconds")
    else:
        _LOGGER.info("Computation finished after " + str(computation_time) +
                     " seconds")

    _LOGGER.info("Problem outputs written in %s", outputs_path)

    return problem
コード例 #6
0
ファイル: api.py プロジェクト: christophe-david/FAST-OAD
def generate_configuration_file(configuration_file_path: str, overwrite: bool = False):
    """
    Generates a sample configuration file.

    :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
    :raise FastFileExistsError: if overwrite==False and configuration_file_path already exists
    """
    if not overwrite and pth.exists(configuration_file_path):
        raise FastFileExistsError(
            "Configuration file %s not written because it already exists. "
            "Use overwrite=True to bypass." % configuration_file_path,
            configuration_file_path,
        )

    make_parent_dir(configuration_file_path)

    copy_resource(resources, SAMPLE_FILENAME, configuration_file_path)
    _LOGGER.info("Sample configuration written in %s", configuration_file_path)
コード例 #7
0
def write_xdsm(
    configuration_file_path: str,
    xdsm_file_path: str = None,
    overwrite: bool = False,
    depth: int = 2,
    wop_server_url: str = None,
    dry_run: bool = False,
):
    """

    :param configuration_file_path:
    :param xdsm_file_path: the path for HTML file to be written (will overwrite if needed)
    :param overwrite: if False, will raise an error if file already exists.
    :param depth: the depth analysis for WhatsOpt
    :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: 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")
    xdsm_file_path = pth.abspath(xdsm_file_path)

    if not overwrite and pth.exists(xdsm_file_path):
        raise FastFileExistsError(
            "XDSM-diagram file %s not written because it already exists. "
            "Use overwrite=True to bypass." % xdsm_file_path,
            xdsm_file_path,
        )

    make_parent_dir(xdsm_file_path)

    conf = FASTOADProblemConfigurator(configuration_file_path)
    conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
    problem = conf.get_problem()
    problem.setup()
    problem.final_setup()

    fastoad.openmdao.whatsopt.write_xdsm(problem, xdsm_file_path, depth,
                                         wop_server_url, dry_run)
    return xdsm_file_path
コード例 #8
0
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: 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. "
            "Use overwrite=True to bypass." % n2_file_path,
            n2_file_path,
        )

    make_parent_dir(n2_file_path)
    conf = FASTOADProblemConfigurator(configuration_file_path)
    conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
    problem = conf.get_problem()
    problem.setup()
    problem.final_setup()

    om.n2(problem, outfile=n2_file_path, show_browser=False)
    if InteractiveShell.initialized():
        clear_output()
    _LOGGER.info("N2 diagram written in %s", pth.abspath(n2_file_path))
    return n2_file_path
コード例 #9
0
ファイル: api.py プロジェクト: christophe-david/FAST-OAD
def write_xdsm(
    configuration_file_path: str,
    xdsm_file_path: str = None,
    overwrite: bool = False,
    depth: int = 2,
    wop_server_url=None,
    api_key=None,
):
    """

    :param configuration_file_path:
    :param xdsm_file_path:
    :param overwrite:
    :param depth:
    :param wop_server_url:
    :param api_key:
    :return:
    """
    if not xdsm_file_path:
        xdsm_file_path = pth.join(pth.dirname(configuration_file_path), "xdsm.html")
    xdsm_file_path = pth.abspath(xdsm_file_path)

    if not overwrite and pth.exists(xdsm_file_path):
        raise FastFileExistsError(
            "XDSM-diagram file %s not written because it already exists. "
            "Use overwrite=True to bypass." % xdsm_file_path,
            xdsm_file_path,
        )

    make_parent_dir(xdsm_file_path)

    conf = FASTOADProblemConfigurator(configuration_file_path)
    conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
    problem = conf.get_problem()
    problem.setup()
    problem.final_setup()

    wop = WhatsOpt(url=wop_server_url, login=False)

    try:
        ok = wop.login(api_key=api_key, echo=False)
    except requests.exceptions.ConnectionError:

        if not wop_server_url and wop.url == PROD_URL:
            used_url = wop.url
            # If connection failed while attempting to reach the wop default URL,
            # that is the internal ONERA server, try with the external server
            try:
                wop = WhatsOpt(url=DEFAULT_WOP_URL)
                ok = wop.login(api_key=api_key, echo=False)
            except requests.exceptions.ConnectionError:
                _LOGGER.warning("Failed to connect to %s and %s", used_url, DEFAULT_WOP_URL)
                return
        else:
            _LOGGER.warning("Failed to connect to %s", wop.url)
            return

    if ok:
        xdsm = wop.push_mda(
            problem, {"--xdsm": True, "--name": None, "--dry-run": False, "--depth": depth}
        )
        generate_xdsm_html(xdsm, xdsm_file_path)
    else:
        wop.logout()
        _LOGGER.warning("Could not login to %s", wop.url)
コード例 #10
0
ファイル: api.py プロジェクト: christophe-david/FAST-OAD
def list_modules(
    configuration_file_path: str = None,
    out: Union[IO, str] = None,
    overwrite: bool = False,
    verbose: bool = False,
    force_text_output: bool = False,
):
    """
    Writes list of available systems.
    If configuration_file_path is given and if it defines paths where there are registered systems,
    they will be listed too.

    :param configuration_file_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
    """
    if out is None:
        out = sys.stdout

    if configuration_file_path:
        conf = FASTOADProblemConfigurator(configuration_file_path)
        conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
        conf.load(configuration_file_path)
    # As the problem has been configured, BundleLoader now knows additional registered systems

    if verbose:
        cell_list = _get_detailed_system_list()
    else:
        cell_list = _get_simple_system_list()

    if isinstance(out, str):
        if not overwrite and pth.exists(out):
            raise FastFileExistsError(
                "File %s not written because it already exists. "
                "Use overwrite=True to bypass." % out,
                out,
            )

        make_parent_dir(out)
        out_file = open(out, "w")
    else:
        if (
            out == sys.stdout
            and InteractiveShell.initialized()
            and not force_text_output
            and not verbose
        ):
            display(HTML(tabulate(cell_list, tablefmt="html")))
            return

        out_file = out

    out_file.write(tabulate(cell_list, tablefmt="grid"))
    out_file.write("\n")

    if isinstance(out, str):
        out_file.close()
        _LOGGER.info("System list written in %s", out_file)
コード例 #11
0
ファイル: api.py プロジェクト: christophe-david/FAST-OAD
def list_variables(
    configuration_file_path: str,
    out: Union[IO, str] = None,
    overwrite: bool = False,
    force_text_output: bool = False,
    tablefmt: str = "grid",
):
    """
    Writes list of variables for the :class:`FASTOADProblem` specified in configuration_file_path.

    List is generally written as text. It can be displayed as a scrollable table view if:
    - function is used in an interactive IPython shell
    - out == sys.stdout
    - force_text_output == False

    :param configuration_file_path:
    :param out: the output stream or a path for the output file (None means sys.stdout)
    :param overwrite: if True and out parameter is a file path, the file will be written even if one
                      already exists
    :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
    :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.
    :raise FastFileExistsError: if overwrite==False and out parameter is a file path and the file
                                exists
    """
    if out is None:
        out = sys.stdout

    conf = FASTOADProblemConfigurator(configuration_file_path)
    conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
    problem = conf.get_problem()
    problem.setup()

    # Extracting inputs and outputs
    variables = VariableList.from_problem(problem)
    variables.sort(key=lambda var: var.name)
    input_variables = VariableList([var for var in variables if var.is_input])
    output_variables = VariableList([var for var in variables if not var.is_input])

    for var in input_variables:
        var.metadata["I/O"] = "IN"
    for var in output_variables:
        var.metadata["I/O"] = "OUT"

    variables_df = (
        (input_variables + output_variables)
        .to_dataframe()[["name", "I/O", "desc"]]
        .rename(columns={"name": "NAME", "desc": "DESCRIPTION"})
    )

    if isinstance(out, str):
        if not overwrite and pth.exists(out):
            raise FastFileExistsError(
                "File %s not written because it already exists. "
                "Use overwrite=True to bypass." % out,
                out,
            )
        make_parent_dir(out)
        out_file = open(out, "w")
    else:
        if out == sys.stdout and InteractiveShell.initialized() and not force_text_output:
            display(HTML(variables_df.to_html(index=False)))
            return

        # Here we continue with text output
        out_file = out

        # For a terminal output, we limit width of NAME column
        variables_df["NAME"] = variables_df["NAME"].apply(lambda s: "\n".join(tw.wrap(s, 50)))

    # In any case, let's break descriptions that are too long
    variables_df["DESCRIPTION"] = variables_df["DESCRIPTION"].apply(
        lambda s: "\n".join(tw.wrap(s, 100,))
    )

    out_file.write(
        tabulate(variables_df, headers=variables_df.columns, showindex=False, tablefmt=tablefmt)
    )
    out_file.write("\n")

    if isinstance(out, str):
        out_file.close()
        _LOGGER.info("Output list written in %s", out_file)
コード例 #12
0
def list_modules(
    source_path: Union[List[str], str] = None,
    out: Union[IO, str] = None,
    overwrite: bool = False,
    verbose: bool = False,
    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
    :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

    if isinstance(source_path, str):
        if pth.isfile(source_path):
            conf = FASTOADProblemConfigurator(source_path)
            conf._set_configuration_modifier(_PROBLEM_CONFIGURATOR)
            # As the problem has been configured,
            # BundleLoader now knows additional registered systems
        elif pth.isdir(source_path):
            RegisterOpenMDAOSystem.explore_folder(source_path)
        else:
            raise FileNotFoundError("Could not find %s" % source_path)
    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)
            else:
                RegisterOpenMDAOSystem.explore_folder(folder_path)
    elif source_path is not None:
        raise RuntimeError("Unexpected type for source_path")

    if verbose:
        cell_list = _get_detailed_system_list()
    else:
        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. "
                "Use overwrite=True to bypass." % out,
                out,
            )

        make_parent_dir(out)
        out_file = open(out, "w")
    else:
        if (out == sys.stdout and InteractiveShell.initialized()
                and not force_text_output and not verbose):
            display(HTML(tabulate(cell_list, tablefmt="html")))
            return None

        out_file = out

    out_file.write(tabulate(cell_list, tablefmt="grid"))
    out_file.write("\n")

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

    return None
コード例 #13
0
ファイル: api.py プロジェクト: areysset/FAST-OAD
def list_systems(configuration_file_path: str = None,
                 out: Union[IO, str] = sys.stdout,
                 overwrite: bool = False):
    """
    Writes list of available systems.
    If configuration_file_path is given and if it defines paths where there are registered systems,
    they will be listed too.

    :param configuration_file_path:
    :param out: the output stream or a path for the output file
    :param overwrite: if True and out is a file path, the file will be written even if one already
                      exists
    :raise FastFileExistsError: if overwrite==False and out is a file path and the file exists
    """

    if configuration_file_path:
        conf = FASTOADProblemConfigurator(configuration_file_path)
        conf.load(configuration_file_path)
    # As the problem has been configured, BundleLoader now knows additional registered systems

    if isinstance(out, str):
        if not overwrite and pth.exists(out):
            raise FastFileExistsError(
                "File %s not written because it already exists. "
                "Use overwrite=True to bypass." % out,
                out,
            )

        make_parent_dir(out)
        out_file = open(out, "w")
    else:
        out_file = out
    out_file.writelines([
        "== AVAILABLE SYSTEM IDENTIFIERS " + "=" * 68 + "\n", "-" * 100 + "\n"
    ])
    for identifier in sorted(OpenMDAOSystemRegistry.get_system_ids()):
        path = BundleLoader().get_factory_path(identifier)
        domain = OpenMDAOSystemRegistry.get_system_domain(identifier)
        description = OpenMDAOSystemRegistry.get_system_description(identifier)
        if description is None:
            description = ""
        out_file.write("  IDENTIFIER:   %s\n" % identifier)
        out_file.write("  PATH:         %s\n" % path)
        out_file.write("  DOMAIN:       %s\n" % domain.value)
        out_file.write("  DESCRIPTION:  %s\n" %
                       tw.indent(tw.dedent(description), "    "))
        out_file.write("-" * 100 + "\n")
    out_file.write("=" * 100 + "\n")

    out_file.writelines([
        "\n== AVAILABLE PROPULSION WRAPPER IDENTIFIERS " + "=" * 56 + "\n",
        "-" * 100 + "\n"
    ])
    for identifier in sorted(RegisterPropulsion.get_model_ids()):
        path = BundleLoader().get_factory_path(identifier)
        description = RegisterPropulsion.get_service_description(identifier)
        if description is None:
            description = ""
        out_file.write("  IDENTIFIER:   %s\n" % identifier)
        out_file.write("  PATH:         %s\n" % path)
        out_file.write("  DESCRIPTION:  %s\n" %
                       tw.indent(tw.dedent(description), "    "))
        out_file.write("-" * 100 + "\n")
    out_file.write("=" * 100 + "\n")

    if isinstance(out, str):
        out_file.close()
        _LOGGER.info("System list written in %s", out_file)
コード例 #14
0
ファイル: api.py プロジェクト: areysset/FAST-OAD
def list_variables(
    configuration_file_path: str,
    out: Union[IO, str] = sys.stdout,
    overwrite: bool = False,
    force_text_output: bool = False,
):
    """
    Writes list of variables for the :class:`FASTOADProblem` specified in configuration_file_path.

    List is generally written as text. It can be displayed as a scrollable table view if:
    - function is used in an interactive IPython shell
    - out == sys.stdout
    - force_text_output == False

    :param configuration_file_path:
    :param out: the output stream or a path for the output file
    :param overwrite: if True and out parameter is a file path, the file will be written even if one
                      already exists
    :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 parameter is a file path and the file
                                exists
    """
    conf = FASTOADProblemConfigurator(configuration_file_path)
    problem = conf.get_problem()
    problem.setup()

    # Extracting inputs and outputs
    variables = VariableList.from_problem(problem, get_promoted_names=False)
    variables.sort(key=lambda var: var.name)
    input_variables = VariableList([var for var in variables if var.is_input])
    output_variables = VariableList(
        [var for var in variables if not var.is_input])

    if isinstance(out, str):
        if not overwrite and pth.exists(out):
            raise FastFileExistsError(
                "File %s not written because it already exists. "
                "Use overwrite=True to bypass." % out,
                out,
            )
        make_parent_dir(out)
        out_file = open(out, "w")
        table_width = MAX_TABLE_WIDTH
    else:
        if out == sys.stdout and InteractiveShell.initialized(
        ) and not force_text_output:
            # Here we display the variable list as VariableViewer in a notebook
            for var in input_variables:
                var.metadata["I/O"] = "IN"
            for var in output_variables:
                var.metadata["I/O"] = "OUT"

            df = ((input_variables + output_variables).to_dataframe()[[
                "I/O", "name", "desc"
            ]].rename(columns={
                "name": "Name",
                "desc": "Description"
            }))
            display(HTML(df.to_html()))
            return

        # Here we continue with text output
        out_file = out
        table_width = min(get_terminal_size().columns, MAX_TABLE_WIDTH) - 1

    pd.set_option("display.max_colwidth", 1000)
    max_name_length = np.max([
        len(name)
        for name in input_variables.names() + output_variables.names()
    ])
    description_text_width = table_width - max_name_length - 2

    def _write_variables(out_f, variables):
        """Writes variables and their description as a pandas DataFrame"""
        df = variables.to_dataframe()

        # Create a new Series where description are wrapped on several lines if needed.
        # Each line becomes an element of the Series
        df["desc"] = [
            "\n".join(tw.wrap(s, description_text_width)) for s in df["desc"]
        ]
        new_desc = df.desc.str.split("\n", expand=True).stack()

        # Create a Series for name that will match new_desc Series. Variable name will be in front of
        # first line of description. An empty string will be in front of other lines.
        new_name = [
            df.name.loc[i] if j == 0 else "" for i, j in new_desc.index
        ]

        # Create the DataFrame that will be displayed
        new_df = pd.DataFrame({"NAME": new_name, "DESCRIPTION": new_desc})

        out_f.write(
            new_df.to_string(
                index=False,
                columns=["NAME", "DESCRIPTION"],
                justify="center",
                formatters={  # Formatters are needed for enforcing left justification
                    "NAME": ("{:%s}" % max_name_length).format,
                    "DESCRIPTION": ("{:%s}" % description_text_width).format,
                },
            )
        )
        out_file.write("\n")

    def _write_text_with_line(txt: str, line_length: int):
        """ Writes a line of given length with provided text inside """
        out_file.write("-" + txt + "-" * (line_length - 1 - len(txt)) + "\n")

    # Inputs
    _write_text_with_line(" INPUTS OF THE PROBLEM ", table_width)
    _write_variables(out_file, input_variables)

    # Outputs
    out_file.write("\n")
    _write_text_with_line(" OUTPUTS OF THE PROBLEM ", table_width)
    _write_variables(out_file, output_variables)
    _write_text_with_line("", table_width)

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