Salome HOME
Documentation examples corrections and update
authorJean-Philippe ARGAUD <jean-philippe.argaud@edf.fr>
Sun, 26 May 2024 19:25:59 +0000 (21:25 +0200)
committerJean-Philippe ARGAUD <jean-philippe.argaud@edf.fr>
Sun, 26 May 2024 19:25:59 +0000 (21:25 +0200)
20 files changed:
doc/en/ref_algorithm_InterpolationByReducedModelTest.rst
doc/en/ref_algorithm_MeasurementsOptimalPositioningTask.rst
doc/en/ref_observations_requirements.rst
doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.py [new file with mode: 0644]
doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.res [new file with mode: 0644]
doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.rst [new file with mode: 0644]
doc/en/scripts/simple_MeasurementsOptimalPositioningTask31.png [new file with mode: 0644]
doc/en/scripts/simple_MeasurementsOptimalPositioningTask32.png [new file with mode: 0644]
doc/en/scripts/simple_MeasurementsOptimalPositioningTask33.png [new file with mode: 0644]
doc/fr/ref_algorithm_InterpolationByReducedModelTest.rst
doc/fr/ref_algorithm_MeasurementsOptimalPositioningTask.rst
doc/fr/ref_observations_requirements.rst
doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.py [new file with mode: 0644]
doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.res [new file with mode: 0644]
doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.rst [new file with mode: 0644]
doc/fr/scripts/simple_MeasurementsOptimalPositioningTask31.png [new file with mode: 0644]
doc/fr/scripts/simple_MeasurementsOptimalPositioningTask32.png [new file with mode: 0644]
doc/fr/scripts/simple_MeasurementsOptimalPositioningTask33.png [new file with mode: 0644]
src/daComposant/daNumerics/Models/TwoDimensionalInverseDistanceCS2010.py [new file with mode: 0644]
src/daComposant/daNumerics/Models/__init__.py [new file with mode: 0644]

index f6c55ebd0b3437be08b11b9f57cc8490c9698455..d122878cd501f300f19138eee3654efc201a120d 100644 (file)
@@ -31,13 +31,14 @@ Checking algorithm "*InterpolationByReducedModelTest*"
 .. include:: snippets/Header2Algo01.rst
 
 This algorithm provides a simple way of analyzing the quality of the empirical
-state interpolation obtained using a reduced basis, using measurements at
-specific points.
+interpolation obtained by a reduced basis for complete states, using
+measurements at precise points.
 
 The results displayed by default are simple statistics related to the
-normalized errors of interpolation with a reduced basis. The test uses a
-reduced base and a set of optimal measurement positions, and uses
-pseudo-measurements from each complete state (“*snapshot*”) included in the
+normalized errors between the interpolation with a reduced basis and the
+complete states we seek to represent on the reduced basis. The test requires a
+reduced basis and a set of optimal measurement positions, and uses
+pseudo-measurements from each complete state ("*snapshot*") included in the
 given test set.
 
 Please note: to be consistent, this test must use the same mathematical norm as
index 7e260eb2dfaaef5e7e3542b70f98b414f69d4371..a61ccd3358851a9b8e35baa037fde2e8db003d75 100644 (file)
@@ -220,6 +220,33 @@ StoreSupplementaryCalculations
 .. literalinclude:: scripts/simple_MeasurementsOptimalPositioningTask2.res
     :language: none
 
+.. --------- ..
+.. include:: scripts/simple_MeasurementsOptimalPositioningTask3.rst
+
+.. literalinclude:: scripts/simple_MeasurementsOptimalPositioningTask3.py
+
+.. include:: snippets/Header2Algo10.rst
+
+.. literalinclude:: scripts/simple_MeasurementsOptimalPositioningTask3.res
+    :language: none
+
+.. include:: snippets/Header2Algo11.rst
+
+.. _simple_MeasurementsOptimalPositioningTask31:
+.. image:: scripts/simple_MeasurementsOptimalPositioningTask31.png
+  :align: center
+  :width: 60%
+
+.. _simple_MeasurementsOptimalPositioningTask32:
+.. image:: scripts/simple_MeasurementsOptimalPositioningTask32.png
+  :align: center
+  :width: 60%
+
+.. _simple_MeasurementsOptimalPositioningTask33:
+.. image:: scripts/simple_MeasurementsOptimalPositioningTask33.png
+  :align: center
+  :width: 60%
+
 .. ------------------------------------ ..
 .. include:: snippets/Header2Algo06.rst
 
