Source code for gemseo.problems.scalable.parametric.core.design_space

# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
#
# 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
# Contributors:
#    INITIAL AUTHORS - initial API and implementation and/or initial
#                         documentation
#        :author: Matthias De Lozzo
#    OTHER AUTHORS   - MACROSCOPIC CHANGES
"""
Scalable problem - Design space
*******************************
"""
from __future__ import annotations

import collections
import logging

from numpy import ones
from numpy import zeros

from .variables import check_consistency
from .variables import get_coupling_name
from .variables import get_x_local_name
from .variables import X_SHARED_NAME

LOGGER = logging.getLogger(__name__)

[docs]class TMDesignSpace:
"""The design space for the scalable problem introduced by Tedford and Martins (2010)
defines the lower and upper bounds of both local design parameters, shared design
parameters and coupling variables, as well as default values.

The lower bounds are all equal to 0, the upper bounds are all equal to 1 and default
values are all equal to 0.5.
"""

def __init__(
self,
n_shared: int = 1,
n_local: list[int] | None = None,
n_coupling: list[int] | None = None,
default_inputs=None,
dtype: str = "float64",
) -> None:
"""The construction of the design space requires the number of shared design
parameters, the number of local design parameters per discipline and the number
of coupling variables per discipline. The two latter arguments must be list of
integers with the same length which corresponds to the number of strongly coupled
disciplines. By default, the design space considers two disciplines.

:param int n_shared: size of the shared design parameters.
Default: 1.
:param list(int) n_local: sizes of the local design parameters.
If None, use [1, 1]. Default: None.
:param list(int) n_coupling: sizes of the coupling parameters.
If None, use [1, 1]. Default: None.
:param dict default_inputs: default inputs.
Default: None.
:param dtype: numpy data type. Default: 'float64'.
"""
# Set and check the dimensions of the problem
n_local = n_local or [1, 1]
n_coupling = n_coupling or [1, 1]
check_consistency(n_shared, n_local, n_coupling)

default_inputs = (
{}
if not isinstance(default_inputs, collections.abc.Mapping)
else default_inputs
)
self.names = []
self.sizes = {}
self.lower_bounds = {}
self.upper_bounds = {}
self.default_values = {}

# Set the local design parameters
for index, size in enumerate(n_local):
name = get_x_local_name(index)
value = default_inputs.get(name, zeros(size) + 0.5)
value.astype(dtype)
self.names.append(name)
self.sizes[name] = size
self.lower_bounds[name] = zeros(size)
self.upper_bounds[name] = ones(size)
self.default_values[name] = value

# Set the shared design parameters
name = X_SHARED_NAME
value = default_inputs.get(X_SHARED_NAME, zeros(n_shared) + 0.5)
value.astype(dtype)
self.names.append(name)
self.sizes[name] = n_shared
self.lower_bounds[name] = zeros(n_shared)
self.upper_bounds[name] = ones(n_shared)
self.default_values[name] = value

# Set the coupling variables
for index, size in enumerate(n_coupling):
name = get_coupling_name(index)
value = default_inputs.get(name, zeros(size) + 0.5)
value.astype(dtype)
self.names.append(name)
self.sizes[name] = size
self.lower_bounds[name] = zeros(size)
self.upper_bounds[name] = ones(size)
self.default_values[name] = value