Salome HOME
Documentation update with features and review corrections
[modules/adao.git] / src / daComposant / daAlgorithms / ObservationSimulationComparisonTest.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2008-2024 EDF R&D
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21 # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
22
23 import numpy, copy, logging
24 from daCore import BasicObjects
25 from daCore.PlatformInfo import PlatformInfo, vfloat
26 mpr = PlatformInfo().MachinePrecision()
27 mfp = PlatformInfo().MaximumPrecision()
28
29 # ==============================================================================
30 class ElementaryAlgorithm(BasicObjects.Algorithm):
31     def __init__(self):
32         BasicObjects.Algorithm.__init__(self, "OBSERVATIONSIMULATIONCOMPARISONTEST")
33         self.defineRequiredParameter(
34             name     = "ShowElementarySummary",
35             default  = True,
36             typecast = bool,
37             message  = "Calcule et affiche un résumé à chaque évaluation élémentaire",
38         )
39         self.defineRequiredParameter(
40             name     = "NumberOfPrintedDigits",
41             default  = 5,
42             typecast = int,
43             message  = "Nombre de chiffres affichés pour les impressions de réels",
44             minval   = 0,
45         )
46         self.defineRequiredParameter(
47             name     = "NumberOfRepetition",
48             default  = 1,
49             typecast = int,
50             message  = "Nombre de fois où l'exécution de la fonction est répétée",
51             minval   = 1,
52         )
53         self.defineRequiredParameter(
54             name     = "ResultTitle",
55             default  = "",
56             typecast = str,
57             message  = "Titre du tableau et de la figure",
58         )
59         self.defineRequiredParameter(
60             name     = "SetDebug",
61             default  = False,
62             typecast = bool,
63             message  = "Activation du mode debug lors de l'exécution",
64         )
65         self.defineRequiredParameter(
66             name     = "StoreSupplementaryCalculations",
67             default  = [],
68             typecast = tuple,
69             message  = "Liste de calculs supplémentaires à stocker et/ou effectuer",
70             listval  = [
71                 "CostFunctionJ",
72                 "CostFunctionJb",
73                 "CostFunctionJo",
74                 "CurrentState",
75                 "Innovation",
76                 "InnovationAtCurrentState",
77                 "OMB",
78                 "SimulatedObservationAtCurrentState",
79             ]
80         )
81         self.requireInputArguments(
82             mandatory= ("Xb", "Y", "HO", "R", "B"),
83         )
84         self.setAttributes(
85             tags=(
86                 "Checking",
87             ),
88             features=(
89                 "DerivativeFree",
90             ),
91         )
92
93     def run(self, Xb=None, Y=None, U=None, HO=None, EM=None, CM=None, R=None, B=None, Q=None, Parameters=None):
94         self._pre_run(Parameters, Xb, Y, U, HO, EM, CM, R, B, Q)
95         #
96         Hm = HO["Direct"].appliedTo
97         #
98         X0 = copy.copy( Xb )
99         Y0 = copy.copy( Y )
100         #
101         # ----------
102         if len(self._parameters["StoreSupplementaryCalculations"]) > 0:
103             BI = B.getI()
104             RI = R.getI()
105
106             def CostFunction(x, HmX):
107                 _X  = numpy.ravel(  x  )
108                 _HX = numpy.ravel( HmX )
109                 _X0 = numpy.ravel( X0 )
110                 _Y0 = numpy.ravel( Y0 )
111                 Jb  = vfloat( 0.5 *  (_X - _X0).T * (BI * (_X - _X0))  )  # noqa: E222
112                 Jo  = vfloat( 0.5 * (_Y0 - _HX).T * (RI * (_Y0 - _HX)) )
113                 J   = Jb + Jo
114                 self.StoredVariables["CostFunctionJb"].store( Jb )
115                 self.StoredVariables["CostFunctionJo"].store( Jo )
116                 self.StoredVariables["CostFunctionJ" ].store( J )
117                 return J, Jb, Jo
118         # ----------
119         __s = self._parameters["ShowElementarySummary"]
120         __p = self._parameters["NumberOfPrintedDigits"]
121         __r = self._parameters["NumberOfRepetition"]
122         #
123         __marge = 5 * u" "
124         __flech = 3 * "=" + "> "
125         msgs  = ("\n")  # 1
126         if len(self._parameters["ResultTitle"]) > 0:
127             __rt = str(self._parameters["ResultTitle"])
128             msgs += (__marge + "====" + "=" * len(__rt) + "====\n")
129             msgs += (__marge + "    " + __rt + "\n")
130             msgs += (__marge + "====" + "=" * len(__rt) + "====\n")
131         else:
132             msgs += (__marge + "%s\n"%self._name)
133             msgs += (__marge + "%s\n"%("=" * len(self._name),))
134         #
135         msgs += ("\n")
136         msgs += (__marge + "This test allows to analyze the (repetition of the) launch of some\n")
137         msgs += (__marge + "given simulation operator F, applied to one single vector argument x,\n")
138         msgs += (__marge + "and its comparison to observations or measures y through the innovation\n")
139         msgs += (__marge + "difference OMB = y - F(x) (Observation minus evaluation at Background)\n")
140         msgs += (__marge + "and (if required) the data assimilation standard cost function J.\n")
141         msgs += (__marge + "The output shows simple statistics related to its successful execution,\n")
142         msgs += (__marge + "or related to the similarities of repetition of its execution.\n")
143         msgs += ("\n")
144         msgs += (__flech + "Information before launching:\n")
145         msgs += (__marge + "-----------------------------\n")
146         msgs += ("\n")
147         msgs += (__marge + "Characteristics of input vector X, internally converted:\n")
148         msgs += (__marge + "  Type...............: %s\n")%type( X0 )
149         msgs += (__marge + "  Length of vector...: %i\n")%max(numpy.ravel( X0 ).shape)
150         msgs += (__marge + "  Minimum value......: %." + str(__p) + "e\n")%numpy.min(  X0 )
151         msgs += (__marge + "  Maximum value......: %." + str(__p) + "e\n")%numpy.max(  X0 )
152         msgs += (__marge + "  Mean of vector.....: %." + str(__p) + "e\n")%numpy.mean( X0, dtype=mfp )
153         msgs += (__marge + "  Standard error.....: %." + str(__p) + "e\n")%numpy.std(  X0, dtype=mfp )
154         msgs += (__marge + "  L2 norm of vector..: %." + str(__p) + "e\n")%numpy.linalg.norm( X0 )
155         msgs += ("\n")
156         msgs += (__marge + "Characteristics of input vector of observations Yobs, internally converted:\n")
157         msgs += (__marge + "  Type...............: %s\n")%type( Y0 )
158         msgs += (__marge + "  Length of vector...: %i\n")%max(numpy.ravel( Y0 ).shape)
159         msgs += (__marge + "  Minimum value......: %." + str(__p) + "e\n")%numpy.min(  Y0 )
160         msgs += (__marge + "  Maximum value......: %." + str(__p) + "e\n")%numpy.max(  Y0 )
161         msgs += (__marge + "  Mean of vector.....: %." + str(__p) + "e\n")%numpy.mean( Y0, dtype=mfp )
162         msgs += (__marge + "  Standard error.....: %." + str(__p) + "e\n")%numpy.std(  Y0, dtype=mfp )
163         msgs += (__marge + "  L2 norm of vector..: %." + str(__p) + "e\n")%numpy.linalg.norm( Y0 )
164         msgs += ("\n")
165         msgs += (__marge + "%s\n\n"%("-" * 75,))
166         #
167         if self._parameters["SetDebug"]:
168             CUR_LEVEL = logging.getLogger().getEffectiveLevel()
169             logging.getLogger().setLevel(logging.DEBUG)
170             if __r > 1:
171                 msgs += (__flech + "Beginning of repeated evaluation, activating debug\n")
172             else:
173                 msgs += (__flech + "Beginning of evaluation, activating debug\n")
174         else:
175             if __r > 1:
176                 msgs += (__flech + "Beginning of repeated evaluation, without activating debug\n")
177             else:
178                 msgs += (__flech + "Beginning of evaluation, without activating debug\n")
179         print(msgs)  # 1
180         #
181         # ----------
182         HO["Direct"].disableAvoidingRedundancy()
183         # ----------
184         Ys = []
185         Ds = []
186         Js = []
187         _Y0 = numpy.ravel( Y0 )
188         for i in range(__r):
189             if self._toStore("CurrentState"):
190                 self.StoredVariables["CurrentState"].store( X0 )
191             if __s:
192                 msgs  = (__marge + "%s\n"%("-" * 75,))  # 2-1
193                 if __r > 1:
194                     msgs += ("\n")
195                     msgs += (__flech + "Repetition step number %i on a total of %i\n"%(i + 1, __r))
196                 msgs += ("\n")
197                 msgs += (__flech + "Launching operator sequential evaluation\n")
198                 print(msgs)  # 2-1
199             #
200             Yn = Hm( X0 )
201             #
202             if _Y0.size != Yn.size:
203                 raise ValueError("The size %i of observations Y and %i of observed calculation F(X) are different, they have to be identical."%(Y0.size, Yn.size))  # noqa: E501
204             #
205             Dn = _Y0 - numpy.ravel( Yn )
206             #
207             if len(self._parameters["StoreSupplementaryCalculations"]) > 0:
208                 J, Jb, Jo = CostFunction( X0, Yn )
209                 if self._toStore("CostFunctionJ"):
210                     Js.append( J )
211             if __s:
212                 msgs  = ("\n")  # 2-2
213                 msgs += (__flech + "End of operator sequential evaluation\n")
214                 msgs += ("\n")
215                 msgs += (__flech + "Information after evaluation:\n")
216                 msgs += ("\n")
217                 msgs += (__marge + "Characteristics of simulated output vector Y=F(X), to compare to others:\n")
218                 msgs += (__marge + "  Type...............: %s\n")%type( Yn )
219                 msgs += (__marge + "  Length of vector...: %i\n")%max(numpy.ravel( Yn ).shape)
220                 msgs += (__marge + "  Minimum value......: %." + str(__p) + "e\n")%numpy.min(  Yn )
221                 msgs += (__marge + "  Maximum value......: %." + str(__p) + "e\n")%numpy.max(  Yn )
222                 msgs += (__marge + "  Mean of vector.....: %." + str(__p) + "e\n")%numpy.mean( Yn, dtype=mfp )
223                 msgs += (__marge + "  Standard error.....: %." + str(__p) + "e\n")%numpy.std(  Yn, dtype=mfp )
224                 msgs += (__marge + "  L2 norm of vector..: %." + str(__p) + "e\n")%numpy.linalg.norm( Yn )
225                 msgs += ("\n")
226                 msgs += (__marge + "Characteristics of OMB differences between observations Yobs and simulated output vector Y=F(X):\n")  # noqa: E501
227                 msgs += (__marge + "  Type...............: %s\n")%type( Dn )
228                 msgs += (__marge + "  Length of vector...: %i\n")%max(numpy.ravel( Dn ).shape)
229                 msgs += (__marge + "  Minimum value......: %." + str(__p) + "e\n")%numpy.min(  Dn )
230                 msgs += (__marge + "  Maximum value......: %." + str(__p) + "e\n")%numpy.max(  Dn )
231                 msgs += (__marge + "  Mean of vector.....: %." + str(__p) + "e\n")%numpy.mean( Dn, dtype=mfp )
232                 msgs += (__marge + "  Standard error.....: %." + str(__p) + "e\n")%numpy.std(  Dn, dtype=mfp )
233                 msgs += (__marge + "  L2 norm of vector..: %." + str(__p) + "e\n")%numpy.linalg.norm( Dn )
234                 if len(self._parameters["StoreSupplementaryCalculations"]) > 0:
235                     if self._toStore("CostFunctionJ"):
236                         msgs += ("\n")
237                         msgs += (__marge + "  Cost function J....: %." + str(__p) + "e\n")%J
238                         msgs += (__marge + "  Cost function Jb...: %." + str(__p) + "e\n")%Jb
239                         msgs += (__marge + "  Cost function Jo...: %." + str(__p) + "e\n")%Jo
240                         msgs += (__marge + "  (Remark: the Jb background part of the cost function J is zero by hypothesis)\n")  # noqa: E501
241                 print(msgs)  # 2-2
242             if self._toStore("SimulatedObservationAtCurrentState"):
243                 self.StoredVariables["SimulatedObservationAtCurrentState"].store( numpy.ravel(Yn) )
244             if self._toStore("Innovation"):
245                 self.StoredVariables["Innovation"].store( Dn )
246             if self._toStore("OMB"):
247                 self.StoredVariables["OMB"].store( Dn )
248             if self._toStore("InnovationAtCurrentState"):
249                 self.StoredVariables["InnovationAtCurrentState"].store( Dn )
250             #
251             Ys.append( copy.copy( numpy.ravel(
252                 Yn
253             ) ) )
254             Ds.append( copy.copy( numpy.ravel(
255                 Dn
256             ) ) )
257         # ----------
258         HO["Direct"].enableAvoidingRedundancy()
259         # ----------
260         #
261         msgs  = (__marge + "%s\n\n"%("-" * 75,))  # 3
262         if self._parameters["SetDebug"]:
263             if __r > 1:
264                 msgs += (__flech + "End of repeated evaluation, deactivating debug if necessary\n")
265             else:
266                 msgs += (__flech + "End of evaluation, deactivating debug if necessary\n")
267             logging.getLogger().setLevel(CUR_LEVEL)
268         else:
269             if __r > 1:
270                 msgs += (__flech + "End of repeated evaluation, without deactivating debug\n")
271             else:
272                 msgs += (__flech + "End of evaluation, without deactivating debug\n")
273         msgs += ("\n")
274         msgs += (__marge + "%s\n"%("-" * 75,))
275         #
276         if __r > 1:
277             msgs += ("\n")
278             msgs += (__flech + "Launching statistical summary calculation for %i states\n"%__r)
279             msgs += ("\n")
280             msgs += (__marge + "%s\n"%("-" * 75,))
281             msgs += ("\n")
282             msgs += (__flech + "Statistical analysis of the outputs obtained through sequential repeated evaluations\n")  # noqa: E501
283             msgs += ("\n")
284             msgs += (__marge + "(Remark: numbers that are (about) under %.0e represent 0 to machine precision)\n"%mpr)  # noqa: E501
285             msgs += ("\n")
286             Yy = numpy.array( Ys )
287             msgs += (__marge + "Number of evaluations...........................: %i\n")%len( Ys )
288             msgs += ("\n")
289             msgs += (__marge + "Characteristics of the whole set of outputs Y:\n")
290             msgs += (__marge + "  Size of each of the outputs...................: %i\n")%Ys[0].size
291             msgs += (__marge + "  Minimum value of the whole set of outputs.....: %." + str(__p) + "e\n")%numpy.min(  Yy )  # noqa: E501
292             msgs += (__marge + "  Maximum value of the whole set of outputs.....: %." + str(__p) + "e\n")%numpy.max(  Yy )  # noqa: E501
293             msgs += (__marge + "  Mean of vector of the whole set of outputs....: %." + str(__p) + "e\n")%numpy.mean( Yy, dtype=mfp )  # noqa: E501
294             msgs += (__marge + "  Standard error of the whole set of outputs....: %." + str(__p) + "e\n")%numpy.std(  Yy, dtype=mfp )  # noqa: E501
295             msgs += ("\n")
296             Ym = numpy.mean( numpy.array( Ys ), axis=0, dtype=mfp )
297             msgs += (__marge + "Characteristics of the vector Ym, mean of the outputs Y:\n")
298             msgs += (__marge + "  Size of the mean of the outputs...............: %i\n")%Ym.size
299             msgs += (__marge + "  Minimum value of the mean of the outputs......: %." + str(__p) + "e\n")%numpy.min(  Ym )  # noqa: E501
300             msgs += (__marge + "  Maximum value of the mean of the outputs......: %." + str(__p) + "e\n")%numpy.max(  Ym )  # noqa: E501
301             msgs += (__marge + "  Mean of the mean of the outputs...............: %." + str(__p) + "e\n")%numpy.mean( Ym, dtype=mfp )  # noqa: E501
302             msgs += (__marge + "  Standard error of the mean of the outputs.....: %." + str(__p) + "e\n")%numpy.std(  Ym, dtype=mfp )  # noqa: E501
303             msgs += ("\n")
304             Ye = numpy.mean( numpy.array( Ys ) - Ym, axis=0, dtype=mfp )
305             msgs += (__marge + "Characteristics of the mean of the differences between the outputs Y and their mean Ym:\n")  # noqa: E501
306             msgs += (__marge + "  Size of the mean of the differences...........: %i\n")%Ye.size
307             msgs += (__marge + "  Minimum value of the mean of the differences..: %." + str(__p) + "e\n")%numpy.min(  Ye )  # noqa: E501
308             msgs += (__marge + "  Maximum value of the mean of the differences..: %." + str(__p) + "e\n")%numpy.max(  Ye )  # noqa: E501
309             msgs += (__marge + "  Mean of the mean of the differences...........: %." + str(__p) + "e\n")%numpy.mean( Ye, dtype=mfp )  # noqa: E501
310             msgs += (__marge + "  Standard error of the mean of the differences.: %." + str(__p) + "e\n")%numpy.std(  Ye, dtype=mfp )  # noqa: E501
311             msgs += ("\n")
312             msgs += (__marge + "%s\n"%("-" * 75,))
313             msgs += ("\n")
314             msgs += (__flech + "Statistical analysis of the OMB differences obtained through sequential repeated evaluations\n")  # noqa: E501
315             msgs += ("\n")
316             msgs += (__marge + "(Remark: numbers that are (about) under %.0e represent 0 to machine precision)\n"%mpr)  # noqa: E501
317             msgs += ("\n")
318             Dy = numpy.array( Ds )
319             msgs += (__marge + "Number of evaluations...........................: %i\n")%len( Ds )
320             msgs += ("\n")
321             msgs += (__marge + "Characteristics of the whole set of OMB differences:\n")
322             msgs += (__marge + "  Size of each of the outputs...................: %i\n")%Ds[0].size
323             msgs += (__marge + "  Minimum value of the whole set of differences.: %." + str(__p) + "e\n")%numpy.min(  Dy )  # noqa: E501
324             msgs += (__marge + "  Maximum value of the whole set of differences.: %." + str(__p) + "e\n")%numpy.max(  Dy )  # noqa: E501
325             msgs += (__marge + "  Mean of vector of the whole set of differences: %." + str(__p) + "e\n")%numpy.mean( Dy, dtype=mfp )  # noqa: E501
326             msgs += (__marge + "  Standard error of the whole set of differences: %." + str(__p) + "e\n")%numpy.std(  Dy, dtype=mfp )  # noqa: E501
327             msgs += ("\n")
328             Dm = numpy.mean( numpy.array( Ds ), axis=0, dtype=mfp )
329             msgs += (__marge + "Characteristics of the vector Dm, mean of the OMB differences:\n")
330             msgs += (__marge + "  Size of the mean of the differences...........: %i\n")%Dm.size
331             msgs += (__marge + "  Minimum value of the mean of the differences..: %." + str(__p) + "e\n")%numpy.min(  Dm )  # noqa: E501
332             msgs += (__marge + "  Maximum value of the mean of the differences..: %." + str(__p) + "e\n")%numpy.max(  Dm )  # noqa: E501
333             msgs += (__marge + "  Mean of the mean of the differences...........: %." + str(__p) + "e\n")%numpy.mean( Dm, dtype=mfp )  # noqa: E501
334             msgs += (__marge + "  Standard error of the mean of the differences.: %." + str(__p) + "e\n")%numpy.std(  Dm, dtype=mfp )  # noqa: E501
335             msgs += ("\n")
336             De = numpy.mean( numpy.array( Ds ) - Dm, axis=0, dtype=mfp )
337             msgs += (__marge + "Characteristics of the mean of the differences between the OMB differences and their mean Dm:\n")  # noqa: E501
338             msgs += (__marge + "  Size of the mean of the differences...........: %i\n")%De.size
339             msgs += (__marge + "  Minimum value of the mean of the differences..: %." + str(__p) + "e\n")%numpy.min( De )  # noqa: E501
340             msgs += (__marge + "  Maximum value of the mean of the differences..: %." + str(__p) + "e\n")%numpy.max( De )  # noqa: E501
341             msgs += (__marge + "  Mean of the mean of the differences...........: %." + str(__p) + "e\n")%numpy.mean( De, dtype=mfp )  # noqa: E501
342             msgs += (__marge + "  Standard error of the mean of the differences.: %." + str(__p) + "e\n")%numpy.std( De, dtype=mfp )  # noqa: E501
343             #
344             if self._toStore("CostFunctionJ"):
345                 msgs += ("\n")
346                 Jj = numpy.array( Js )
347                 msgs += (__marge + "%s\n\n"%("-" * 75,))
348                 msgs += (__flech + "Statistical analysis of the cost function J values obtained through sequential repeated evaluations\n")  # noqa: E501
349                 msgs += ("\n")
350                 msgs += (__marge + "Number of evaluations...........................: %i\n")%len( Js )
351                 msgs += ("\n")
352                 msgs += (__marge + "Characteristics of the whole set of data assimilation cost function J values:\n")
353                 msgs += (__marge + "  Minimum value of the whole set of J...........: %." + str(__p) + "e\n")%numpy.min(  Jj )  # noqa: E501
354                 msgs += (__marge + "  Maximum value of the whole set of J...........: %." + str(__p) + "e\n")%numpy.max(  Jj )  # noqa: E501
355                 msgs += (__marge + "  Mean of vector of the whole set of J..........: %." + str(__p) + "e\n")%numpy.mean( Jj, dtype=mfp )  # noqa: E501
356                 msgs += (__marge + "  Standard error of the whole set of J..........: %." + str(__p) + "e\n")%numpy.std(  Jj, dtype=mfp )  # noqa: E501
357                 msgs += (__marge + "  (Remark: variations of the cost function J only come from the observation part Jo of J)\n")  # noqa: E501
358             msgs += ("\n")
359             msgs += (__marge + "%s\n"%("-" * 75,))
360         #
361         msgs += ("\n")
362         msgs += (__marge + "End of the \"%s\" verification\n\n"%self._name)
363         msgs += (__marge + "%s\n"%("-" * 75,))
364         print(msgs)  # 3
365         #
366         self._post_run(HO, EM)
367         return 0
368
369 # ==============================================================================
370 if __name__ == "__main__":
371     print("\n AUTODIAGNOSTIC\n")