index 81f896d766f5a14154004b569d1689c010640f07..3ec2454f13cf43f6c15ea0c9a97a77d3c61b5e2c 100644 (file)
@@ -91,14 +91,15 @@ of measurements, or several fields physically homogeneous or not.
     - data text file (TXT, CSV, TSV, DAT), with variable pointer by name in column or row, indicated by the keyword "*DataFile*" with the condition ``Vector=True``
     - binary data file (NPY, NPZ), with variable pointer by name, indicated by the keyword "*DataFile*" with the condition ``Vector=True``
 
-- Examples of statements in TUI interface:
+Examples of statements in TUI interface:
+::
 
-    - ``case.setObservation( Vector = [1, 2, 3] )``
-    - ``case.setObservation( Vector = numpy.array([1, 2, 3]) )``
-    - ``case.setObservation( Vector = '1 2 3' )``
-    - ``case.setObservation( Vector=True, Script = 'script.py' )```
-    - ``case.setObservation( Vector=True, DataFile = 'data.csv' )```
-    - ``case.setObservation( Vector=True, DataFile = 'data.npy' )```
+    case.setObservation( Vector = [1, 2, 3] )
+    case.setObservation( Vector = numpy.array([1, 2, 3]) )
+    case.setObservation( Vector = '1 2 3' )
+    case.setObservation( Vector=True, Script = 'script.py' )
+    case.setObservation( Vector=True, DataFile = 'data.csv' )
+    case.setObservation( Vector=True, DataFile = 'data.npy' )
 
 Use note: in a given study, only the last record (whether a single vector or a
 series of vectors) can be used, as only one observation concept exists per ADAO
@@ -139,16 +140,17 @@ of which we consider a history.
     - data text file (TXT, CSV, TSV), with variable pointer by name in column or row, indicated by the keyword "*DataFile*" with the condition ``VectorSerie=True``
     - binary data file (NPY, NPZ), with variable pointer by name, indicated by the keyword "*DataFile*" with the condition ``VectorSerie=True``
 
-- Examples of statements in TUI interface:
-
-    - ``case.setObservation( VectorSerie = [[1,2,3], [1,2,3]] )``
-    - ``case.setObservation( VectorSerie = [numpy.array([1,2,3]), numpy.array([1,2,3])] )``
-    - ``case.setObservation( VectorSerie =  ['1 2 3', '1 2 3'] )``
-    - ``case.setObservation( VectorSerie =  '[[1,2,3], [1,2,3]]' )``
-    - ``case.setObservation( VectorSerie =  '1 2 3 ; 1 2 3' )``
-    - ``case.setObservation( VectorSerie=True, Script = 'script.py' )```
-    - ``case.setObservation( VectorSerie=True, DataFile = 'data.csv' )```
-    - ``case.setObservation( VectorSerie=True, DataFile = 'data.npy' )```
+Examples of statements in TUI interface:
+::
+
+    case.setObservation( VectorSerie = [[1,2,3], [1,2,3]] )
+    case.setObservation( VectorSerie = [numpy.array([1,2,3]), numpy.array([1,2,3])] )
+    case.setObservation( VectorSerie =  ['1 2 3', '1 2 3'] )
+    case.setObservation( VectorSerie =  '[[1,2,3], [1,2,3]]' )
+    case.setObservation( VectorSerie =  '1 2 3 ; 1 2 3' )
+    case.setObservation( VectorSerie=True, Script = 'script.py' )
+    case.setObservation( VectorSerie=True, DataFile = 'data.csv' )
+    case.setObservation( VectorSerie=True, DataFile = 'data.npy' )
 
 Use note: in a given study, only the last record (whether a single vector or a
 series of vectors) can be used, as only one observation concept exists per ADAO
