1 #-*-coding:iso-8859-1-*-
3 # Copyright (C) 2008-2012 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
21 # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
24 Définit les outils généraux élémentaires.
26 Ce module est destiné à etre appelée par AssimilationStudy pour constituer
27 les objets élémentaires de l'étude.
29 __author__ = "Jean-Philippe ARGAUD"
33 import Logging ; Logging.Logging() # A importer en premier
35 from BasicObjects import Operator
37 # ==============================================================================
38 class AssimilationStudy:
40 Cette classe sert d'interface pour l'utilisation de l'assimilation de
41 données. Elle contient les méthodes ou accesseurs nécessaires à la
42 construction d'un calcul d'assimilation.
44 def __init__(self, name=""):
46 Prévoit de conserver l'ensemble des variables nécssaires à un algorithme
47 élémentaire. Ces variables sont ensuite disponibles pour implémenter un
48 algorithme élémentaire particulier.
50 Background............: vecteur Xb
51 Observation...........: vecteur Y (potentiellement temporel)
53 State.................: vecteur d'état dont une partie est le vecteur de
54 contrôle. Cette information n'est utile que si l'on veut faire des
55 calculs sur l'état complet, mais elle n'est pas indispensable pour
57 Control...............: vecteur X contenant toutes les variables de
58 contrôle, i.e. les paramètres ou l'état dont on veut estimer la
59 valeur pour obtenir les observations
60 ObservationOperator...: opérateur d'observation H
62 Les observations présentent une erreur dont la matrice de covariance est
63 R. L'ébauche du vecteur de contrôle présente une erreur dont la matrice
66 self.__name = str(name)
75 self.__X = Persistence.OneVector()
76 self.__Parameters = {}
77 self.__StoredDiagnostics = {}
78 self.__StoredInputs = {}
80 self.__B_scalar = None
81 self.__R_scalar = None
82 self.__Q_scalar = None
83 self.__Parameters["B_scalar"] = None
84 self.__Parameters["R_scalar"] = None
85 self.__Parameters["Q_scalar"] = None
87 # Variables temporaires
89 self.__algorithmFile = None
90 self.__algorithmName = None
91 self.__diagnosticFile = None
93 # Récupère le chemin du répertoire parent et l'ajoute au path
94 # (Cela complète l'action de la classe PathManagement dans PlatformInfo,
95 # qui est activée dans Persistence)
96 self.__parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
97 sys.path.insert(0, self.__parent)
98 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
100 # ---------------------------------------------------------
101 def setBackground(self,
103 asPersistentVector = None,
108 Permet de définir l'estimation a priori :
109 - asVector : entrée des données, comme un vecteur compatible avec le
110 constructeur de numpy.matrix
111 - asPersistentVector : entrée des données, comme un vecteur de type
112 persistent contruit avec la classe ad-hoc "Persistence"
113 - Scheduler est le contrôle temporel des données
114 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
115 être rendue disponible au même titre que les variables de calcul
117 if asVector is not None:
118 if type( asVector ) is type( numpy.matrix([]) ):
119 self.__Xb = numpy.matrix( asVector.A1, numpy.float ).T
121 self.__Xb = numpy.matrix( asVector, numpy.float ).T
122 elif asPersistentVector is not None:
123 if type(asPersistentVector) in [type([]),type(()),type(numpy.array([])),type(numpy.matrix([]))]:
124 self.__Xb = Persistence.OneVector("Background", basetype=numpy.matrix)
125 for member in asPersistentVector:
126 self.__Xb.store( numpy.matrix( numpy.asarray(member).flatten(), numpy.float ).T )
128 self.__Xb = asPersistentVector
130 raise ValueError("Error: improperly defined background")
132 self.__StoredInputs["Background"] = self.__Xb
135 def setBackgroundError(self,
137 asEyeByScalar = None,
141 Permet de définir la covariance des erreurs d'ébauche :
142 - asCovariance : entrée des données, comme une matrice compatible avec
143 le constructeur de numpy.matrix
144 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
145 multiplicatif d'une matrice de corrélation identité, aucune matrice
146 n'étant donc explicitement à donner
147 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
148 être rendue disponible au même titre que les variables de calcul
150 if asEyeByScalar is not None:
151 self.__B_scalar = float(asEyeByScalar)
154 self.__B_scalar = None
155 self.__B = numpy.matrix( asCovariance, numpy.float )
157 self.__Parameters["B_scalar"] = self.__B_scalar
159 self.__StoredInputs["BackgroundError"] = self.__B
162 # -----------------------------------------------------------
163 def setObservation(self,
165 asPersistentVector = None,
170 Permet de définir les observations :
171 - asVector : entrée des données, comme un vecteur compatible avec le
172 constructeur de numpy.matrix
173 - asPersistentVector : entrée des données, comme un vecteur de type
174 persistent contruit avec la classe ad-hoc "Persistence"
175 - Scheduler est le contrôle temporel des données disponibles
176 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
177 être rendue disponible au même titre que les variables de calcul
179 if asVector is not None:
180 if type( asVector ) is type( numpy.matrix([]) ):
181 self.__Y = numpy.matrix( asVector.A1, numpy.float ).T
183 self.__Y = numpy.matrix( asVector, numpy.float ).T
184 elif asPersistentVector is not None:
185 self.__Y = asPersistentVector
187 raise ValueError("Error: improperly defined observations")
189 self.__StoredInputs["Observation"] = self.__Y
192 def setObservationError(self,
194 asEyeByScalar = None,
198 Permet de définir la covariance des erreurs d'observations :
199 - asCovariance : entrée des données, comme une matrice compatible avec
200 le constructeur de numpy.matrix
201 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
202 multiplicatif d'une matrice de corrélation identité, aucune matrice
203 n'étant donc explicitement à donner
204 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
205 être rendue disponible au même titre que les variables de calcul
207 if asEyeByScalar is not None:
208 self.__R_scalar = float(asEyeByScalar)
211 self.__R_scalar = None
212 self.__R = numpy.matrix( asCovariance, numpy.float )
214 self.__Parameters["R_scalar"] = self.__R_scalar
216 self.__StoredInputs["ObservationError"] = self.__R
219 def setObservationOperator(self,
220 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
226 Permet de définir un opérateur d'observation H. L'ordre de priorité des
227 définitions et leur sens sont les suivants :
228 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
229 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
230 "Direct" n'est pas définie, on prend la fonction "Tangent".
231 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
232 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
233 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
234 matrice fournie doit être sous une forme compatible avec le
235 constructeur de numpy.matrix.
236 - si l'argument "appliedToX" n'est pas None, alors on définit, pour des
237 X divers, l'opérateur par sa valeur appliquée à cet X particulier,
238 sous la forme d'un dictionnaire appliedToX[NAME] avec NAME un nom.
239 L'opérateur doit néanmoins déjà avoir été défini comme d'habitude.
240 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
241 être rendue disponible au même titre que les variables de calcul
243 if (type(asFunction) is type({})) and \
244 asFunction.has_key("Tangent") and asFunction.has_key("Adjoint") and \
245 (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
246 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
247 self.__H["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
249 self.__H["Direct"] = Operator( fromMethod = asFunction["Direct"] )
250 self.__H["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
251 self.__H["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
252 elif asMatrix is not None:
253 mat = numpy.matrix( asMatrix, numpy.float )
254 self.__H["Direct"] = Operator( fromMatrix = mat )
255 self.__H["Tangent"] = Operator( fromMatrix = mat )
256 self.__H["Adjoint"] = Operator( fromMatrix = mat.T )
258 raise ValueError("Error: improperly defined observation operator")
260 if appliedToX is not None:
261 self.__H["AppliedToX"] = {}
262 if type(appliedToX) is not dict:
263 raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.")
264 for key in appliedToX.keys():
265 if type( appliedToX[key] ) is type( numpy.matrix([]) ):
266 # Pour le cas où l'on a une vraie matrice
267 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T
268 elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1:
269 # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions
270 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T
272 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key], numpy.float ).T
274 self.__H["AppliedToX"] = None
277 self.__StoredInputs["ObservationOperator"] = self.__H
280 # -----------------------------------------------------------
281 def setEvolutionModel(self,
282 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
288 Permet de définir un opérateur d'évolution M. L'ordre de priorité des
289 définitions et leur sens sont les suivants :
290 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
291 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
292 "Direct" n'est pas définie, on prend la fonction "Tangent".
293 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
294 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
295 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
296 matrice fournie doit être sous une forme compatible avec le
297 constructeur de numpy.matrix.
298 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
299 être rendue disponible au même titre que les variables de calcul
301 if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
302 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
303 self.__M["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
305 self.__M["Direct"] = Operator( fromMethod = asFunction["Direct"] )
306 self.__M["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
307 self.__M["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
308 elif asMatrix is not None:
309 matrice = numpy.matrix( asMatrix, numpy.float )
310 self.__M["Direct"] = Operator( fromMatrix = matrice )
311 self.__M["Tangent"] = Operator( fromMatrix = matrice )
312 self.__M["Adjoint"] = Operator( fromMatrix = matrice.T )
314 raise ValueError("Error: improperly defined evolution operator")
317 self.__StoredInputs["EvolutionModel"] = self.__M
320 def setEvolutionError(self,
322 asEyeByScalar = None,
326 Permet de définir la covariance des erreurs de modèle :
327 - asCovariance : entrée des données, comme une matrice compatible avec
328 le constructeur de numpy.matrix
329 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
330 multiplicatif d'une matrice de corrélation identité, aucune matrice
331 n'étant donc explicitement à donner
332 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
333 être rendue disponible au même titre que les variables de calcul
335 if asEyeByScalar is not None:
336 self.__Q_scalar = float(asEyeByScalar)
339 self.__Q_scalar = None
340 self.__Q = numpy.matrix( asCovariance, numpy.float )
342 self.__Parameters["Q_scalar"] = self.__Q_scalar
344 self.__StoredInputs["EvolutionError"] = self.__Q
347 # -----------------------------------------------------------
348 def setControls (self,
353 Permet de définir la valeur initiale du vecteur X contenant toutes les
354 variables de contrôle, i.e. les paramètres ou l'état dont on veut
355 estimer la valeur pour obtenir les observations. C'est utile pour un
356 algorithme itératif/incrémental.
357 - asVector : entrée des données, comme un vecteur compatible avec le
358 constructeur de numpy.matrix.
359 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
360 être rendue disponible au même titre que les variables de calcul
362 if asVector is not None:
363 self.__X.store( asVector )
365 self.__StoredInputs["Controls"] = self.__X
368 # -----------------------------------------------------------
369 def setAlgorithm(self, choice = None ):
371 Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
372 d'assimilation. L'argument est un champ caractère se rapportant au nom
373 d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération
374 d'assimilation sur les arguments fixes.
377 raise ValueError("Error: algorithm choice has to be given")
378 if self.__algorithmName is not None:
379 raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
380 daDirectory = "daAlgorithms"
382 # Recherche explicitement le fichier complet
383 # ------------------------------------------
385 for directory in sys.path:
386 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
387 module_path = os.path.abspath(os.path.join(directory, daDirectory))
388 if module_path is None:
389 raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
391 # Importe le fichier complet comme un module
392 # ------------------------------------------
394 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
395 self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
396 self.__algorithmName = str(choice)
397 sys.path = sys_path_tmp ; del sys_path_tmp
398 except ImportError, e:
399 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
401 # Instancie un objet du type élémentaire du fichier
402 # -------------------------------------------------
403 self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
404 self.__StoredInputs["AlgorithmName"] = str(choice)
407 def setAlgorithmParameters(self, asDico=None):
409 Permet de définir les paramètres de l'algorithme, sous la forme d'un
412 if asDico is not None:
413 self.__Parameters.update( dict( asDico ) )
415 self.__StoredInputs["AlgorithmParameters"] = self.__Parameters
418 # -----------------------------------------------------------
419 def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ):
421 Permet de sélectionner un diagnostic a effectuer.
424 raise ValueError("Error: diagnostic choice has to be given")
425 daDirectory = "daDiagnostics"
427 # Recherche explicitement le fichier complet
428 # ------------------------------------------
430 for directory in sys.path:
431 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
432 module_path = os.path.abspath(os.path.join(directory, daDirectory))
433 if module_path is None:
434 raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
436 # Importe le fichier complet comme un module
437 # ------------------------------------------
439 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
440 self.__diagnosticFile = __import__(str(choice), globals(), locals(), [])
441 sys.path = sys_path_tmp ; del sys_path_tmp
442 except ImportError, e:
443 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
445 # Instancie un objet du type élémentaire du fichier
446 # -------------------------------------------------
447 if self.__StoredInputs.has_key(name):
448 raise ValueError("A default input with the same name \"%s\" already exists."%str(name))
449 elif self.__StoredDiagnostics.has_key(name):
450 raise ValueError("A diagnostic with the same name \"%s\" already exists."%str(name))
452 self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic(
456 parameters = parameters )
459 # -----------------------------------------------------------
460 def shape_validate(self):
462 Validation de la correspondance correcte des tailles des variables et
463 des matrices s'il y en a.
465 if self.__Xb is None: __Xb_shape = (0,)
466 elif hasattr(self.__Xb,"shape"):
467 if type(self.__Xb.shape) is tuple: __Xb_shape = self.__Xb.shape
468 else: __Xb_shape = self.__Xb.shape()
469 else: raise TypeError("Xb has no attribute of shape: problem !")
471 if self.__Y is None: __Y_shape = (0,)
472 elif hasattr(self.__Y,"shape"):
473 if type(self.__Y.shape) is tuple: __Y_shape = self.__Y.shape
474 else: __Y_shape = self.__Y.shape()
475 else: raise TypeError("Y has no attribute of shape: problem !")
477 if self.__B is None and self.__B_scalar is None:
479 elif self.__B is None and self.__B_scalar is not None:
480 __B_shape = (max(__Xb_shape),max(__Xb_shape))
481 elif hasattr(self.__B,"shape"):
482 if type(self.__B.shape) is tuple: __B_shape = self.__B.shape
483 else: __B_shape = self.__B.shape()
484 else: raise TypeError("B has no attribute of shape: problem !")
486 if self.__R is None and self.__R_scalar is None:
488 elif self.__R is None and self.__R_scalar is not None:
489 __R_shape = (max(__Y_shape),max(__Y_shape))
490 elif hasattr(self.__R,"shape"):
491 if type(self.__R.shape) is tuple: __R_shape = self.__R.shape
492 else: __R_shape = self.__R.shape()
493 else: raise TypeError("R has no attribute of shape: problem !")
495 if self.__Q is None: __Q_shape = (0,0)
496 elif hasattr(self.__Q,"shape"):
497 if type(self.__Q.shape) is tuple: __Q_shape = self.__Q.shape
498 else: __Q_shape = self.__Q.shape()
499 else: raise TypeError("Q has no attribute of shape: problem !")
501 if len(self.__H) == 0: __H_shape = (0,0)
502 elif type(self.__H) is type({}): __H_shape = (0,0)
503 elif hasattr(self.__H["Direct"],"shape"):
504 if type(self.__H["Direct"].shape) is tuple: __H_shape = self.__H["Direct"].shape
505 else: __H_shape = self.__H["Direct"].shape()
506 else: raise TypeError("H has no attribute of shape: problem !")
508 if len(self.__M) == 0: __M_shape = (0,0)
509 elif type(self.__M) is type({}): __M_shape = (0,0)
510 elif hasattr(self.__M["Direct"],"shape"):
511 if type(self.__M["Direct"].shape) is tuple: __M_shape = self.__M["Direct"].shape
512 else: __M_shape = self.__M["Direct"].shape()
513 else: raise TypeError("M has no attribute of shape: problem !")
515 # Vérification des conditions
516 # ---------------------------
517 if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
518 raise ValueError("Shape characteristic of Xb is incorrect: \"%s\""%(__Xb_shape,))
519 if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
520 raise ValueError("Shape characteristic of Y is incorrect: \"%s\""%(__Y_shape,))
522 if not( min(__B_shape) == max(__B_shape) ):
523 raise ValueError("Shape characteristic of B is incorrect: \"%s\""%(__B_shape,))
524 if not( min(__R_shape) == max(__R_shape) ):
525 raise ValueError("Shape characteristic of R is incorrect: \"%s\""%(__R_shape,))
526 if not( min(__Q_shape) == max(__Q_shape) ):
527 raise ValueError("Shape characteristic of Q is incorrect: \"%s\""%(__Q_shape,))
528 if not( min(__M_shape) == max(__M_shape) ):
529 raise ValueError("Shape characteristic of M is incorrect: \"%s\""%(__M_shape,))
531 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[1] == max(__Xb_shape) ):
532 raise ValueError("Shape characteristic of H \"%s\" and X \"%s\" are incompatible"%(__H_shape,__Xb_shape))
533 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[0] == max(__Y_shape) ):
534 raise ValueError("Shape characteristic of H \"%s\" and Y \"%s\" are incompatible"%(__H_shape,__Y_shape))
535 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] ):
536 raise ValueError("Shape characteristic of H \"%s\" and B \"%s\" are incompatible"%(__H_shape,__B_shape))
537 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] ):
538 raise ValueError("Shape characteristic of H \"%s\" and R \"%s\" are incompatible"%(__H_shape,__R_shape))
540 if self.__B is not None and len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
541 if self.__StoredInputs["AlgorithmName"] in ["EnsembleBlue",]:
542 asPersistentVector = self.__Xb.reshape((-1,min(__B_shape)))
543 self.__Xb = Persistence.OneVector("Background", basetype=numpy.matrix)
544 for member in asPersistentVector:
545 self.__Xb.store( numpy.matrix( numpy.asarray(member).flatten(), numpy.float ).T )
546 __Xb_shape = min(__B_shape)
548 raise ValueError("Shape characteristic of B \"%s\" and Xb \"%s\" are incompatible"%(__B_shape,__Xb_shape))
550 if self.__R is not None and len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
551 raise ValueError("Shape characteristic of R \"%s\" and Y \"%s\" are incompatible"%(__R_shape,__Y_shape))
553 if self.__M is not None and len(self.__M) > 0 and not(type(self.__M) is type({})) and not( __M_shape[1] == max(__Xb_shape) ):
554 raise ValueError("Shape characteristic of M \"%s\" and X \"%s\" are incompatible"%(__M_shape,__Xb_shape))
558 # -----------------------------------------------------------
561 Permet de lancer le calcul d'assimilation.
563 Le nom de la méthode à activer est toujours "run". Les paramètres en
564 arguments de la méthode sont fixés. En sortie, on obtient les résultats
565 dans la variable de type dictionnaire "StoredVariables", qui contient en
566 particulier des objets de Persistence pour les analyses, OMA...
568 self.shape_validate()
570 self.__algorithm.run(
578 Parameters = self.__Parameters,
582 # -----------------------------------------------------------
583 def get(self, key=None):
585 Renvoie les résultats disponibles après l'exécution de la méthode
586 d'assimilation, ou les diagnostics disponibles. Attention, quand un
587 diagnostic porte le même nom qu'une variable stockée, c'est la variable
588 stockée qui est renvoyée, et le diagnostic est inatteignable.
591 if self.__algorithm.has_key(key):
592 return self.__algorithm.get( key )
593 elif self.__StoredInputs.has_key(key):
594 return self.__StoredInputs[key]
595 elif self.__StoredDiagnostics.has_key(key):
596 return self.__StoredDiagnostics[key]
598 raise ValueError("The requested key \"%s\" does not exists as an input, a diagnostic or a stored variable."%key)
600 allvariables = self.__algorithm.get()
601 allvariables.update( self.__StoredDiagnostics )
602 allvariables.update( self.__StoredInputs )
605 def get_available_variables(self):
607 Renvoie les variables potentiellement utilisables pour l'étude,
608 initialement stockées comme données d'entrées ou dans les algorithmes,
609 identifiés par les chaînes de caractères. L'algorithme doit avoir été
610 préalablement choisi sinon la méthode renvoie "None".
612 if len( self.__algorithm.keys()) == 0 and len( self.__StoredInputs.keys() ) == 0:
616 if len( self.__algorithm.keys()) > 0:
617 variables.extend( self.__algorithm.get().keys() )
618 if len( self.__StoredInputs.keys() ) > 0:
619 variables.extend( self.__StoredInputs.keys() )
623 def get_available_algorithms(self):
625 Renvoie la liste des algorithmes potentiellement utilisables, identifiés
626 par les chaînes de caractères.
629 for directory in sys.path:
630 if os.path.isdir(os.path.join(directory,"daAlgorithms")):
631 for fname in os.listdir(os.path.join(directory,"daAlgorithms")):
632 root, ext = os.path.splitext(fname)
633 if ext == '.py' and root != '__init__':
638 def get_available_diagnostics(self):
640 Renvoie la liste des diagnostics potentiellement utilisables, identifiés
641 par les chaînes de caractères.
644 for directory in sys.path:
645 if os.path.isdir(os.path.join(directory,"daDiagnostics")):
646 for fname in os.listdir(os.path.join(directory,"daDiagnostics")):
647 root, ext = os.path.splitext(fname)
648 if ext == '.py' and root != '__init__':
653 # -----------------------------------------------------------
654 def get_algorithms_main_path(self):
656 Renvoie le chemin pour le répertoire principal contenant les algorithmes
657 dans un sous-répertoire "daAlgorithms"
661 def add_algorithms_path(self, asPath=None):
663 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
664 se trouve un sous-répertoire "daAlgorithms"
666 Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est
667 pas indispensable de le rajouter ici.
669 if not os.path.isdir(asPath):
670 raise ValueError("The given "+asPath+" argument must exist as a directory")
671 if not os.path.isdir(os.path.join(asPath,"daAlgorithms")):
672 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"")
673 if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")):
674 raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"")
675 sys.path.insert(0, os.path.abspath(asPath))
676 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
679 def get_diagnostics_main_path(self):
681 Renvoie le chemin pour le répertoire principal contenant les diagnostics
682 dans un sous-répertoire "daDiagnostics"
686 def add_diagnostics_path(self, asPath=None):
688 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
689 se trouve un sous-répertoire "daDiagnostics"
691 Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est
692 pas indispensable de le rajouter ici.
694 if not os.path.isdir(asPath):
695 raise ValueError("The given "+asPath+" argument must exist as a directory")
696 if not os.path.isdir(os.path.join(asPath,"daDiagnostics")):
697 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"")
698 if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")):
699 raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"")
700 sys.path.insert(0, os.path.abspath(asPath))
701 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
704 # -----------------------------------------------------------
705 def setDataObserver(self,
708 HookParameters = None,
712 Permet d'associer un observer à une ou des variables nommées gérées en
713 interne, activable selon des règles définies dans le Scheduler. A chaque
714 pas demandé dans le Scheduler, il effectue la fonction HookFunction avec
715 les arguments (variable persistante VariableName, paramètres HookParameters).
718 if type( self.__algorithm ) is dict:
719 raise ValueError("No observer can be build before choosing an algorithm.")
721 # Vérification du nom de variable et typage
722 # -----------------------------------------
723 if type( VariableName ) is str:
724 VariableNames = [VariableName,]
725 elif type( VariableName ) is list:
726 VariableNames = map( str, VariableName )
728 raise ValueError("The observer requires a name or a list of names of variables.")
730 # Association interne de l'observer à la variable
731 # -----------------------------------------------
732 for n in VariableNames:
733 if not self.__algorithm.has_key( n ):
734 raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n)
736 self.__algorithm.StoredVariables[ n ].setDataObserver(
737 Scheduler = Scheduler,
738 HookFunction = HookFunction,
739 HookParameters = HookParameters,
742 def removeDataObserver(self,
747 Permet de retirer un observer à une ou des variable nommée.
750 if type( self.__algorithm ) is dict:
751 raise ValueError("No observer can be removed before choosing an algorithm.")
753 # Vérification du nom de variable et typage
754 # -----------------------------------------
755 if type( VariableName ) is str:
756 VariableNames = [VariableName,]
757 elif type( VariableName ) is list:
758 VariableNames = map( str, VariableName )
760 raise ValueError("The observer requires a name or a list of names of variables.")
762 # Association interne de l'observer à la variable
763 # -----------------------------------------------
764 for n in VariableNames:
765 if not self.__algorithm.has_key( n ):
766 raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%n)
768 self.__algorithm.StoredVariables[ n ].removeDataObserver(
769 HookFunction = HookFunction,
772 # -----------------------------------------------------------
773 def setDebug(self, level=10):
775 Utiliser par exemple "import logging ; level = logging.DEBUG" avant cet
776 appel pour changer le niveau de verbosité, avec :
777 NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50
780 log = logging.getLogger()
781 log.setLevel( level )
783 def unsetDebug(self):
785 Remet le logger au niveau par défaut
788 log = logging.getLogger()
789 log.setLevel( logging.WARNING )
791 def prepare_to_pickle(self):
792 self.__algorithmFile = None
793 self.__diagnosticFile = None
796 # ==============================================================================
797 if __name__ == "__main__":
798 print '\n AUTODIAGNOSTIC \n'
800 ADD = AssimilationStudy("Ma premiere etude BLUE")
802 ADD.setBackground (asVector = [0, 1, 2])
803 ADD.setBackgroundError (asCovariance = "1 0 0;0 1 0;0 0 1")
804 ADD.setObservation (asVector = [0.5, 1.5, 2.5])
805 ADD.setObservationError (asCovariance = "1 0 0;0 1 0;0 0 1")
806 ADD.setObservationOperator(asMatrix = "1 0 0;0 1 0;0 0 1")
808 ADD.setAlgorithm(choice="Blue")
812 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
813 print "Ebauche :", [0, 1, 2]
814 print "Observation :", [0.5, 1.5, 2.5]
815 print "Demi-somme :", list((numpy.array([0, 1, 2])+numpy.array([0.5, 1.5, 2.5]))/2)
816 print " qui doit être identique à :"
817 print "Analyse résultante :", ADD.get("Analysis").valueserie(0)
818 print "Innovation :", ADD.get("Innovation").valueserie(0)
821 print "Algorithmes disponibles.......................:", ADD.get_available_algorithms()
822 # print " Chemin des algorithmes.....................:", ADD.get_algorithms_main_path()
823 print "Diagnostics types disponibles.................:", ADD.get_available_diagnostics()
824 # print " Chemin des diagnostics.....................:", ADD.get_diagnostics_main_path()
825 print "Variables disponibles.........................:", ADD.get_available_variables()
828 ADD.setDiagnostic("RMS", "Ma RMS")
830 liste = ADD.get().keys()
832 print "Variables et diagnostics nommés disponibles...:", liste
835 print "Exemple de mise en place d'un observeur :"
836 def obs(var=None,info=None):
837 print " ---> Mise en oeuvre de l'observer"
838 print " var =",var.valueserie(-1)
840 ADD.setDataObserver( 'Analysis', HookFunction=obs, Scheduler = [2, 4], HookParameters = "Second observer")
841 # Attention, il faut décaler le stockage de 1 pour suivre le pas interne
842 # car le pas 0 correspond à l'analyse ci-dessus.
845 print "Action sur la variable observée, étape :",i
846 ADD.get('Analysis').store( [i, i, i] )
849 print "Mise en debug et hors debug"
850 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
854 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
856 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()