Source code for fastoad.io.variable_io

#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
#  FAST is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.

import os.path as pth
from fnmatch import fnmatchcase
from typing import IO, List, Sequence, Union

from fastoad.openmdao.variables import VariableList
from . import IVariableIOFormatter
from .xml import VariableXmlStandardFormatter


[docs]class VariableIO: """ Class for reading and writing variable values from/to file. The file format is defined by the class provided as `formatter` argument. :param data_source: the I/O stream, or a file path, used for reading or writing data :param formatter: a class that determines the file format to be used. Defaults to a VariableBasicXmlFormatter instance. """ def __init__(self, data_source: Union[str, IO], formatter: IVariableIOFormatter = None): self.data_source = data_source self.formatter: IVariableIOFormatter = ( formatter if formatter else VariableXmlStandardFormatter() )
[docs] def read(self, only: List[str] = None, ignore: List[str] = None) -> VariableList: """ Reads variables from provided data source. Elements of `only` and `ignore` can be real variable names or Unix-shell-style patterns. In any case, comparison is case-sensitive. :param only: List of variable names that should be read. Other names will be ignored. If None, all variables will be read. :param ignore: List of variable names that should be ignored when reading. :return: an VariableList instance where outputs have been defined using provided source """ variables = self.formatter.read_variables(self.data_source) used_variables = self._filter_variables(variables, only=only, ignore=ignore) return used_variables
[docs] def write(self, variables: VariableList, only: List[str] = None, ignore: List[str] = None): """ Writes variables from provided VariableList instance. Elements of `only` and `ignore` can be real variable names or Unix-shell-style patterns. In any case, comparison is case-sensitive. :param variables: a VariableList instance :param only: List of variable names that should be written. Other names will be ignored. If None, all variables will be written. :param ignore: List of variable names that should be ignored when writing """ used_variables = self._filter_variables(variables, only=only, ignore=ignore) # Before writing, variables are sorted to have short paths first. With equal path length # alphanumeric order will be used. used_variables.sort(key=lambda var: "%02i_%s" % (len(var.name.split(":")), var.name)) self.formatter.write_variables(self.data_source, used_variables)
@staticmethod def _filter_variables( variables: VariableList, only: Sequence[str] = None, ignore: Sequence[str] = None ) -> VariableList: """ filters the variables such that the ones in arg only are kept and the ones in arg ignore are removed. Elements of `only` and `ignore` can be variable names or Unix-shell-style patterns. In any case, filter is case-sensitive. :param variables: :param only: List of OpenMDAO variable names that should be written. Other names will be ignored. If None, all variables will be written. :param ignore: List of OpenMDAO variable names that should be ignored when writing :return: filtered variables """ # Dev note: We use sets, but sets of Variable instances do # not work. Do we work with variable names instead. # FIXME: Variable instances are now hashable, so set of Variable instances should now work var_names = variables.names() if only is None: used_var_names = set(var_names) else: used_var_names = set() for pattern in only: used_var_names.update( [variable.name for variable in variables if fnmatchcase(variable.name, pattern)] ) if ignore is not None: for pattern in ignore: used_var_names.difference_update( [variable.name for variable in variables if fnmatchcase(variable.name, pattern)] ) # It could be simpler, but I want to keep the order used_variables = VariableList() for var in variables: if var.name in used_var_names: used_variables.append(var) return used_variables
[docs]class DataFile(VariableList): """ Class for managing FAST-OAD data files. Behaves like :class:`~fastoad.openmdao.variables.VariableList` class but has :meth:`load` and :meth:`save` methods. """ def __init__(self, file_path: str, formatter: IVariableIOFormatter = None, load_data=True): """ :param file_path: the file path where data will be loaded and saved. :param formatter: a class that determines the file format to be used. Defaults to FAST-OAD native format. See :class:`VariableIO` for more information. :param load_data: if True and if file exists, its content will be loaded at instantiation. """ super().__init__() self._variable_io = VariableIO(file_path, formatter) if pth.exists(file_path) and load_data: self.load() @property def file_path(self) -> str: """Path of data file.""" return self._variable_io.data_source @file_path.setter def file_path(self, value: str): self._variable_io.data_source = value @property def formatter(self) -> IVariableIOFormatter: """Class that defines the file format.""" return self._variable_io.formatter @formatter.setter def formatter(self, value: IVariableIOFormatter): self._variable_io.formatter = value
[docs] def load(self): """Loads file content.""" self.clear() self.update(self._variable_io.read(), add_variables=True)
[docs] def save(self): """Saves current state of variables in file.""" self._variable_io.write(self)