diff --git a/doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.py b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.py
new file mode 100644 (file)
index 0000000..8878d12
--- /dev/null
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+#
+import numpy as np
+import matplotlib.pyplot as plt
+np.random.seed(123456789)
+#
+dimension = 20
+#
+print("Defining a set of artificial physical fields")
+from Models.TwoDimensionalInverseDistanceCS2010 \
+     import TwoDimensionalInverseDistanceCS2010 as Equation
+Eq = Equation(dimension, dimension)
+print()
+#
+print("Search for optimal measurement positions")
+from adao import adaoBuilder
+case = adaoBuilder.New()
+case.setAlgorithmParameters(
+    Algorithm = 'MeasurementsOptimalPositioningTask',
+    Parameters = {
+        "Variant":"DEIM",
+        "SampleAsnUplet":Eq.get_sample_of_mu(15, 15),
+        "MaximumNumberOfLocations":50,
+        "ErrorNorm":"Linf",
+        "ErrorNormTolerance":0.,
+        "StoreSupplementaryCalculations":[
+            "SingularValues",
+            "OptimalPoints",
+            "EnsembleOfSimulations",
+            ],
+        }
+    )
+case.setBackground(Vector = [1,1] )
+case.setObservationOperator(OneFunction = Eq.OneRealisation)
+case.execute()
+#
+print()
+print("Graphical display of the results")
+#
+sp = case.get("EnsembleOfSimulations")[-1]
+x1, x2 = Eq.get_x()
+x1, x2 = np.meshgrid(x1, x2)
+name = "Representation of a few snapshots of G out of a total of %i"%sp.shape[1]
+print("  -", name)
+fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, figsize=(6, 6))
+fig.suptitle(name)
+ax.set_xlabel("Position x1", fontweight='bold', color='red')
+ax.set_ylabel("Position x2", fontweight='bold', color='red')
+ax.set_zlabel("Magnitude of G", fontweight='bold', color='red')
+for i in range(sp.shape[1]):
+    if i % 44 != 0: continue
+    ax.plot_surface(x1, x2, sp[:,i].reshape((dimension, dimension)), cmap='coolwarm')
+fig.savefig("simple_MeasurementsOptimalPositioningTask31.png")
+plt.close()
+#
+sv = case.get("SingularValues")[-1]
+name = "Singular values of the set of G simulations"
+print("  -", name)
+fig, ax = plt.subplots(figsize=(6, 6))
+fig.suptitle(name)
+ax.set_xlabel("Index of singular values, numbered from 1")
+ax.set_ylabel("Magnitude of singular values")
+ax.set_xlim(1, len(sv))
+ax.set_yscale("log")
+ax.grid(True)
+ax.plot(range(1, 1 + len(sv)), sv)
+fig.savefig("simple_MeasurementsOptimalPositioningTask32.png")
+plt.tight_layout()
+plt.close()
+#
+nbmax = 15
+op = case.get("OptimalPoints")[-1]
+posx1 = [x1.reshape((-1,))[ip] for ip in op[:nbmax]]
+posx2 = [x2.reshape((-1,))[ip] for ip in op[:nbmax]]
+name = "Set of %i first optimal points of measurement"%nbmax
+Omega = Eq.get_bounds_on_space()
+print("  -", name)
+fig, ax = plt.subplots(figsize=(6, 6))
+fig.suptitle(name)
+ax.set_xlabel("Position x1", fontweight='bold', color='red')
+ax.set_ylabel("Position x2", fontweight='bold', color='red')
+ax.set_xlim(Omega[0][0] - 0.05, Omega[0][1] + 0.05)
+ax.set_ylim(Omega[1][0] - 0.05, Omega[1][1] + 0.05)
+ax.grid(True, which='both', linestyle=(0, (1, 5)), linewidth=0.5)
+ax.plot(posx1, posx2, markersize=6, marker="o", linestyle='')
+for i in range(len(posx1)):
+    ax.text(posx1[i] + 0.005, posx2[i] + 0.01, str(i + 1), fontweight='bold')
+fig.savefig("simple_MeasurementsOptimalPositioningTask33.png")
+plt.tight_layout()
+plt.close()
diff --git a/doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.res b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.res
new file mode 100644 (file)
index 0000000..455a3d3
--- /dev/null
@@ -0,0 +1,8 @@
+Defining a set of artificial physical fields
+
+Search for optimal measurement positions
+
+Graphical display of the results
+  - Representation of a few snapshots of G out of a total of 225
+  - Singular values of the set of G simulations
+  - Set of 15 first optimal points of measurement
diff --git a/doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.rst b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask3.rst
new file mode 100644 (file)
index 0000000..9445285
--- /dev/null
@@ -0,0 +1,30 @@
+.. index:: single: MeasurementsOptimalPositioningTask (example)
+
+Third example
+.............
+
+This more complete example describes the optimal positioning of measurements
+associated with a reduced DEIM-type decomposition on a classical parametric
+non-linear function, as proposed in reference [Chaturantabut10]_. This
+particular function has the notable advantage of being dependent only on a
+position :math:`x` in 2D and a parameter :math:`\mu` in 2D too. It therefore
+enables a pedagogical illustration of optimal measurement points.
+
+This function depends on the position :math:`x=(x_1,x_2)\in\Omega=[0.1,0.9]^2`
+in the 2D plane, and on the parameter
+:math:`\mu=(\mu_1,\mu_2)\in\mathcal{D}=[-1,-0.01]^2` of dimension 2 :
+
+.. math:: G(x;\mu) = \frac{1}{\sqrt{(x_1 - \mu_1)^2 + (x_2 - \mu_2)^2 + 0.1^2}}
+
+The function is represented on a regular :math:`\Omega_G` spatial grid of size
+20x20 points. It is available in ADAO built-in test models under the name
+`TwoDimensionalInverseDistanceCS2010`. So here we first build a set of
+simulations of :math:`G`, then apply the DEIM-type decomposition algorithm to
+it, and derive some simple illustrations.
+
+It can be seen that the singular values decrease steadily down to numerical
+noise, indicating that around a hundred basis elements are needed to fully
+represent the information contained in the set of :math:`G` simulations.
+Furthermore, the optimal measurement points in the :math:`\Omega_G` domain are
+inhomogeneously distributed, favoring the spatial zone near the
+:math:`(0.1,0.1)` corner in which the :math:`G` function varies more.
diff --git a/doc/en/scripts/simple_MeasurementsOptimalPositioningTask31.png b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask31.png
new file mode 100644 (file)
index 0000000..128980d
Binary files /dev/null and b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask31.png differ
diff --git a/doc/en/scripts/simple_MeasurementsOptimalPositioningTask32.png b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask32.png
new file mode 100644 (file)
index 0000000..8c78ab5
Binary files /dev/null and b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask32.png differ
diff --git a/doc/en/scripts/simple_MeasurementsOptimalPositioningTask33.png b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask33.png
new file mode 100644 (file)
index 0000000..e1aa57a
Binary files /dev/null and b/doc/en/scripts/simple_MeasurementsOptimalPositioningTask33.png differ
index 1b4831efb817cf47f58a2e4a5caca6bf142ccbb3..a0f465199bf7526808196ec674ea6240e60af59f 100644 (file)
@@ -31,14 +31,15 @@ Algorithme de vérification "*InterpolationByReducedModelTest*"
 .. include:: snippets/Header2Algo01.rst
 
 Cet algorithme permet d'analyser de manière simple la qualité de
