Salome HOME
Adding KalmanFilter and treatment of evolution model
[modules/adao.git] / src / daComposant / daCore / BasicObjects.py
1 #-*-coding:iso-8859-1-*-
2 #
3 #  Copyright (C) 2008-2012 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 __doc__ = """
24     Définit les outils généraux élémentaires.
25     
26     Ce module est destiné à etre appelée par AssimilationStudy pour constituer
27     les objets élémentaires de l'algorithme.
28 """
29 __author__ = "Jean-Philippe ARGAUD"
30
31 import logging
32 import numpy
33 import Persistence
34
35 # ==============================================================================
36 class Operator:
37     """
38     Classe générale d'interface de type opérateur
39     """
40     def __init__(self, fromMethod=None, fromMatrix=None):
41         """
42         On construit un objet de ce type en fournissant à l'aide de l'un des
43         deux mots-clé, soit une fonction python, soit matrice.
44         Arguments :
45         - fromMethod : argument de type fonction Python
46         - fromMatrix : argument adapté au constructeur numpy.matrix
47         """
48         if   fromMethod is not None:
49             self.__Method = fromMethod
50             self.__Matrix = None
51             self.__Type   = "Method"
52         elif fromMatrix is not None:
53             self.__Method = None
54             self.__Matrix = numpy.matrix( fromMatrix, numpy.float )
55             self.__Type   = "Matrix"
56         else:
57             self.__Method = None
58             self.__Matrix = None
59             self.__Type   = None
60
61     def isType(self):
62         return self.__Type
63
64     def appliedTo(self, xValue):
65         """
66         Permet de restituer le résultat de l'application de l'opérateur à un
67         argument xValue. Cette méthode se contente d'appliquer, son argument
68         devant a priori être du bon type.
69         Arguments :
70         - xValue : argument adapté pour appliquer l'opérateur
71         """
72         if self.__Matrix is not None:
73             return self.__Matrix * xValue
74         else:
75             return self.__Method( xValue )
76
77     def appliedInXTo(self, (xNominal, xValue) ):
78         """
79         Permet de restituer le résultat de l'application de l'opérateur à un
80         argument xValue, sachant que l'opérateur est valable en xNominal.
81         Cette méthode se contente d'appliquer, son argument devant a priori
82         être du bon type. Si l'opérateur est linéaire car c'est une matrice,
83         alors il est valable en tout point nominal et il n'est pas nécessaire
84         d'utiliser xNominal.
85         Arguments : une liste contenant
86         - xNominal : argument permettant de donner le point où l'opérateur
87           est construit pour etre ensuite appliqué
88         - xValue : argument adapté pour appliquer l'opérateur
89         """
90         if self.__Matrix is not None:
91             return self.__Matrix * xValue
92         else:
93             return self.__Method( (xNominal, xValue) )
94
95     def asMatrix(self, ValueForMethodForm = "UnknownVoidValue"):
96         """
97         Permet de renvoyer l'opérateur sous la forme d'une matrice
98         """
99         if self.__Matrix is not None:
100             return self.__Matrix
101         elif ValueForMethodForm is not "UnknownVoidValue": # Ne pas utiliser "None"
102             return self.__Method( (ValueForMethodForm, None) )
103         else:
104             raise ValueError("Matrix form of the operator defined as a function/method requires to give an operating point.")
105
106     def shape(self):
107         """
108         Renvoie la taille sous forme numpy si l'opérateur est disponible sous
109         la forme d'une matrice
110         """
111         if self.__Matrix is not None:
112             return self.__Matrix.shape
113         else:
114             raise ValueError("Matrix form of the operator is not available, nor the shape")
115
116 # ==============================================================================
117 class Algorithm:
118     """
119     Classe générale d'interface de type algorithme
120     
121     Elle donne un cadre pour l'écriture d'une classe élémentaire d'algorithme
122     d'assimilation, en fournissant un container (dictionnaire) de variables
123     persistantes initialisées, et des méthodes d'accès à ces variables stockées.
124     
125     Une classe élémentaire d'algorithme doit implémenter la méthode "run".
126     """
127     def __init__(self, name):
128         """
129         L'initialisation présente permet de fabriquer des variables de stockage
130         disponibles de manière générique dans les algorithmes élémentaires. Ces
131         variables de stockage sont ensuite conservées dans un dictionnaire
132         interne à l'objet, mais auquel on accède par la méthode "get".
133         
134         Les variables prévues sont :
135             - CostFunctionJ  : fonction-cout globale, somme des deux parties suivantes
136             - CostFunctionJb : partie ébauche ou background de la fonction-cout
137             - CostFunctionJo : partie observations de la fonction-cout
138             - GradientOfCostFunctionJ  : gradient de la fonction-cout globale
139             - GradientOfCostFunctionJb : gradient de la partie ébauche de la fonction-cout
140             - GradientOfCostFunctionJo : gradient de la partie observations de la fonction-cout
141             - CurrentState : état courant lors d'itérations
142             - Analysis : l'analyse
143             - Innovation : l'innovation : d = Y - H Xb
144             - SigmaObs2 : correction optimale des erreurs d'observation
145             - SigmaBck2 : correction optimale des erreurs d'ébauche
146             - OMA : Observation moins Analysis : Y - Xa
147             - OMB : Observation moins Background : Y - Xb
148             - AMB : Analysis moins Background : Xa - Xb
149             - APosterioriCovariance : matrice A
150         On peut rajouter des variables à stocker dans l'initialisation de
151         l'algorithme élémentaire qui va hériter de cette classe
152         """
153         logging.debug("%s Initialisation"%str(name))
154         self._name = str( name )
155         self._parameters = {}
156         self.__required_parameters = {}
157         self.StoredVariables = {}
158         #
159         self.StoredVariables["CostFunctionJ"]            = Persistence.OneScalar(name = "CostFunctionJ")
160         self.StoredVariables["CostFunctionJb"]           = Persistence.OneScalar(name = "CostFunctionJb")
161         self.StoredVariables["CostFunctionJo"]           = Persistence.OneScalar(name = "CostFunctionJo")
162         self.StoredVariables["GradientOfCostFunctionJ"]  = Persistence.OneVector(name = "GradientOfCostFunctionJ")
163         self.StoredVariables["GradientOfCostFunctionJb"] = Persistence.OneVector(name = "GradientOfCostFunctionJb")
164         self.StoredVariables["GradientOfCostFunctionJo"] = Persistence.OneVector(name = "GradientOfCostFunctionJo")
165         self.StoredVariables["CurrentState"]             = Persistence.OneVector(name = "CurrentState")
166         self.StoredVariables["Analysis"]                 = Persistence.OneVector(name = "Analysis")
167         self.StoredVariables["Innovation"]               = Persistence.OneVector(name = "Innovation")
168         self.StoredVariables["SigmaObs2"]                = Persistence.OneScalar(name = "SigmaObs2")
169         self.StoredVariables["SigmaBck2"]                = Persistence.OneScalar(name = "SigmaBck2")
170         self.StoredVariables["OMA"]                      = Persistence.OneVector(name = "OMA")
171         self.StoredVariables["OMB"]                      = Persistence.OneVector(name = "OMB")
172         self.StoredVariables["BMA"]                      = Persistence.OneVector(name = "BMA")
173         self.StoredVariables["APosterioriCovariance"]    = Persistence.OneMatrix(name = "APosterioriCovariance")
174
175     def get(self, key=None):
176         """
177         Renvoie l'une des variables stockées identifiée par la clé, ou le
178         dictionnaire de l'ensemble des variables disponibles en l'absence de
179         clé. Ce sont directement les variables sous forme objet qui sont
180         renvoyées, donc les méthodes d'accès à l'objet individuel sont celles
181         des classes de persistance.
182         """
183         if key is not None:
184             return self.StoredVariables[key]
185         else:
186             return self.StoredVariables
187
188     def has_key(self, key=None):
189         """
190         Vérifie si l'une des variables stockées est identifiée par la clé.
191         """
192         return self.StoredVariables.has_key(key)
193
194     def keys(self):
195         """
196         Renvoie la liste des clés de variables stockées.
197         """
198         return self.StoredVariables.keys()
199
200     def run(self, Xb=None, Y=None, H=None, M=None, R=None, B=None, Q=None, Parameters=None):
201         """
202         Doit implémenter l'opération élémentaire de calcul d'assimilation sous
203         sa forme mathématique la plus naturelle possible.
204         """
205         raise NotImplementedError("Mathematical assimilation calculation has not been implemented!")
206
207     def defineRequiredParameter(self, name = None, default = None, typecast = None, message = None, minval = None, maxval = None, listval = None):
208         """
209         Permet de définir dans l'algorithme des paramètres requis et leurs
210         caractéristiques par défaut.
211         """
212         if name is None:
213             raise ValueError("A name is mandatory to define a required parameter.")
214         #
215         self.__required_parameters[name] = {
216             "default"  : default,
217             "typecast" : typecast,
218             "minval"   : minval,
219             "maxval"   : maxval,
220             "listval"  : listval,
221             "message"  : message,
222             }
223         logging.debug("%s %s (valeur par défaut = %s)"%(self._name, message, self.setParameterValue(name)))
224
225     def getRequiredParameters(self, noDetails=True):
226         """
227         Renvoie la liste des noms de paramètres requis ou directement le
228         dictionnaire des paramètres requis.
229         """
230         if noDetails:
231             ks = self.__required_parameters.keys()
232             ks.sort()
233             return ks
234         else:
235             return self.__required_parameters
236
237     def setParameterValue(self, name=None, value=None):
238         """
239         Renvoie la valeur d'un paramètre requis de manière contrôlée
240         """
241         default  = self.__required_parameters[name]["default"]
242         typecast = self.__required_parameters[name]["typecast"]
243         minval   = self.__required_parameters[name]["minval"]
244         maxval   = self.__required_parameters[name]["maxval"]
245         listval  = self.__required_parameters[name]["listval"]
246         #
247         if value is None and default is None:
248             __val = None
249         elif value is None and default is not None:
250             if typecast is None: __val = default
251             else:                __val = typecast( default )
252         else:
253             if typecast is None: __val = value
254             else:                __val = typecast( value )
255         #
256         if minval is not None and __val < minval:
257             raise ValueError("The parameter named \"%s\" of value %s can not be less than %s."%(name, __val, minval))
258         if maxval is not None and __val > maxval:
259             raise ValueError("The parameter named \"%s\" of value %s can not be greater than %s."%(name, __val, maxval))
260         if listval is not None and __val not in listval:
261             raise ValueError("The parameter named \"%s\" of value %s has to be in the list %s."%(name, __val, listval))
262         return __val
263
264     def setParameters(self, fromDico={}):
265         """
266         Permet de stocker les paramètres reçus dans le dictionnaire interne.
267         """
268         self._parameters.update( fromDico )
269         for k in self.__required_parameters.keys():
270             if k in fromDico.keys():
271                 self._parameters[k] = self.setParameterValue(k,fromDico[k])
272             else:
273                 self._parameters[k] = self.setParameterValue(k)
274             logging.debug("%s %s : %s"%(self._name, self.__required_parameters[k]["message"], self._parameters[k]))
275
276 # ==============================================================================
277 class Diagnostic:
278     """
279     Classe générale d'interface de type diagnostic
280         
281     Ce template s'utilise de la manière suivante : il sert de classe "patron" en
282     même temps que l'une des classes de persistance, comme "OneScalar" par
283     exemple.
284     
285     Une classe élémentaire de diagnostic doit implémenter ses deux méthodes, la
286     méthode "_formula" pour écrire explicitement et proprement la formule pour
287     l'écriture mathématique du calcul du diagnostic (méthode interne non
288     publique), et "calculate" pour activer la précédente tout en ayant vérifié
289     et préparé les données, et pour stocker les résultats à chaque pas (méthode
290     externe d'activation).
291     """
292     def __init__(self, name = "", parameters = {}):
293         self.name       = str(name)
294         self.parameters = dict( parameters )
295
296     def _formula(self, *args):
297         """
298         Doit implémenter l'opération élémentaire de diagnostic sous sa forme
299         mathématique la plus naturelle possible.
300         """
301         raise NotImplementedError("Diagnostic mathematical formula has not been implemented!")
302
303     def calculate(self, *args):
304         """
305         Active la formule de calcul avec les arguments correctement rangés
306         """
307         raise NotImplementedError("Diagnostic activation method has not been implemented!")
308
309 # ==============================================================================
310 if __name__ == "__main__":
311     print '\n AUTODIAGNOSTIC \n'