1 #-*-coding:iso-8859-1-*-
3 # Copyright (C) 2008-2011 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 self.__Xb = asPersistentVector
125 raise ValueError("Error: improperly defined background")
127 self.__StoredInputs["Background"] = self.__Xb
130 def setBackgroundError(self,
132 asEyeByScalar = None,
136 Permet de définir la covariance des erreurs d'ébauche :
137 - asCovariance : entrée des données, comme une matrice compatible avec
138 le constructeur de numpy.matrix
139 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
140 multiplicatif d'une matrice de corrélation identité, aucune matrice
141 n'étant donc explicitement à donner
142 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
143 être rendue disponible au même titre que les variables de calcul
145 if asEyeByScalar is not None:
146 self.__B_scalar = float(asEyeByScalar)
149 self.__B_scalar = None
150 self.__B = numpy.matrix( asCovariance, numpy.float )
152 self.__Parameters["B_scalar"] = self.__B_scalar
154 self.__StoredInputs["BackgroundError"] = self.__B
157 # -----------------------------------------------------------
158 def setObservation(self,
160 asPersistentVector = None,
165 Permet de définir les observations :
166 - asVector : entrée des données, comme un vecteur compatible avec le
167 constructeur de numpy.matrix
168 - asPersistentVector : entrée des données, comme un vecteur de type
169 persistent contruit avec la classe ad-hoc "Persistence"
170 - Scheduler est le contrôle temporel des données disponibles
171 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
172 être rendue disponible au même titre que les variables de calcul
174 if asVector is not None:
175 if type( asVector ) is type( numpy.matrix([]) ):
176 self.__Y = numpy.matrix( asVector.A1, numpy.float ).T
178 self.__Y = numpy.matrix( asVector, numpy.float ).T
179 elif asPersistentVector is not None:
180 self.__Y = asPersistentVector
182 raise ValueError("Error: improperly defined observations")
184 self.__StoredInputs["Observation"] = self.__Y
187 def setObservationError(self,
189 asEyeByScalar = None,
193 Permet de définir la covariance des erreurs d'observations :
194 - asCovariance : entrée des données, comme une matrice compatible avec
195 le constructeur de numpy.matrix
196 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
197 multiplicatif d'une matrice de corrélation identité, aucune matrice
198 n'étant donc explicitement à donner
199 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
200 être rendue disponible au même titre que les variables de calcul
202 if asEyeByScalar is not None:
203 self.__R_scalar = float(asEyeByScalar)
206 self.__R_scalar = None
207 self.__R = numpy.matrix( asCovariance, numpy.float )
209 self.__Parameters["R_scalar"] = self.__R_scalar
211 self.__StoredInputs["ObservationError"] = self.__R
214 def setObservationOperator(self,
215 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
221 Permet de définir un opérateur d'observation H. L'ordre de priorité des
222 définitions et leur sens sont les suivants :
223 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
224 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
225 "Direct" n'est pas définie, on prend la fonction "Tangent".
226 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
227 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
228 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
229 matrice fournie doit être sous une forme compatible avec le
230 constructeur de numpy.matrix.
231 - si l'argument "appliedToX" n'est pas None, alors on définit, pour des
232 X divers, l'opérateur par sa valeur appliquée à cet X particulier,
233 sous la forme d'un dictionnaire appliedToX[NAME] avec NAME un nom.
234 L'opérateur doit néanmoins déjà avoir été défini comme d'habitude.
235 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
236 être rendue disponible au même titre que les variables de calcul
238 if (type(asFunction) is type({})) and \
239 asFunction.has_key("Tangent") and asFunction.has_key("Adjoint") and \
240 (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
241 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
242 self.__H["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
244 self.__H["Direct"] = Operator( fromMethod = asFunction["Direct"] )
245 self.__H["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
246 self.__H["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
247 elif asMatrix is not None:
248 mat = numpy.matrix( asMatrix, numpy.float )
249 self.__H["Direct"] = Operator( fromMatrix = mat )
250 self.__H["Tangent"] = Operator( fromMatrix = mat )
251 self.__H["Adjoint"] = Operator( fromMatrix = mat.T )
253 raise ValueError("Error: improperly defined observation operator")
255 if appliedToX is not None:
256 self.__H["AppliedToX"] = {}
257 if type(appliedToX) is not dict:
258 raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.")
259 for key in appliedToX.keys():
260 if type( appliedToX[key] ) is type( numpy.matrix([]) ):
261 # Pour le cas où l'on a une vraie matrice
262 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T
263 elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1:
264 # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions
265 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T
267 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key], numpy.float ).T
269 self.__H["AppliedToX"] = None
272 self.__StoredInputs["ObservationOperator"] = self.__H
275 # -----------------------------------------------------------
276 def setEvolutionModel(self,
277 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
283 Permet de définir un opérateur d'évolution M. L'ordre de priorité des
284 définitions et leur sens sont les suivants :
285 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
286 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
287 "Direct" n'est pas définie, on prend la fonction "Tangent".
288 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
289 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
290 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
291 matrice fournie doit être sous une forme compatible avec le
292 constructeur de numpy.matrix.
293 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
294 être rendue disponible au même titre que les variables de calcul
296 if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
297 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
298 self.__M["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
300 self.__M["Direct"] = Operator( fromMethod = asFunction["Direct"] )
301 self.__M["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
302 self.__M["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
303 elif asMatrix is not None:
304 matrice = numpy.matrix( asMatrix, numpy.float )
305 self.__M["Direct"] = Operator( fromMatrix = matrice )
306 self.__M["Tangent"] = Operator( fromMatrix = matrice )
307 self.__M["Adjoint"] = Operator( fromMatrix = matrice.T )
309 raise ValueError("Error: improperly defined evolution operator")
312 self.__StoredInputs["EvolutionModel"] = self.__M
315 def setEvolutionError(self,
317 asEyeByScalar = None,
321 Permet de définir la covariance des erreurs de modèle :
322 - asCovariance : entrée des données, comme une matrice compatible avec
323 le constructeur de numpy.matrix
324 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
325 multiplicatif d'une matrice de corrélation identité, aucune matrice
326 n'étant donc explicitement à donner
327 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
328 être rendue disponible au même titre que les variables de calcul
330 if asEyeByScalar is not None:
331 self.__Q_scalar = float(asEyeByScalar)
334 self.__Q_scalar = None
335 self.__Q = numpy.matrix( asCovariance, numpy.float )
337 self.__Parameters["Q_scalar"] = self.__Q_scalar
339 self.__StoredInputs["EvolutionError"] = self.__Q
342 # -----------------------------------------------------------
343 def setControls (self,
348 Permet de définir la valeur initiale du vecteur X contenant toutes les
349 variables de contrôle, i.e. les paramètres ou l'état dont on veut
350 estimer la valeur pour obtenir les observations. C'est utile pour un
351 algorithme itératif/incrémental.
352 - asVector : entrée des données, comme un vecteur compatible avec le
353 constructeur de numpy.matrix.
354 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
355 être rendue disponible au même titre que les variables de calcul
357 if asVector is not None:
358 self.__X.store( asVector )
360 self.__StoredInputs["Controls"] = self.__X
363 # -----------------------------------------------------------
364 def setAlgorithm(self, choice = None ):
366 Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
367 d'assimilation. L'argument est un champ caractère se rapportant au nom
368 d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération
369 d'assimilation sur les arguments fixes.
372 raise ValueError("Error: algorithm choice has to be given")
373 if self.__algorithmName is not None:
374 raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
375 daDirectory = "daAlgorithms"
377 # Recherche explicitement le fichier complet
378 # ------------------------------------------
380 for directory in sys.path:
381 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
382 module_path = os.path.abspath(os.path.join(directory, daDirectory))
383 if module_path is None:
384 raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
386 # Importe le fichier complet comme un module
387 # ------------------------------------------
389 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
390 self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
391 self.__algorithmName = str(choice)
392 sys.path = sys_path_tmp ; del sys_path_tmp
393 except ImportError, e:
394 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
396 # Instancie un objet du type élémentaire du fichier
397 # -------------------------------------------------
398 self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
399 self.__StoredInputs["AlgorithmName"] = str(choice)
402 def setAlgorithmParameters(self, asDico=None):
404 Permet de définir les paramètres de l'algorithme, sous la forme d'un
407 if asDico is not None:
408 self.__Parameters.update( dict( asDico ) )
410 self.__StoredInputs["AlgorithmParameters"] = self.__Parameters
413 # -----------------------------------------------------------
414 def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ):
416 Permet de sélectionner un diagnostic a effectuer.
419 raise ValueError("Error: diagnostic choice has to be given")
420 daDirectory = "daDiagnostics"
422 # Recherche explicitement le fichier complet
423 # ------------------------------------------
425 for directory in sys.path:
426 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
427 module_path = os.path.abspath(os.path.join(directory, daDirectory))
428 if module_path is None:
429 raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
431 # Importe le fichier complet comme un module
432 # ------------------------------------------
434 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
435 self.__diagnosticFile = __import__(str(choice), globals(), locals(), [])
436 sys.path = sys_path_tmp ; del sys_path_tmp
437 except ImportError, e:
438 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
440 # Instancie un objet du type élémentaire du fichier
441 # -------------------------------------------------
442 if self.__StoredInputs.has_key(name):
443 raise ValueError("A default input with the same name \"%s\" already exists."%str(name))
444 elif self.__StoredDiagnostics.has_key(name):
445 raise ValueError("A diagnostic with the same name \"%s\" already exists."%str(name))
447 self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic(
451 parameters = parameters )
454 # -----------------------------------------------------------
455 def shape_validate(self):
457 Validation de la correspondance correcte des tailles des variables et
458 des matrices s'il y en a.
460 if self.__Xb is None: __Xb_shape = (0,)
461 elif hasattr(self.__Xb,"shape"):
462 if type(self.__Xb.shape) is tuple: __Xb_shape = self.__Xb.shape
463 else: __Xb_shape = self.__Xb.shape()
464 else: raise TypeError("Xb has no attribute of shape: problem !")
466 if self.__Y is None: __Y_shape = (0,)
467 elif hasattr(self.__Y,"shape"):
468 if type(self.__Y.shape) is tuple: __Y_shape = self.__Y.shape
469 else: __Y_shape = self.__Y.shape()
470 else: raise TypeError("Y has no attribute of shape: problem !")
472 if self.__B is None and self.__B_scalar is None:
474 elif self.__B is None and self.__B_scalar is not None:
475 __B_shape = (max(__Xb_shape),max(__Xb_shape))
476 elif hasattr(self.__B,"shape"):
477 if type(self.__B.shape) is tuple: __B_shape = self.__B.shape
478 else: __B_shape = self.__B.shape()
479 else: raise TypeError("B has no attribute of shape: problem !")
481 if self.__R is None and self.__R_scalar is None:
483 elif self.__R is None and self.__R_scalar is not None:
484 __R_shape = (max(__Y_shape),max(__Y_shape))
485 elif hasattr(self.__R,"shape"):
486 if type(self.__R.shape) is tuple: __R_shape = self.__R.shape
487 else: __R_shape = self.__R.shape()
488 else: raise TypeError("R has no attribute of shape: problem !")
490 if self.__Q is None: __Q_shape = (0,0)
491 elif hasattr(self.__Q,"shape"):
492 if type(self.__Q.shape) is tuple: __Q_shape = self.__Q.shape
493 else: __Q_shape = self.__Q.shape()
494 else: raise TypeError("Q has no attribute of shape: problem !")
496 if len(self.__H) == 0: __H_shape = (0,0)
497 elif type(self.__H) is type({}): __H_shape = (0,0)
498 elif hasattr(self.__H["Direct"],"shape"):
499 if type(self.__H["Direct"].shape) is tuple: __H_shape = self.__H["Direct"].shape
500 else: __H_shape = self.__H["Direct"].shape()
501 else: raise TypeError("H has no attribute of shape: problem !")
503 if len(self.__M) == 0: __M_shape = (0,0)
504 elif type(self.__M) is type({}): __M_shape = (0,0)
505 elif hasattr(self.__M["Direct"],"shape"):
506 if type(self.__M["Direct"].shape) is tuple: __M_shape = self.__M["Direct"].shape
507 else: __M_shape = self.__M["Direct"].shape()
508 else: raise TypeError("M has no attribute of shape: problem !")
510 # Vérification des conditions
511 # ---------------------------
512 if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
513 raise ValueError("Shape characteristic of Xb is incorrect: \"%s\""%(__Xb_shape,))
514 if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
515 raise ValueError("Shape characteristic of Y is incorrect: \"%s\""%(__Y_shape,))
517 if not( min(__B_shape) == max(__B_shape) ):
518 raise ValueError("Shape characteristic of B is incorrect: \"%s\""%(__B_shape,))
519 if not( min(__R_shape) == max(__R_shape) ):
520 raise ValueError("Shape characteristic of R is incorrect: \"%s\""%(__R_shape,))
521 if not( min(__Q_shape) == max(__Q_shape) ):
522 raise ValueError("Shape characteristic of Q is incorrect: \"%s\""%(__Q_shape,))
523 if not( min(__M_shape) == max(__M_shape) ):
524 raise ValueError("Shape characteristic of M is incorrect: \"%s\""%(__M_shape,))
526 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[1] == max(__Xb_shape) ):
527 raise ValueError("Shape characteristic of H \"%s\" and X \"%s\" are incompatible"%(__H_shape,__Xb_shape))
528 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[0] == max(__Y_shape) ):
529 raise ValueError("Shape characteristic of H \"%s\" and Y \"%s\" are incompatible"%(__H_shape,__Y_shape))
530 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] ):
531 raise ValueError("Shape characteristic of H \"%s\" and B \"%s\" are incompatible"%(__H_shape,__B_shape))
532 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] ):
533 raise ValueError("Shape characteristic of H \"%s\" and R \"%s\" are incompatible"%(__H_shape,__R_shape))
535 if self.__B is not None and len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
536 raise ValueError("Shape characteristic of B \"%s\" and Xb \"%s\" are incompatible"%(__B_shape,__Xb_shape))
538 if self.__R is not None and len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
539 raise ValueError("Shape characteristic of R \"%s\" and Y \"%s\" are incompatible"%(__R_shape,__Y_shape))
541 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) ):
542 raise ValueError("Shape characteristic of M \"%s\" and X \"%s\" are incompatible"%(__M_shape,__Xb_shape))
546 # -----------------------------------------------------------
549 Permet de lancer le calcul d'assimilation.
551 Le nom de la méthode à activer est toujours "run". Les paramètres en
552 arguments de la méthode sont fixés. En sortie, on obtient les résultats
553 dans la variable de type dictionnaire "StoredVariables", qui contient en
554 particulier des objets de Persistence pour les analyses, OMA...
556 self.shape_validate()
558 self.__algorithm.run(
566 Parameters = self.__Parameters,
570 # -----------------------------------------------------------
571 def get(self, key=None):
573 Renvoie les résultats disponibles après l'exécution de la méthode
574 d'assimilation, ou les diagnostics disponibles. Attention, quand un
575 diagnostic porte le même nom qu'une variable stockée, c'est la variable
576 stockée qui est renvoyée, et le diagnostic est inatteignable.
579 if self.__algorithm.has_key(key):
580 return self.__algorithm.get( key )
581 elif self.__StoredInputs.has_key(key):
582 return self.__StoredInputs[key]
583 elif self.__StoredDiagnostics.has_key(key):
584 return self.__StoredDiagnostics[key]
586 raise ValueError("The requested key \"%s\" does not exists as an input, a diagnostic or a stored variable."%key)
588 allvariables = self.__algorithm.get()
589 allvariables.update( self.__StoredDiagnostics )
590 allvariables.update( self.__StoredInputs )
593 def get_available_variables(self):
595 Renvoie les variables potentiellement utilisables pour l'étude,
596 initialement stockées comme données d'entrées ou dans les algorithmes,
597 identifiés par les chaînes de caractères. L'algorithme doit avoir été
598 préalablement choisi sinon la méthode renvoie "None".
600 if len( self.__algorithm.keys()) == 0 and len( self.__StoredInputs.keys() ) == 0:
604 if len( self.__algorithm.keys()) > 0:
605 variables.extend( self.__algorithm.get().keys() )
606 if len( self.__StoredInputs.keys() ) > 0:
607 variables.extend( self.__StoredInputs.keys() )
611 def get_available_algorithms(self):
613 Renvoie la liste des algorithmes potentiellement utilisables, identifiés
614 par les chaînes de caractères.
617 for directory in sys.path:
618 if os.path.isdir(os.path.join(directory,"daAlgorithms")):
619 for fname in os.listdir(os.path.join(directory,"daAlgorithms")):
620 root, ext = os.path.splitext(fname)
621 if ext == '.py' and root != '__init__':
626 def get_available_diagnostics(self):
628 Renvoie la liste des diagnostics potentiellement utilisables, identifiés
629 par les chaînes de caractères.
632 for directory in sys.path:
633 if os.path.isdir(os.path.join(directory,"daDiagnostics")):
634 for fname in os.listdir(os.path.join(directory,"daDiagnostics")):
635 root, ext = os.path.splitext(fname)
636 if ext == '.py' and root != '__init__':
641 # -----------------------------------------------------------
642 def get_algorithms_main_path(self):
644 Renvoie le chemin pour le répertoire principal contenant les algorithmes
645 dans un sous-répertoire "daAlgorithms"
649 def add_algorithms_path(self, asPath=None):
651 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
652 se trouve un sous-répertoire "daAlgorithms"
654 Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est
655 pas indispensable de le rajouter ici.
657 if not os.path.isdir(asPath):
658 raise ValueError("The given "+asPath+" argument must exist as a directory")
659 if not os.path.isdir(os.path.join(asPath,"daAlgorithms")):
660 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"")
661 if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")):
662 raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"")
663 sys.path.insert(0, os.path.abspath(asPath))
664 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
667 def get_diagnostics_main_path(self):
669 Renvoie le chemin pour le répertoire principal contenant les diagnostics
670 dans un sous-répertoire "daDiagnostics"
674 def add_diagnostics_path(self, asPath=None):
676 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
677 se trouve un sous-répertoire "daDiagnostics"
679 Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est
680 pas indispensable de le rajouter ici.
682 if not os.path.isdir(asPath):
683 raise ValueError("The given "+asPath+" argument must exist as a directory")
684 if not os.path.isdir(os.path.join(asPath,"daDiagnostics")):
685 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"")
686 if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")):
687 raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"")
688 sys.path.insert(0, os.path.abspath(asPath))
689 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
692 # -----------------------------------------------------------
693 def setDataObserver(self,
696 HookParameters = None,
700 Permet d'associer un observer à une ou des variables nommées gérées en
701 interne, activable selon des règles définies dans le Scheduler.
704 if type( self.__algorithm ) is dict:
705 raise ValueError("No observer can be build before choosing an algorithm.")
707 # Vérification du nom de variable et typage
708 # -----------------------------------------
709 if type( VariableName ) is str:
710 VariableNames = [VariableName,]
711 elif type( VariableName ) is list:
712 VariableNames = map( str, VariableName )
714 raise ValueError("The observer requires a name or a list of names of variables.")
716 # Association interne de l'observer à la variable
717 # -----------------------------------------------
718 for n in VariableNames:
719 if not self.__algorithm.has_key( n ):
720 raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n)
722 self.__algorithm.StoredVariables[ n ].setDataObserver(
723 Scheduler = Scheduler,
724 HookFunction = HookFunction,
725 HookParameters = HookParameters,
728 def removeDataObserver(self,
733 Permet de retirer un observer à une ou des variable nommée.
736 if type( self.__algorithm ) is dict:
737 raise ValueError("No observer can be removed before choosing an algorithm.")
739 # Vérification du nom de variable et typage
740 # -----------------------------------------
741 if type( VariableName ) is str:
742 VariableNames = [VariableName,]
743 elif type( VariableName ) is list:
744 VariableNames = map( str, VariableName )
746 raise ValueError("The observer requires a name or a list of names of variables.")
748 # Association interne de l'observer à la variable
749 # -----------------------------------------------
750 for n in VariableNames:
751 if not self.__algorithm.has_key( n ):
752 raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%n)
754 self.__algorithm.StoredVariables[ n ].removeDataObserver(
755 HookFunction = HookFunction,
758 # -----------------------------------------------------------
759 def setDebug(self, level=10):
761 Utiliser par exemple "import logging ; level = logging.DEBUG" avant cet
762 appel pour changer le niveau de verbosité, avec :
763 NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50
766 log = logging.getLogger()
767 log.setLevel( level )
769 def unsetDebug(self):
771 Remet le logger au niveau par défaut
774 log = logging.getLogger()
775 log.setLevel( logging.WARNING )
777 def prepare_to_pickle(self):
778 self.__algorithmFile = None
779 self.__diagnosticFile = None
782 # ==============================================================================
783 if __name__ == "__main__":
784 print '\n AUTODIAGNOSTIC \n'
786 ADD = AssimilationStudy("Ma premiere etude BLUE")
788 ADD.setBackground (asVector = [0, 1, 2])
789 ADD.setBackgroundError (asCovariance = "1 0 0;0 1 0;0 0 1")
790 ADD.setObservation (asVector = [0.5, 1.5, 2.5])
791 ADD.setObservationError (asCovariance = "1 0 0;0 1 0;0 0 1")
792 ADD.setObservationOperator(asMatrix = "1 0 0;0 1 0;0 0 1")
794 ADD.setAlgorithm(choice="Blue")
798 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
799 print "Ebauche :", [0, 1, 2]
800 print "Observation :", [0.5, 1.5, 2.5]
801 print "Demi-somme :", list((numpy.array([0, 1, 2])+numpy.array([0.5, 1.5, 2.5]))/2)
802 print " qui doit être identique à :"
803 print "Analyse résultante :", ADD.get("Analysis").valueserie(0)
804 print "Innovation :", ADD.get("Innovation").valueserie(0)
807 print "Algorithmes disponibles.......................:", ADD.get_available_algorithms()
808 # print " Chemin des algorithmes.....................:", ADD.get_algorithms_main_path()
809 print "Diagnostics types disponibles.................:", ADD.get_available_diagnostics()
810 # print " Chemin des diagnostics.....................:", ADD.get_diagnostics_main_path()
811 print "Variables disponibles.........................:", ADD.get_available_variables()
814 ADD.setDiagnostic("RMS", "Ma RMS")
816 liste = ADD.get().keys()
818 print "Variables et diagnostics nommés disponibles...:", liste
821 print "Exemple de mise en place d'un observeur :"
822 def obs(var=None,info=None):
823 print " ---> Mise en oeuvre de l'observer"
824 print " var =",var.valueserie(-1)
826 ADD.setDataObserver( 'Analysis', HookFunction=obs, Scheduler = [2, 4], HookParameters = "Second observer")
827 # Attention, il faut décaler le stockage de 1 pour suivre le pas interne
828 # car le pas 0 correspond à l'analyse ci-dessus.
831 print "Action sur la variable observée, étape :",i
832 ADD.get('Analysis').store( [i, i, i] )
835 print "Mise en debug et hors debug"
836 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
840 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
842 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()