-l'interpolation empirique des états obtenue par une base réduite, en utilisant
-des mesures en des points précis.
+l'interpolation empirique obtenue par une base réduite pour des états complets,
+en utilisant des mesures en des points précis.
 
 Les résultats affichés par défaut sont des statistiques simples liées aux
-erreurs normalisées de l'interpolation avec une base réduite. Le test utilise
+erreurs normalisées entre l'interpolation avec une base réduite et les états
+complets que l'on cherche à représenter sur la base réduite. Le test requiert
 une base réduite et un ensemble de positions de mesures optimales, et utilise
 des pseudo-mesures provenant de chaque état complet ("*snapshot*") inclus dans
-l'ensemble de test donné.
+l'ensemble donné de test.
 
 Attention : pour être cohérent, ce test doit utiliser la même norme
 mathématique que celle utilisée pour construire la base réduite. La norme étant
index 26e45ee48c8a5226201387bccaff98c3c0c14b0d..a4adb23bd9c30da43f57d06c74e46823ed9400ad 100644 (file)
@@ -223,6 +223,33 @@ StoreSupplementaryCalculations
 .. literalinclude:: scripts/simple_MeasurementsOptimalPositioningTask2.res
     :language: none
 
+.. --------- ..
+.. include:: scripts/simple_MeasurementsOptimalPositioningTask3.rst
+
+.. literalinclude:: scripts/simple_MeasurementsOptimalPositioningTask3.py
+
+.. include:: snippets/Header2Algo10.rst
+
+.. literalinclude:: scripts/simple_MeasurementsOptimalPositioningTask3.res
+    :language: none
+
+.. include:: snippets/Header2Algo11.rst
+
+.. _simple_MeasurementsOptimalPositioningTask31:
+.. image:: scripts/simple_MeasurementsOptimalPositioningTask31.png
+  :align: center
+  :width: 60%
+
+.. _simple_MeasurementsOptimalPositioningTask32:
+.. image:: scripts/simple_MeasurementsOptimalPositioningTask32.png
+  :align: center
+  :width: 60%
+
+.. _simple_MeasurementsOptimalPositioningTask33:
+.. image:: scripts/simple_MeasurementsOptimalPositioningTask33.png
+  :align: center
+  :width: 60%
+
 .. ------------------------------------ ..
 .. include:: snippets/Header2Algo06.rst
 
