1 #-*-coding:iso-8859-1-*-
3 # Copyright (C) 2008-2010 EDF R&D
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.
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.
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
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 Définit les outils généraux élémentaires.
24 Ce module est destiné à etre appelée par AssimilationStudy pour constituer
25 les objets élémentaires de l'algorithme.
27 __author__ = "Jean-Philippe ARGAUD - Mars 2008"
31 import Logging ; Logging.Logging() # A importer en premier
33 from BasicObjects import Operator
35 # ==============================================================================
36 class AssimilationStudy:
38 Cette classe sert d'interface pour l'utilisation de l'assimilation de
39 données. Elle contient les méthodes ou accesseurs nécessaires à la
40 construction d'un calcul d'assimilation.
42 def __init__(self, name=""):
44 Prévoit de conserver l'ensemble des variables nécssaires à un algorithme
45 élémentaire. Ces variables sont ensuite disponibles pour implémenter un
46 algorithme élémentaire particulier.
48 Background............: vecteur Xb
49 Observation...........: vecteur Y (potentiellement temporel)
51 State.................: vecteur d'état dont une partie est le vecteur de
52 contrôle. Cette information n'est utile que si l'on veut faire des
53 calculs sur l'état complet, mais elle n'est pas indispensable pour
55 Control...............: vecteur X contenant toutes les variables de
56 contrôle, i.e. les paramètres ou l'état dont on veut estimer la
57 valeur pour obtenir les observations
58 ObservationOperator...: opérateur d'observation H
60 Les observations présentent une erreur dont la matrice de covariance est
61 R. L'ébauche du vecteur de contrôle présente une erreur dont la matrice
64 self.__name = str(name)
73 self.__X = Persistence.OneVector()
74 self.__Parameters = {}
75 self.__StoredDiagnostics = {}
77 # Variables temporaires
79 self.__algorithmFile = None
80 self.__algorithmName = None
81 self.__diagnosticFile = None
83 # Récupère le chemin du répertoire parent et l'ajoute au path
84 # (Cela complète l'action de la classe PathManagement dans PlatformInfo,
85 # qui est activée dans Persistence)
86 self.__parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
87 sys.path.insert(0, self.__parent)
88 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
90 # ---------------------------------------------------------
91 def setBackground(self,
93 asPersistentVector = None,
97 Permet de définir l'estimation a priori :
98 - asVector : entrée des données, comme un vecteur compatible avec le
99 constructeur de numpy.matrix
100 - asPersistentVector : entrée des données, comme un vecteur de type
101 persistent contruit avec la classe ad-hoc "Persistence"
102 - Scheduler est le contrôle temporel des données
104 if asVector is not None:
105 if type( asVector ) is type( numpy.matrix([]) ):
106 self.__Xb = numpy.matrix( asVector.A1, numpy.float ).T
108 self.__Xb = numpy.matrix( asVector, numpy.float ).T
109 elif asPersistentVector is not None:
110 self.__Xb = asPersistentVector
112 raise ValueError("Error: improperly defined background")
115 def setBackgroundError(self, asCovariance=None):
117 Permet de définir la covariance des erreurs d'ébauche :
118 - asCovariance : entrée des données, comme une matrice compatible avec
119 le constructeur de numpy.matrix
121 self.__B = numpy.matrix( asCovariance, numpy.float )
124 # -----------------------------------------------------------
125 def setObservation(self,
127 asPersistentVector = None,
131 Permet de définir les observations :
132 - asVector : entrée des données, comme un vecteur compatible avec le
133 constructeur de numpy.matrix
134 - asPersistentVector : entrée des données, comme un vecteur de type
135 persistent contruit avec la classe ad-hoc "Persistence"
136 - Scheduler est le contrôle temporel des données disponibles
138 if asVector is not None:
139 if type( asVector ) is type( numpy.matrix([]) ):
140 self.__Y = numpy.matrix( asVector.A1, numpy.float ).T
142 self.__Y = numpy.matrix( asVector, numpy.float ).T
143 elif asPersistentVector is not None:
144 self.__Y = asPersistentVector
146 raise ValueError("Error: improperly defined observations")
149 def setObservationError(self, asCovariance=None):
151 Permet de définir la covariance des erreurs d'observations :
152 - asCovariance : entrée des données, comme une matrice compatible avec
153 le constructeur de numpy.matrix
155 self.__R = numpy.matrix( asCovariance, numpy.float )
158 def setObservationOperator(self,
159 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
164 Permet de définir un opérateur d'observation H. L'ordre de priorité des
165 définitions et leur sens sont les suivants :
166 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
167 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
168 "Direct" n'est pas définie, on prend la fonction "Tangent".
169 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
170 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
171 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
172 matrice fournie doit être sous une forme compatible avec le
173 constructeur de numpy.matrix.
174 - si l'argument "appliedToX" n'est pas None, alors on définit, pour des
175 X divers, l'opérateur par sa valeur appliquée à cet X particulier,
176 sous la forme d'un dictionnaire appliedToX[NAME] avec NAME un nom.
177 L'opérateur doit néanmoins déjà avoir été défini comme d'habitude.
179 if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
180 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
181 self.__H["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
183 self.__H["Direct"] = Operator( fromMethod = asFunction["Direct"] )
184 self.__H["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
185 self.__H["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
186 elif asMatrix is not None:
187 mat = numpy.matrix( asMatrix, numpy.float )
188 self.__H["Direct"] = Operator( fromMatrix = mat )
189 self.__H["Tangent"] = Operator( fromMatrix = mat )
190 self.__H["Adjoint"] = Operator( fromMatrix = mat.T )
192 raise ValueError("Error: improperly defined observation operator")
194 if appliedToX is not None:
195 self.__H["AppliedToX"] = {}
196 if type(appliedToX) is not dict:
197 raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.")
198 for key in appliedToX.keys():
199 if type( appliedToX[key] ) is type( numpy.matrix([]) ):
200 # Pour le cas où l'on a une vraie matrice
201 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T
202 elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1:
203 # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions
204 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T
206 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key], numpy.float ).T
208 self.__H["AppliedToX"] = None
212 # -----------------------------------------------------------
213 def setEvolutionModel(self,
214 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
219 Permet de définir un opérateur d'évolution M. L'ordre de priorité des
220 définitions et leur sens sont les suivants :
221 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
222 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
223 "Direct" n'est pas définie, on prend la fonction "Tangent".
224 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
225 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
226 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
227 matrice fournie doit être sous une forme compatible avec le
228 constructeur de numpy.matrix.
230 if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
231 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
232 self.__M["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
234 self.__M["Direct"] = Operator( fromMethod = asFunction["Direct"] )
235 self.__M["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
236 self.__M["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
237 elif asMatrix is not None:
238 matrice = numpy.matrix( asMatrix, numpy.float )
239 self.__M["Direct"] = Operator( fromMatrix = matrice )
240 self.__M["Tangent"] = Operator( fromMatrix = matrice )
241 self.__M["Adjoint"] = Operator( fromMatrix = matrice.T )
243 raise ValueError("Error: improperly defined evolution operator")
246 def setEvolutionError(self, asCovariance=None):
248 Permet de définir la covariance des erreurs de modèle :
249 - asCovariance : entrée des données, comme une matrice compatible avec
250 le constructeur de numpy.matrix
252 self.__Q = numpy.matrix( asCovariance, numpy.float )
255 # -----------------------------------------------------------
256 def setControls (self, asVector = None ):
258 Permet de définir la valeur initiale du vecteur X contenant toutes les
259 variables de contrôle, i.e. les paramètres ou l'état dont on veut
260 estimer la valeur pour obtenir les observations. C'est utile pour un
261 algorithme itératif/incrémental
262 - asVector : entrée des données, comme un vecteur compatible avec le
263 constructeur de numpy.matrix.
265 if asVector is not None:
266 self.__X.store( asVector )
269 # -----------------------------------------------------------
270 def setAlgorithm(self, choice = None ):
272 Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
273 d'assimilation. L'argument est un champ caractère se rapportant au nom
274 d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération
275 d'assimilation sur les arguments (Xb,Y,H,R,B,Xa).
278 raise ValueError("Error: algorithm choice has to be given")
279 if self.__algorithmName is not None:
280 raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
281 daDirectory = "daAlgorithms"
283 # Recherche explicitement le fichier complet
284 # ------------------------------------------
286 for directory in sys.path:
287 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
288 module_path = os.path.abspath(os.path.join(directory, daDirectory))
289 if module_path is None:
290 raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
292 # Importe le fichier complet comme un module
293 # ------------------------------------------
295 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
296 self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
297 self.__algorithmName = str(choice)
298 sys.path = sys_path_tmp ; del sys_path_tmp
299 except ImportError, e:
300 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
302 # Instancie un objet du type élémentaire du fichier
303 # -------------------------------------------------
304 self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
307 def setAlgorithmParameters(self, asDico=None):
309 Permet de définir les paramètres de l'algorithme, sous la forme d'un
312 self.__Parameters = dict( asDico )
315 # -----------------------------------------------------------
316 def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ):
318 Permet de sélectionner un diagnostic a effectuer.
321 raise ValueError("Error: diagnostic choice has to be given")
322 daDirectory = "daDiagnostics"
324 # Recherche explicitement le fichier complet
325 # ------------------------------------------
327 for directory in sys.path:
328 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
329 module_path = os.path.abspath(os.path.join(directory, daDirectory))
330 if module_path is None:
331 raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
333 # Importe le fichier complet comme un module
334 # ------------------------------------------
336 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
337 self.__diagnosticFile = __import__(str(choice), globals(), locals(), [])
338 sys.path = sys_path_tmp ; del sys_path_tmp
339 except ImportError, e:
340 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
342 # Instancie un objet du type élémentaire du fichier
343 # -------------------------------------------------
344 if self.__StoredDiagnostics.has_key(name):
345 raise ValueError("A diagnostic with the same name already exists")
347 self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic(
351 parameters = parameters )
354 # -----------------------------------------------------------
355 def shape_validate(self):
357 Validation de la correspondance correcte des tailles des variables et
358 des matrices s'il y en a.
360 if self.__Xb is None: __Xb_shape = (0,)
361 elif hasattr(self.__Xb,"shape"):
362 if type(self.__Xb.shape) is tuple: __Xb_shape = self.__Xb.shape
363 else: __Xb_shape = self.__Xb.shape()
364 else: raise TypeError("Xb has no attribute of shape: problem !")
366 if self.__Y is None: __Y_shape = (0,)
367 elif hasattr(self.__Y,"shape"):
368 if type(self.__Y.shape) is tuple: __Y_shape = self.__Y.shape
369 else: __Y_shape = self.__Y.shape()
370 else: raise TypeError("Y has no attribute of shape: problem !")
372 if self.__B is None: __B_shape = (0,0)
373 elif hasattr(self.__B,"shape"):
374 if type(self.__B.shape) is tuple: __B_shape = self.__B.shape
375 else: __B_shape = self.__B.shape()
376 else: raise TypeError("B has no attribute of shape: problem !")
378 if self.__R is None: __R_shape = (0,0)
379 elif hasattr(self.__R,"shape"):
380 if type(self.__R.shape) is tuple: __R_shape = self.__R.shape
381 else: __R_shape = self.__R.shape()
382 else: raise TypeError("R has no attribute of shape: problem !")
384 if self.__Q is None: __Q_shape = (0,0)
385 elif hasattr(self.__Q,"shape"):
386 if type(self.__Q.shape) is tuple: __Q_shape = self.__Q.shape
387 else: __Q_shape = self.__Q.shape()
388 else: raise TypeError("Q has no attribute of shape: problem !")
390 if len(self.__H) == 0: __H_shape = (0,0)
391 elif type(self.__H) is type({}): __H_shape = (0,0)
392 elif hasattr(self.__H["Direct"],"shape"):
393 if type(self.__H["Direct"].shape) is tuple: __H_shape = self.__H["Direct"].shape
394 else: __H_shape = self.__H["Direct"].shape()
395 else: raise TypeError("H has no attribute of shape: problem !")
397 if len(self.__M) == 0: __M_shape = (0,0)
398 elif type(self.__M) is type({}): __M_shape = (0,0)
399 elif hasattr(self.__M["Direct"],"shape"):
400 if type(self.__M["Direct"].shape) is tuple: __M_shape = self.__M["Direct"].shape
401 else: __M_shape = self.__M["Direct"].shape()
402 else: raise TypeError("M has no attribute of shape: problem !")
404 # Vérification des conditions
405 # ---------------------------
406 if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
407 raise ValueError("Shape characteristic of Xb is incorrect: \"%s\""%(__Xb_shape,))
408 if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
409 raise ValueError("Shape characteristic of Y is incorrect: \"%s\""%(__Y_shape,))
411 if not( min(__B_shape) == max(__B_shape) ):
412 raise ValueError("Shape characteristic of B is incorrect: \"%s\""%(__B_shape,))
413 if not( min(__R_shape) == max(__R_shape) ):
414 raise ValueError("Shape characteristic of R is incorrect: \"%s\""%(__R_shape,))
415 if not( min(__Q_shape) == max(__Q_shape) ):
416 raise ValueError("Shape characteristic of Q is incorrect: \"%s\""%(__Q_shape,))
417 if not( min(__M_shape) == max(__M_shape) ):
418 raise ValueError("Shape characteristic of M is incorrect: \"%s\""%(__M_shape,))
420 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[1] == max(__Xb_shape) ):
421 raise ValueError("Shape characteristic of H \"%s\" and X \"%s\" are incompatible"%(__H_shape,__Xb_shape))
422 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[0] == max(__Y_shape) ):
423 raise ValueError("Shape characteristic of H \"%s\" and Y \"%s\" are incompatible"%(__H_shape,__Y_shape))
424 if len(self.__H) > 0 and not(type(self.__H) is type({})) and len(self.__B) > 0 and not( __H_shape[1] == __B_shape[0] ):
425 raise ValueError("Shape characteristic of H \"%s\" and B \"%s\" are incompatible"%(__H_shape,__B_shape))
426 if len(self.__H) > 0 and not(type(self.__H) is type({})) and len(self.__R) > 0 and not( __H_shape[0] == __R_shape[1] ):
427 raise ValueError("Shape characteristic of H \"%s\" and R \"%s\" are incompatible"%(__H_shape,__R_shape))
429 if len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
430 raise ValueError("Shape characteristic of B \"%s\" and Xb \"%s\" are incompatible"%(__B_shape,__Xb_shape))
432 if len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
433 raise ValueError("Shape characteristic of R \"%s\" and Y \"%s\" are incompatible"%(__R_shape,__Y_shape))
435 if len(self.__M) > 0 and not(type(self.__M) is type({})) and not( __M_shape[1] == max(__Xb_shape) ):
436 raise ValueError("Shape characteristic of M \"%s\" and X \"%s\" are incompatible"%(__M_shape,__Xb_shape))
440 # -----------------------------------------------------------
443 Permet de lancer le calcul d'assimilation.
445 Le nom de la méthode à activer est toujours "run". Les paramètres en
446 arguments de la méthode sont fixés. En sortie, on obtient les résultats
447 dans la variable de type dictionnaire "StoredVariables", qui contient en
448 particulier des objets de Persistence pour les analyses, OMA...
450 self.shape_validate()
452 self.__algorithm.run(
460 Par = self.__Parameters,
464 # -----------------------------------------------------------
465 def get(self, key=None):
467 Renvoie les résultats disponibles après l'exécution de la méthode
468 d'assimilation, ou les diagnostics disponibles. Attention, quand un
469 diagnostic porte le même nom qu'une variable stockée, c'est la variable
470 stockée qui est renvoyée, et le diagnostic est inatteignable.
473 if self.__algorithm.has_key(key):
474 return self.__algorithm.get( key )
475 elif self.__StoredDiagnostics.has_key(key):
476 return self.__StoredDiagnostics[key]
478 raise ValueError("The requested key \"%s\" does not exists as a diagnostic or as a stored variable."%key)
480 allvariables = self.__algorithm.get()
481 allvariables.update( self.__StoredDiagnostics )
484 def get_available_variables(self, key=None):
486 Renvoie les variables potentiellement utilisables pour l'étude,
487 identifiés par les chaînes de caractères. L'algorithme doit avoir été
488 préalablement choisi sinon la méthode renvoie "None".
491 if type( self.__algorithm ) is type( {} ):
494 if self.__algorithm.has_key(key):
495 return self.__algorithm.get( key )
497 raise ValueError("The requested key \"%s\" does not exists as a stored variable."%key)
499 variables = self.__algorithm.get().keys()
503 def get_available_algorithms(self):
505 Renvoie la liste des algorithmes potentiellement utilisables, identifiés
506 par les chaînes de caractères.
509 for directory in sys.path:
510 if os.path.isdir(os.path.join(directory,"daAlgorithms")):
511 for fname in os.listdir(os.path.join(directory,"daAlgorithms")):
512 root, ext = os.path.splitext(fname)
513 if ext == '.py' and root != '__init__':
518 def get_available_diagnostics(self):
520 Renvoie la liste des diagnostics potentiellement utilisables, identifiés
521 par les chaînes de caractères.
524 for directory in sys.path:
525 if os.path.isdir(os.path.join(directory,"daDiagnostics")):
526 for fname in os.listdir(os.path.join(directory,"daDiagnostics")):
527 root, ext = os.path.splitext(fname)
528 if ext == '.py' and root != '__init__':
533 # -----------------------------------------------------------
534 def get_algorithms_main_path(self):
536 Renvoie le chemin pour le répertoire principal contenant les algorithmes
537 dans un sous-répertoire "daAlgorithms"
541 def add_algorithms_path(self, asPath=None):
543 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
544 se trouve un sous-répertoire "daAlgorithms"
546 Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est
547 pas indispensable de le rajouter ici.
549 if not os.path.isdir(asPath):
550 raise ValueError("The given "+asPath+" argument must exist as a directory")
551 if not os.path.isdir(os.path.join(asPath,"daAlgorithms")):
552 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"")
553 if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")):
554 raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"")
555 sys.path.insert(0, os.path.abspath(asPath))
556 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
559 def get_diagnostics_main_path(self):
561 Renvoie le chemin pour le répertoire principal contenant les diagnostics
562 dans un sous-répertoire "daDiagnostics"
566 def add_diagnostics_path(self, asPath=None):
568 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
569 se trouve un sous-répertoire "daDiagnostics"
571 Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est
572 pas indispensable de le rajouter ici.
574 if not os.path.isdir(asPath):
575 raise ValueError("The given "+asPath+" argument must exist as a directory")
576 if not os.path.isdir(os.path.join(asPath,"daDiagnostics")):
577 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"")
578 if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")):
579 raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"")
580 sys.path.insert(0, os.path.abspath(asPath))
581 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
584 # -----------------------------------------------------------
585 def setDataObserver(self,
588 HookParameters = None,
592 Permet d'associer un observer à une variable nommée gérée en interne,
593 activable selon des règles définies dans le Scheduler.
596 if type( self.__algorithm ) is dict:
597 raise ValueError("No observer can be build before choosing an algorithm.")
599 # Vérification du nom de variable et typage
600 # -----------------------------------------
601 if type( VariableName ) is str:
602 VariableNames = [VariableName,]
603 elif type( VariableName ) is list:
604 VariableNames = map( str, VariableName )
606 raise ValueError("The observer requires a name or a list of names of variables.")
608 # Association interne de l'observer à la variable
609 # -----------------------------------------------
610 for n in VariableNames:
611 if not self.__algorithm.has_key( n ):
612 raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n)
614 self.__algorithm.StoredVariables[ n ].setDataObserver(
615 Scheduler = Scheduler,
616 HookFunction = HookFunction,
617 HookParameters = HookParameters,
619 def prepare_to_pickle(self):
620 self.__algorithmFile = None
621 self.__diagnosticFile = None
624 # ==============================================================================
625 if __name__ == "__main__":
626 print '\n AUTODIAGNOSTIC \n'
628 ADD = AssimilationStudy("Ma premiere etude BLUE")
630 ADD.setBackground (asVector = [0, 1, 2])
631 ADD.setBackgroundError (asCovariance = "1 0 0;0 1 0;0 0 1")
632 ADD.setObservation (asVector = [0.5, 1.5, 2.5])
633 ADD.setObservationError (asCovariance = "1 0 0;0 1 0;0 0 1")
634 ADD.setObservationOperator(asMatrix = "1 0 0;0 1 0;0 0 1")
636 ADD.setAlgorithm(choice="Blue")
640 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
641 print "Ebauche :", [0, 1, 2]
642 print "Observation :", [0.5, 1.5, 2.5]
643 print "Demi-somme :", list((numpy.array([0, 1, 2])+numpy.array([0.5, 1.5, 2.5]))/2)
644 print " qui doit être identique à :"
645 print "Analyse résultante :", ADD.get("Analysis").valueserie(0)
646 print "Innovation :", ADD.get("Innovation").valueserie(0)
649 print "Algorithmes disponibles :", ADD.get_available_algorithms()
650 # print " Chemin des algorithmes :", ADD.get_algorithms_main_path()
651 print "Diagnostics disponibles :", ADD.get_available_diagnostics()
652 # print " Chemin des diagnostics :", ADD.get_diagnostics_main_path()
655 ADD.setDiagnostic("RMS", "Ma RMS")
657 liste = ADD.get().keys()
659 print "Variables et diagnostics disponibles :", liste
662 def obs(var=None,info=None):
663 print " ---> Mise en oeuvre de l'observer"
664 print " var =",var.valueserie(-1)
666 ADD.setDataObserver( 'Analysis', HookFunction=obs, Scheduler = [2, 4], HookParameters = "Second observer")
667 # Attention, il faut décaler le stockage de 1 pour suivre le pas interne
668 # car le pas 0 correspond à l'analyse ci-dessus.
671 print "Action sur la variable observée, étape :",i
672 ADD.get('Analysis').store( [i, i, i] )