Skip to content

Commit

Permalink
Merge pull request #503 from fast-aircraft-design/setup-optimisation
Browse files Browse the repository at this point in the history
Avoiding unnecessary setup operations
  • Loading branch information
christophe-david committed Jan 18, 2024
2 parents 7e7cada + 4eeb488 commit 51a596a
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 146 deletions.
39 changes: 6 additions & 33 deletions src/fastoad/io/configuration/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,15 @@
from importlib.resources import open_text
from typing import Dict

import numpy as np
import openmdao.api as om
import tomlkit
from jsonschema import validate
from ruamel.yaml import YAML

from fastoad._utils.files import make_parent_dir
from fastoad.io import DataFile, IVariableIOFormatter
from fastoad.io import IVariableIOFormatter
from fastoad.module_management.service_registry import RegisterOpenMDAOSystem, RegisterSubmodel
from fastoad.openmdao.problem import FASTOADProblem
from fastoad.openmdao.variables import VariableList
from . import resources
from .exceptions import (
FASTConfigurationBadOpenMDAOInstructionError,
Expand Down Expand Up @@ -115,6 +113,10 @@ def get_problem(self, read_inputs: bool = False, auto_scaling: bool = False) ->

problem = FASTOADProblem()
self._build_model(problem)

if self._configuration_modifier:
self._configuration_modifier.modify(problem)

problem.input_file_path = self.input_file_path
problem.output_file_path = self.output_file_path

Expand All @@ -132,9 +134,6 @@ def get_problem(self, read_inputs: bool = False, auto_scaling: bool = False) ->
if read_inputs:
self._add_design_vars(problem.model, auto_scaling)

if self._configuration_modifier:
self._configuration_modifier.modify(problem)

return problem

def load(self, conf_file):
Expand Down Expand Up @@ -213,33 +212,7 @@ def write_needed_inputs(
not provided, expected format will be the default one.
"""
problem = self.get_problem(read_inputs=False)
problem.setup()
variables = DataFile(self.input_file_path, load_data=False)

unconnected_inputs = VariableList.from_problem(
problem,
use_initial_values=True,
get_promoted_names=True,
promoted_only=True,
io_status="inputs",
)

variables.update(
unconnected_inputs,
add_variables=True,
)
if source_file_path:
ref_vars = DataFile(source_file_path, formatter=source_formatter)
variables.update(ref_vars, add_variables=False)
nan_variable_names = []
for var in variables:
var.is_input = True
# Checking if variables have NaN values
if np.any(np.isnan(var.value)):
nan_variable_names.append(var.name)
if nan_variable_names:
_LOGGER.warning("The following variables have NaN values: %s", nan_variable_names)
variables.save()
problem.write_needed_inputs(source_file_path, source_formatter)

def get_optimization_definition(self) -> Dict:
"""
Expand Down
49 changes: 27 additions & 22 deletions src/fastoad/openmdao/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Utility functions for OpenMDAO classes/instances
"""
# This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
# Copyright (C) 2023 ONERA & ISAE-SUPAERO
# Copyright (C) 2024 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
Expand All @@ -16,52 +16,57 @@

from contextlib import contextmanager
from copy import deepcopy
from typing import List, Tuple
from typing import List, Tuple, TypeVar

import numpy as np
import openmdao.api as om
from deprecated import deprecated
from openmdao.core.constants import _SetupStatus
from openmdao.utils.mpi import FakeComm

T = TypeVar("T", bound=om.Problem)

@contextmanager
def problem_without_mpi(problem: om.Problem) -> om.Problem:
"""
Context manager that delivers a copy of the given OpenMDAO problem.

A deepcopy operation may crash if problem.comm is not pickle-able, like a
def get_mpi_safe_problem_copy(problem: T) -> T:
"""
This function does a deep copy of input OpenMDAO problem while avoiding
the crash that can occur if problem.comm is not pickle-able, like a
mpi4py.MPI.Intracomm object.
This context manager temporarily sets a FakeComm object as problem.comm and
does the copy.
:param problem:
:return: a copy of the problem with a FakeComm object as problem.comm
"""
with copyable_problem(problem) as no_mpi_problem:
problem_copy = deepcopy(no_mpi_problem)

return problem_copy


It ensures the original problem gets back its original communicator after
@contextmanager
def copyable_problem(problem: om.Problem) -> om.Problem:
"""
Context manager that temporarily makes the input problem compatible with deepcopy.
It ensures the problem gets back its original attributes after
the `with` block is ended.
:param problem: any openMDAO problem
:return: A copy of the given problem with a FakeComm object as problem.comm
:return: The given problem with a FakeComm object as problem.comm
"""

# An actual MPI communicator will make the deepcopy crash if an MPI
# library is installed.

actual_comm = problem.comm
problem.comm = FakeComm()

metadata_were_added = False
try:
if not problem._metadata:
# Adding temporarily this attribute ensures that the post-hook for N2 reports
# Adding this attribute ensures that the post-hook for N2 reports
# will not crash. Indeed, due to the copy, it tries to post-process
# the 'problem' instance at the end of setup of the 'problem_copy' instance.
problem._metadata = {"saved_errors": []}
metadata_were_added = True
problem_copy = deepcopy(problem)
problem_copy.comm = problem.comm

yield problem_copy
problem._metadata = {"saved_errors": [], "setup_status": _SetupStatus.PRE_SETUP}
yield problem
finally:
if metadata_were_added:
problem._metadata = None
problem.comm = actual_comm


Expand Down

0 comments on commit 51a596a

Please sign in to comment.