index 327084e318a6d22feace94f7a51e242d01631bd1..dd04765d3291d0b118594d69d593f30c85607bfc 100644 (file)
@@ -94,14 +94,15 @@ physiquement homogènes ou pas.
     - fichier texte de données (TXT, CSV, TSV, DAT), avec pointeur de variable par nom en colonne ou en ligne, indiqué par le mot-clé "*DataFile*" avec la condition ``Vector=True``
     - fichier binaire de données (NPY, NPZ), avec pointeur de variable par nom, indiqué par le mot-clé "*DataFile*" avec la condition ``Vector=True``
 
-- Exemples de déclaration en interface TUI :
+Exemples de déclaration en interface TUI :
+::
 
-    - ``case.setObservation( Vector = [1, 2, 3] )``
-    - ``case.setObservation( Vector = numpy.array([1, 2, 3]) )``
-    - ``case.setObservation( Vector = '1 2 3' )``
-    - ``case.setObservation( Vector=True, Script = 'script.py' )```
-    - ``case.setObservation( Vector=True, DataFile = 'data.csv' )```
-    - ``case.setObservation( Vector=True, DataFile = 'data.npy' )```
+    case.setObservation( Vector = [1, 2, 3] )
+    case.setObservation( Vector = numpy.array([1, 2, 3]) )
+    case.setObservation( Vector = '1 2 3' )
+    case.setObservation( Vector=True, Script = 'script.py' )
+    case.setObservation( Vector=True, DataFile = 'data.csv' )
+    case.setObservation( Vector=True, DataFile = 'data.npy' )
 
 Remarque d'utilisation : dans une étude donnée, seul le dernier enregistrement
 (que ce soit un vecteur unique ou une série de vecteurs) est utilisable, car un
@@ -142,16 +143,17 @@ physiquement homogènes ou pas, dont on considère un historique.
     - fichier texte de données (TXT, CSV, TSV, DAT), avec pointeur de variable par nom en colonne ou en ligne, indiqué par le mot-clé "*DataFile*" avec la condition ``VectorSerie=True``
     - fichier binaire de données (NPY, NPZ), avec pointeur de variable par nom, indiqué par le mot-clé "*DataFile*" avec la condition ``VectorSerie=True``
 
-- Exemples de déclaration en interface TUI :
-
-    - ``case.setObservation( VectorSerie = [[1,2,3], [1,2,3]] )``
-    - ``case.setObservation( VectorSerie = [numpy.array([1,2,3]), numpy.array([1,2,3])] )``
-    - ``case.setObservation( VectorSerie =  ['1 2 3', '1 2 3'] )``
-    - ``case.setObservation( VectorSerie =  '[[1,2,3], [1,2,3]]' )``
-    - ``case.setObservation( VectorSerie =  '1 2 3 ; 1 2 3' )``
-    - ``case.setObservation( VectorSerie=True, Script = 'script.py' )```
-    - ``case.setObservation( VectorSerie=True, DataFile = 'data.csv' )```
-    - ``case.setObservation( VectorSerie=True, DataFile = 'data.npy' )```
+Exemples de déclaration en interface TUI :
+::
+
+    case.setObservation( VectorSerie = [[1,2,3], [1,2,3]] )
+    case.setObservation( VectorSerie = [numpy.array([1,2,3]), numpy.array([1,2,3])] )
+    case.setObservation( VectorSerie =  ['1 2 3', '1 2 3'] )
+    case.setObservation( VectorSerie =  '[[1,2,3], [1,2,3]]' )
+    case.setObservation( VectorSerie =  '1 2 3 ; 1 2 3' )
+    case.setObservation( VectorSerie=True, Script = 'script.py' )
+    case.setObservation( VectorSerie=True, DataFile = 'data.csv' )
+    case.setObservation( VectorSerie=True, DataFile = 'data.npy' )
 
 Remarque d'utilisation : dans une étude donnée, seul le dernier enregistrement
 (que ce soit un vecteur unique ou une série de vecteurs) est utilisable, car un
