diff --git a/changelog/fragments/1369.fixed.rst b/changelog/fragments/1369.fixed.rst new file mode 100644 index 0000000000000000000000000000000000000000..d6927ed0726bcb880867817f308f0ac0417232db --- /dev/null +++ b/changelog/fragments/1369.fixed.rst @@ -0,0 +1 @@ +- ``Discipline.check_jacobian`` now correctly uses the ``input_data`` argument for the approximate Jacobian when provided. diff --git a/src/gemseo/core/discipline/discipline.py b/src/gemseo/core/discipline/discipline.py index 7e78b0265549022e3c946a1ac491ee645ec370b9..b666ffc8bbb59bc62084ee6128011356237a0647 100644 --- a/src/gemseo/core/discipline/discipline.py +++ b/src/gemseo/core/discipline/discipline.py @@ -667,6 +667,7 @@ class Discipline(BaseDiscipline, metaclass=ClassInjector): reference_jacobian_path=reference_jacobian_path, save_reference_jacobian=save_reference_jacobian, indices=indices, + input_data=input_data, ) def _get_differentiated_io( diff --git a/src/gemseo/utils/derivatives/derivatives_approx.py b/src/gemseo/utils/derivatives/derivatives_approx.py index 1d89bc66b0690d345dd444da282247d18e7b61ac..560113374ac8ff51d9e0c7fc34da5419ac2826f5 100644 --- a/src/gemseo/utils/derivatives/derivatives_approx.py +++ b/src/gemseo/utils/derivatives/derivatives_approx.py @@ -134,17 +134,23 @@ class DisciplineJacApprox: self, output_names: Sequence[str], input_names: Sequence[str], + input_data: Mapping[str, ndarray] = READ_ONLY_EMPTY_DICT, ) -> None: """Create the Jacobian approximation class. Args: input_names: The names of the inputs used to differentiate the outputs. output_names: The names of the outputs to be differentiated. + input_data: The input data for the Jacobian approximation. Raises: ValueError: If the Jacobian approximation method is unknown. """ - self.func = self.generator.get_function(input_names, output_names) + self.func = self.generator.get_function( + input_names, + output_names, + input_data or None, + ) self.approximator = GradientApproximatorFactory().create( self.approx_method, self.func.evaluate, @@ -260,6 +266,7 @@ class DisciplineJacApprox: output_names: Iterable[str], input_names: Iterable[str], x_indices: Sequence[int] = (), + input_data: Mapping[str, ndarray] = READ_ONLY_EMPTY_DICT, ) -> dict[str, dict[str, ndarray]]: """Approximate the Jacobian. @@ -269,11 +276,14 @@ class DisciplineJacApprox: x_indices: The components of the input vector to be used for the differentiation. If empty, use all the components. + input_data: The input data needed to approximate the Jacobian + according to the discipline input grammar. + If empty, use the :attr:`.Discipline.default_input_data`. Returns: The approximated Jacobian. """ - self._create_approximator(output_names, input_names) + self._create_approximator(output_names, input_names, input_data) if self.auto_steps and all(key in self.auto_steps for key in input_names): step = ( @@ -340,6 +350,7 @@ class DisciplineJacApprox: indices: Mapping[ str, int | Sequence[int] | Ellipsis | slice ] = READ_ONLY_EMPTY_DICT, + input_data: Mapping[str, ndarray] = READ_ONLY_EMPTY_DICT, ) -> bool: """Check if the analytical Jacobian is correct with respect to a reference one. @@ -381,6 +392,9 @@ class DisciplineJacApprox: If a variable name is missing, consider all its components. If ``None``, consider all the components of all the ``inputs`` and ``outputs``. + input_data: The input data needed to execute the discipline + according to the discipline input grammar. + If empty, use the :attr:`.Discipline.default_input_data`. Returns: Whether the analytical Jacobian is correct. @@ -404,8 +418,11 @@ class DisciplineJacApprox: analytic_jacobian = analytic_jacobian or self.discipline.jac if not reference_jacobian_path or save_reference_jacobian: + for input_name in tuple(input_data): + if input_name not in self.discipline.input_grammar.names: + del input_data[input_name] approximated_jacobian = self.compute_approx_jac( - output_names, input_names, input_indices + output_names, input_names, input_indices, input_data ) else: with Path(reference_jacobian_path).open("rb") as infile: diff --git a/tests/core/test_discipline.py b/tests/core/test_discipline.py index 3fb3f87dab792f552b8e679986d19de865c26390..f0e7a542141702b0da263e89cee0de7bcdc2c5f9 100644 --- a/tests/core/test_discipline.py +++ b/tests/core/test_discipline.py @@ -38,6 +38,7 @@ from numpy import ndarray from numpy import ones from numpy.linalg import norm +from gemseo import create_discipline from gemseo.caches.hdf5_cache import HDF5Cache from gemseo.caches.simple_cache import SimpleCache from gemseo.core.chains.chain import MDOChain @@ -51,6 +52,9 @@ from gemseo.disciplines.analytic import AnalyticDiscipline from gemseo.disciplines.auto_py import AutoPyDiscipline from gemseo.mda.base_mda import BaseMDA from gemseo.problems.mdo.sellar.sellar_1 import Sellar1 +from gemseo.problems.mdo.sellar.variables import X_1 +from gemseo.problems.mdo.sellar.variables import X_SHARED +from gemseo.problems.mdo.sellar.variables import Y_2 from gemseo.problems.mdo.sobieski._disciplines_sg import SobieskiStructureSG from gemseo.problems.mdo.sobieski.core.problem import SobieskiProblem from gemseo.problems.mdo.sobieski.disciplines import SobieskiAerodynamics @@ -482,6 +486,19 @@ def test_check_jacobian_2() -> None: disc.linearize({"x": x}, compute_all_jacobians=True) +def test_check_jacobian_input_data() -> None: + sellar_1 = create_discipline("Sellar1") + input_data = { + X_1: array([3.0]), + X_SHARED: array([3.0, 3.0]), + Y_2: array([3.0]), + } + sellar_1.check_jacobian( + input_data=input_data, + input_names=[Y_2], + ) + + def test_check_jacobian_parallel_fd() -> None: """Test check_jacobian in parallel.""" sm = SobieskiMission()