diff --git a/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.py b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.py
new file mode 100644 (file)
index 0000000..d9fca5a
--- /dev/null
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+#
+import numpy as np
+import matplotlib.pyplot as plt
+np.random.seed(123456789)
+#
+dimension = 20
+#
+print("Définition d'un ensemble artificiel de champs physiques")
+from Models.TwoDimensionalInverseDistanceCS2010 \
+     import TwoDimensionalInverseDistanceCS2010 as Equation
+Eq = Equation(dimension, dimension)
+print()
+#
+print("Recherche des positions optimales de mesure")
+from adao import adaoBuilder
+case = adaoBuilder.New()
+case.setAlgorithmParameters(
+    Algorithm = 'MeasurementsOptimalPositioningTask',
+    Parameters = {
+        "Variant":"DEIM",
+        "SampleAsnUplet":Eq.get_sample_of_mu(15, 15),
+        "MaximumNumberOfLocations":50,
+        "ErrorNorm":"Linf",
+        "ErrorNormTolerance":0.,
+        "StoreSupplementaryCalculations":[
+            "SingularValues",
+            "OptimalPoints",
+            "EnsembleOfSimulations",
+            ],
+        }
+    )
+case.setBackground(Vector = [1,1] )
+case.setObservationOperator(OneFunction = Eq.OneRealisation)
+case.execute()
+#
+print()
+print("Affichage graphique des résultats")
+#
+sp = case.get("EnsembleOfSimulations")[-1]
+x1, x2 = Eq.get_x()
+x1, x2 = np.meshgrid(x1, x2)
+name = "Représentation de quelques snapshots de G sur un total de %i"%sp.shape[1]
+print("  -", name)
+fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, figsize=(6, 6))
+fig.suptitle(name)
+ax.set_xlabel("Position x1", fontweight='bold', color='red')
+ax.set_ylabel("Position x2", fontweight='bold', color='red')
+ax.set_zlabel("Amplitude de G", fontweight='bold', color='red')
+for i in range(sp.shape[1]):
+    if i % 44 != 0: continue
+    ax.plot_surface(x1, x2, sp[:,i].reshape((dimension, dimension)), cmap='coolwarm')
+fig.savefig("simple_MeasurementsOptimalPositioningTask31.png")
+plt.close()
+#
+sv = case.get("SingularValues")[-1]
+name = "Valeurs singulières de l'ensemble des simulations de G"
+print("  -", name)
+fig, ax = plt.subplots(figsize=(6, 6))
+fig.suptitle(name)
+ax.set_xlabel("Index des valeurs singulières, numérotées à partir de 1")
+ax.set_ylabel("Amplitude des valeurs singulières")
+ax.set_xlim(1, len(sv))
+ax.set_yscale("log")
+ax.grid(True)
+ax.plot(range(1, 1 + len(sv)), sv)
+fig.savefig("simple_MeasurementsOptimalPositioningTask32.png")
+plt.tight_layout()
+plt.close()
+#
+nbmax = 15
+op = case.get("OptimalPoints")[-1]
+posx1 = [x1.reshape((-1,))[ip] for ip in op[:nbmax]]
+posx2 = [x2.reshape((-1,))[ip] for ip in op[:nbmax]]
+name = "Ensemble des %i premiers points optimaux de mesure"%nbmax
+Omega = Eq.get_bounds_on_space()
+print("  -", name)
+fig, ax = plt.subplots(figsize=(6, 6))
+fig.suptitle(name)
+ax.set_xlabel("Position x1", fontweight='bold', color='red')
+ax.set_ylabel("Position x2", fontweight='bold', color='red')
+ax.set_xlim(Omega[0][0] - 0.05, Omega[0][1] + 0.05)
+ax.set_ylim(Omega[1][0] - 0.05, Omega[1][1] + 0.05)
+ax.grid(True, which='both', linestyle=(0, (1, 5)), linewidth=0.5)
+ax.plot(posx1, posx2, markersize=6, marker="o", linestyle='')
+for i in range(len(posx1)):
+    ax.text(posx1[i] + 0.005, posx2[i] + 0.01, str(i + 1), fontweight='bold')
+fig.savefig("simple_MeasurementsOptimalPositioningTask33.png")
+plt.tight_layout()
+plt.close()
diff --git a/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.res b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.res
new file mode 100644 (file)
index 0000000..756b5bf
--- /dev/null
@@ -0,0 +1,8 @@
+Définition d'un ensemble artificiel de champs physiques
+
+Recherche des positions optimales de mesure
+
+Affichage graphique des résultats
+  - Représentation de quelques snapshots de G sur un total de 225
+  - Valeurs singulières de l'ensemble des simulations de G
+  - Ensemble des 15 premiers points optimaux de mesure
diff --git a/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.rst b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask3.rst
new file mode 100644 (file)
index 0000000..8225906
--- /dev/null
@@ -0,0 +1,33 @@
+.. index:: single: MeasurementsOptimalPositioningTask (exemple)
+
+Troisième exemple
+.................
+
+Cet exemple plus complet décrit le positionnement optimal de mesures associé à
+une décomposition réduite de type DEIM sur une fonction classique paramétrique
+non-linéaire, telle que proposée dans la référence [Chaturantabut10]_. Cette
+fonction particulière a l'avantage notable de n'être dépendante que d'une
+position :math:`x` en 2D et d'un paramètre :math:`\mu` en 2D aussi. Elle permet
+donc une illustration pédagogique pour représenter les points optimaux de
+mesure.
+
+Cette fonction dépend de la position :math:`x=(x_1,x_2)\in\Omega=[0.1,0.9]^2`
+dans le plan 2D, et du paramètre
+:math:`\mu=(\mu_1,\mu_2)\in\mathcal{D}=[-1,-0.01]^2` de dimension 2 :
+
+.. math:: G(x;\mu) = \frac{1}{\sqrt{(x_1 - \mu_1)^2 + (x_2 - \mu_2)^2 + 0.1^2}}
+
+La fonction est représenté sur une grille spatiale régulière :math:`\Omega_G`
+de taille 20x20 points. Elle est disponible dans les modèles de tests intégrés
+pour ADAO sous le nom `TwoDimensionalInverseDistanceCS2010`. On construit donc
+ici tout d'abord un ensemble de simulations de :math:`G`, pour lui appliquer
+ensuite l'algorithme de décomposition de type DEIM, et en tirer des
+illustrations simples.
+
+On observe ainsi que les valeurs singulières décroissent régulièrement jusqu'au
+bruit numérique, indiquant qu'il faut environ une centaine d'éléments de base
+pour complètement représenter l'information contenue dans l'ensemble des
+simulations de :math:`G`. Par ailleurs, les points de mesure optimaux dans le
+domaine :math:`\Omega_G` sont répartis de manière inhomogène, privilégiant la
+zone spatiale proche du coin :math:`(0.1,0.1)` dans laquelle la fonction
+:math:`G` varie plus.
diff --git a/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask31.png b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask31.png
new file mode 100644 (file)
index 0000000..4e21179
Binary files /dev/null and b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask31.png differ
diff --git a/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask32.png b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask32.png
new file mode 100644 (file)
index 0000000..a84b5bd
Binary files /dev/null and b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask32.png differ
diff --git a/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask33.png b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask33.png
new file mode 100644 (file)
index 0000000..296b6ea
Binary files /dev/null and b/doc/fr/scripts/simple_MeasurementsOptimalPositioningTask33.png differ
diff --git a/src/daComposant/daNumerics/Models/TwoDimensionalInverseDistanceCS2010.py b/src/daComposant/daNumerics/Models/TwoDimensionalInverseDistanceCS2010.py
new file mode 100644 (file)
index 0000000..6df69eb
--- /dev/null
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2024 EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+# Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
+
+import sys, unittest, numpy
+
+# ==============================================================================
+class TwoDimensionalInverseDistanceCS2010:
+    """
+    Two-dimensional inverse distance function of parameter µ=(µ1,µ2):
+
+        s(x,y,µ) = 1 / sqrt( (x-µ1)² + (y-µ2)² + 0.1² )
+
+        avec (x,y) ∈ [0.1,0.9]² et µ=(µ1,µ2) ∈ [-1,-0.01]².
+
+    This is the non-linear parametric function (3.38) of the reference:
+        Chaturantabut, S., Sorensen, D. C.,
+        Nonlinear Model Reduction via Discrete Empirical Interpolation,
+        SIAM Journal on Scientific Computing, 32(5), pp. 2737-2764 (2010).
+    """
+    def __init__(self, nx: int = 20, ny: int = 20):
+        self.nx  = max(1, nx)
+        self.ny  = max(1, ny)
+        self.x   = numpy.linspace(0.1, 0.9, self.nx, dtype=float)
+        self.y   = numpy.linspace(0.1, 0.9, self.ny, dtype=float)
+
+    def FunctionH(self, XX ):
+        __mu1, __mu2 = numpy.ravel(XX)
+        #
+        __x, __y = numpy.meshgrid( self.x, self.y )
+        __sxymu = 1. / numpy.sqrt( (__x - __mu1)**2 + (__y - __mu2)**2 + 0.1**2 )
+        #
+        return __sxymu
+
+    def get_x(self):
+        return self.x, self.y
+
+    def get_sample_of_mu(self, ns1: int = 20, ns2: int = 20):
+        smu1 = numpy.linspace(-1, -0.01, ns1, dtype=float)
+        smu2 = numpy.linspace(-1, -0.01, ns2, dtype=float)
+        smu = numpy.array([(mu1, mu2) for mu1 in smu1 for mu2 in smu2])
+        return smu
+
+    def get_random_sample_of_mu(self, ns1: int = 1, ns2: int = 1):
+        smu = []
+        for i in range(ns1 * ns2):
+            smu1 = numpy.random.uniform(-1, -0.01)
+            smu2 = numpy.random.uniform(-1, -0.01)
+            smu.append((smu1, smu2))
+        smu = numpy.array(smu)
+        return smu
+
+    def get_bounds_on_space(self):
+        return [[min(self.x), max(self.x)], [min(self.y), max(self.y)]]
+
+    def get_bounds_on_parameter(self):
+        return [[-1, -0.01]] * 2
+
+    OneRealisation = FunctionH
+
+# ==============================================================================
+class LocalTest(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        print('\nAUTODIAGNOSTIC\n==============\n')
+        print("    " + TwoDimensionalInverseDistanceCS2010().__doc__.strip())
+
+    def test001(self):
+        numpy.random.seed(123456789)
+        Equation = TwoDimensionalInverseDistanceCS2010()
+        for mu in Equation.get_sample_of_mu(5, 5):
+            solution = Equation.OneRealisation( mu )
+            # Nappe maximale au coin (0,0)
+            self.assertTrue(numpy.max(solution.flat) <= solution[0, 0])
+            # Nappe minimale au coin [-1,-1]
+            self.assertTrue(numpy.min(solution.flat) >= solution[-1, -1])
+
+    def tearDown(cls):
+        print("\n    Tests OK\n")
+
+# ==============================================================================
+if __name__ == "__main__":
+    sys.stderr = sys.stdout
+    unittest.main(verbosity=0)
diff --git a/src/daComposant/daNumerics/Models/__init__.py b/src/daComposant/daNumerics/Models/__init__.py
new file mode 100644 (file)
index 0000000..a32af42
--- /dev/null
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2024 EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library 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 library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+# Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D