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 # Variables temporaires
82 self.__algorithmFile = None
83 self.__algorithmName = None
84 self.__diagnosticFile = None
86 # Récupère le chemin du répertoire parent et l'ajoute au path
87 # (Cela complète l'action de la classe PathManagement dans PlatformInfo,
88 # qui est activée dans Persistence)
89 self.__parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
90 sys.path.insert(0, self.__parent)
91 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
93 # ---------------------------------------------------------
94 def setBackground(self,
96 asPersistentVector = None,
101 Permet de définir l'estimation a priori :
102 - asVector : entrée des données, comme un vecteur compatible avec le
103 constructeur de numpy.matrix
104 - asPersistentVector : entrée des données, comme un vecteur de type
105 persistent contruit avec la classe ad-hoc "Persistence"
106 - Scheduler est le contrôle temporel des données
107 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
108 être rendue disponible au même titre que les variables de calcul
110 if asVector is not None:
111 if type( asVector ) is type( numpy.matrix([]) ):
112 self.__Xb = numpy.matrix( asVector.A1, numpy.float ).T
114 self.__Xb = numpy.matrix( asVector, numpy.float ).T
115 elif asPersistentVector is not None:
116 self.__Xb = asPersistentVector
118 raise ValueError("Error: improperly defined background")
120 self.__StoredInputs["Background"] = self.__Xb
123 def setBackgroundError(self,
128 Permet de définir la covariance des erreurs d'ébauche :
129 - asCovariance : entrée des données, comme une matrice compatible avec
130 le constructeur de numpy.matrix
131 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
132 être rendue disponible au même titre que les variables de calcul
134 self.__B = numpy.matrix( asCovariance, numpy.float )
136 self.__StoredInputs["BackgroundError"] = self.__B
139 # -----------------------------------------------------------
140 def setObservation(self,
142 asPersistentVector = None,
147 Permet de définir les observations :
148 - asVector : entrée des données, comme un vecteur compatible avec le
149 constructeur de numpy.matrix
150 - asPersistentVector : entrée des données, comme un vecteur de type
151 persistent contruit avec la classe ad-hoc "Persistence"
152 - Scheduler est le contrôle temporel des données disponibles
153 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
154 être rendue disponible au même titre que les variables de calcul
156 if asVector is not None:
157 if type( asVector ) is type( numpy.matrix([]) ):
158 self.__Y = numpy.matrix( asVector.A1, numpy.float ).T
160 self.__Y = numpy.matrix( asVector, numpy.float ).T
161 elif asPersistentVector is not None:
162 self.__Y = asPersistentVector
164 raise ValueError("Error: improperly defined observations")
166 self.__StoredInputs["Observation"] = self.__Y
169 def setObservationError(self,
174 Permet de définir la covariance des erreurs d'observations :
175 - asCovariance : entrée des données, comme une matrice compatible avec
176 le constructeur de numpy.matrix
177 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
178 être rendue disponible au même titre que les variables de calcul
180 self.__R = numpy.matrix( asCovariance, numpy.float )
182 self.__StoredInputs["ObservationError"] = self.__R
185 def setObservationOperator(self,
186 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
192 Permet de définir un opérateur d'observation H. L'ordre de priorité des
193 définitions et leur sens sont les suivants :
194 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
195 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
196 "Direct" n'est pas définie, on prend la fonction "Tangent".
197 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
198 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
199 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
200 matrice fournie doit être sous une forme compatible avec le
201 constructeur de numpy.matrix.
202 - si l'argument "appliedToX" n'est pas None, alors on définit, pour des
203 X divers, l'opérateur par sa valeur appliquée à cet X particulier,
204 sous la forme d'un dictionnaire appliedToX[NAME] avec NAME un nom.
205 L'opérateur doit néanmoins déjà avoir été défini comme d'habitude.
206 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
207 être rendue disponible au même titre que les variables de calcul
209 if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
210 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
211 self.__H["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
213 self.__H["Direct"] = Operator( fromMethod = asFunction["Direct"] )
214 self.__H["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
215 self.__H["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
216 elif asMatrix is not None:
217 mat = numpy.matrix( asMatrix, numpy.float )
218 self.__H["Direct"] = Operator( fromMatrix = mat )
219 self.__H["Tangent"] = Operator( fromMatrix = mat )
220 self.__H["Adjoint"] = Operator( fromMatrix = mat.T )
222 raise ValueError("Error: improperly defined observation operator")
224 if appliedToX is not None:
225 self.__H["AppliedToX"] = {}
226 if type(appliedToX) is not dict:
227 raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.")
228 for key in appliedToX.keys():
229 if type( appliedToX[key] ) is type( numpy.matrix([]) ):
230 # Pour le cas où l'on a une vraie matrice
231 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T
232 elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1:
233 # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions
234 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T
236 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key], numpy.float ).T
238 self.__H["AppliedToX"] = None
241 self.__StoredInputs["ObservationOperator"] = self.__H
244 # -----------------------------------------------------------
245 def setEvolutionModel(self,
246 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
252 Permet de définir un opérateur d'évolution M. L'ordre de priorité des
253 définitions et leur sens sont les suivants :
254 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
255 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
256 "Direct" n'est pas définie, on prend la fonction "Tangent".
257 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
258 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
259 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
260 matrice fournie doit être sous une forme compatible avec le
261 constructeur de numpy.matrix.
262 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
263 être rendue disponible au même titre que les variables de calcul
265 if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
266 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
267 self.__M["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
269 self.__M["Direct"] = Operator( fromMethod = asFunction["Direct"] )
270 self.__M["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
271 self.__M["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
272 elif asMatrix is not None:
273 matrice = numpy.matrix( asMatrix, numpy.float )
274 self.__M["Direct"] = Operator( fromMatrix = matrice )
275 self.__M["Tangent"] = Operator( fromMatrix = matrice )
276 self.__M["Adjoint"] = Operator( fromMatrix = matrice.T )
278 raise ValueError("Error: improperly defined evolution operator")
281 self.__StoredInputs["EvolutionModel"] = self.__M
284 def setEvolutionError(self,
289 Permet de définir la covariance des erreurs de modèle :
290 - asCovariance : entrée des données, comme une matrice compatible avec
291 le constructeur de numpy.matrix
292 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
293 être rendue disponible au même titre que les variables de calcul
295 self.__Q = numpy.matrix( asCovariance, numpy.float )
297 self.__StoredInputs["EvolutionError"] = self.__Q
300 # -----------------------------------------------------------
301 def setControls (self,
306 Permet de définir la valeur initiale du vecteur X contenant toutes les
307 variables de contrôle, i.e. les paramètres ou l'état dont on veut
308 estimer la valeur pour obtenir les observations. C'est utile pour un
309 algorithme itératif/incrémental
310 - asVector : entrée des données, comme un vecteur compatible avec le
311 constructeur de numpy.matrix.
312 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
313 être rendue disponible au même titre que les variables de calcul
315 if asVector is not None:
316 self.__X.store( asVector )
318 self.__StoredInputs["Controls"] = self.__X
321 # -----------------------------------------------------------
322 def setAlgorithm(self, choice = None ):
324 Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
325 d'assimilation. L'argument est un champ caractère se rapportant au nom
326 d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération
327 d'assimilation sur les arguments (Xb,Y,H,R,B,Xa).
330 raise ValueError("Error: algorithm choice has to be given")
331 if self.__algorithmName is not None:
332 raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
333 daDirectory = "daAlgorithms"
335 # Recherche explicitement le fichier complet
336 # ------------------------------------------
338 for directory in sys.path:
339 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
340 module_path = os.path.abspath(os.path.join(directory, daDirectory))
341 if module_path is None:
342 raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
344 # Importe le fichier complet comme un module
345 # ------------------------------------------
347 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
348 self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
349 self.__algorithmName = str(choice)
350 sys.path = sys_path_tmp ; del sys_path_tmp
351 except ImportError, e:
352 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
354 # Instancie un objet du type élémentaire du fichier
355 # -------------------------------------------------
356 self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
357 self.__StoredInputs["AlgorithmName"] = str(choice)
360 def setAlgorithmParameters(self, asDico=None):
362 Permet de définir les paramètres de l'algorithme, sous la forme d'un
365 if asDico is not None:
366 self.__Parameters = dict( asDico )
368 self.__Parameters = {}
369 self.__StoredInputs["AlgorithmParameters"] = self.__Parameters
372 # -----------------------------------------------------------
373 def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ):
375 Permet de sélectionner un diagnostic a effectuer.
378 raise ValueError("Error: diagnostic choice has to be given")
379 daDirectory = "daDiagnostics"
381 # Recherche explicitement le fichier complet
382 # ------------------------------------------
384 for directory in sys.path:
385 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
386 module_path = os.path.abspath(os.path.join(directory, daDirectory))
387 if module_path is None:
388 raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
390 # Importe le fichier complet comme un module
391 # ------------------------------------------
393 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
394 self.__diagnosticFile = __import__(str(choice), globals(), locals(), [])
395 sys.path = sys_path_tmp ; del sys_path_tmp
396 except ImportError, e:
397 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
399 # Instancie un objet du type élémentaire du fichier
400 # -------------------------------------------------
401 if self.__StoredInputs.has_key(name):
402 raise ValueError("A default input with the same name \"%s\" already exists."%str(name))
403 elif self.__StoredDiagnostics.has_key(name):
404 raise ValueError("A diagnostic with the same name \"%s\" already exists."%str(name))
406 self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic(
410 parameters = parameters )
413 # -----------------------------------------------------------
414 def shape_validate(self):
416 Validation de la correspondance correcte des tailles des variables et
417 des matrices s'il y en a.
419 if self.__Xb is None: __Xb_shape = (0,)
420 elif hasattr(self.__Xb,"shape"):
421 if type(self.__Xb.shape) is tuple: __Xb_shape = self.__Xb.shape
422 else: __Xb_shape = self.__Xb.shape()
423 else: raise TypeError("Xb has no attribute of shape: problem !")
425 if self.__Y is None: __Y_shape = (0,)
426 elif hasattr(self.__Y,"shape"):
427 if type(self.__Y.shape) is tuple: __Y_shape = self.__Y.shape
428 else: __Y_shape = self.__Y.shape()
429 else: raise TypeError("Y has no attribute of shape: problem !")
431 if self.__B is None: __B_shape = (0,0)
432 elif hasattr(self.__B,"shape"):
433 if type(self.__B.shape) is tuple: __B_shape = self.__B.shape
434 else: __B_shape = self.__B.shape()
435 else: raise TypeError("B has no attribute of shape: problem !")
437 if self.__R is None: __R_shape = (0,0)
438 elif hasattr(self.__R,"shape"):
439 if type(self.__R.shape) is tuple: __R_shape = self.__R.shape
440 else: __R_shape = self.__R.shape()
441 else: raise TypeError("R has no attribute of shape: problem !")
443 if self.__Q is None: __Q_shape = (0,0)
444 elif hasattr(self.__Q,"shape"):
445 if type(self.__Q.shape) is tuple: __Q_shape = self.__Q.shape
446 else: __Q_shape = self.__Q.shape()
447 else: raise TypeError("Q has no attribute of shape: problem !")
449 if len(self.__H) == 0: __H_shape = (0,0)
450 elif type(self.__H) is type({}): __H_shape = (0,0)
451 elif hasattr(self.__H["Direct"],"shape"):
452 if type(self.__H["Direct"].shape) is tuple: __H_shape = self.__H["Direct"].shape
453 else: __H_shape = self.__H["Direct"].shape()
454 else: raise TypeError("H has no attribute of shape: problem !")
456 if len(self.__M) == 0: __M_shape = (0,0)
457 elif type(self.__M) is type({}): __M_shape = (0,0)
458 elif hasattr(self.__M["Direct"],"shape"):
459 if type(self.__M["Direct"].shape) is tuple: __M_shape = self.__M["Direct"].shape
460 else: __M_shape = self.__M["Direct"].shape()
461 else: raise TypeError("M has no attribute of shape: problem !")
463 # Vérification des conditions
464 # ---------------------------
465 if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
466 raise ValueError("Shape characteristic of Xb is incorrect: \"%s\""%(__Xb_shape,))
467 if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
468 raise ValueError("Shape characteristic of Y is incorrect: \"%s\""%(__Y_shape,))
470 if not( min(__B_shape) == max(__B_shape) ):
471 raise ValueError("Shape characteristic of B is incorrect: \"%s\""%(__B_shape,))
472 if not( min(__R_shape) == max(__R_shape) ):
473 raise ValueError("Shape characteristic of R is incorrect: \"%s\""%(__R_shape,))
474 if not( min(__Q_shape) == max(__Q_shape) ):
475 raise ValueError("Shape characteristic of Q is incorrect: \"%s\""%(__Q_shape,))
476 if not( min(__M_shape) == max(__M_shape) ):
477 raise ValueError("Shape characteristic of M is incorrect: \"%s\""%(__M_shape,))
479 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[1] == max(__Xb_shape) ):
480 raise ValueError("Shape characteristic of H \"%s\" and X \"%s\" are incompatible"%(__H_shape,__Xb_shape))
481 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[0] == max(__Y_shape) ):
482 raise ValueError("Shape characteristic of H \"%s\" and Y \"%s\" are incompatible"%(__H_shape,__Y_shape))
483 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] ):
484 raise ValueError("Shape characteristic of H \"%s\" and B \"%s\" are incompatible"%(__H_shape,__B_shape))
485 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] ):
486 raise ValueError("Shape characteristic of H \"%s\" and R \"%s\" are incompatible"%(__H_shape,__R_shape))
488 if self.__B is not None and len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
489 raise ValueError("Shape characteristic of B \"%s\" and Xb \"%s\" are incompatible"%(__B_shape,__Xb_shape))
491 if self.__R is not None and len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
492 raise ValueError("Shape characteristic of R \"%s\" and Y \"%s\" are incompatible"%(__R_shape,__Y_shape))
494 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) ):
495 raise ValueError("Shape characteristic of M \"%s\" and X \"%s\" are incompatible"%(__M_shape,__Xb_shape))
499 # -----------------------------------------------------------
502 Permet de lancer le calcul d'assimilation.
504 Le nom de la méthode à activer est toujours "run". Les paramètres en
505 arguments de la méthode sont fixés. En sortie, on obtient les résultats
506 dans la variable de type dictionnaire "StoredVariables", qui contient en
507 particulier des objets de Persistence pour les analyses, OMA...
509 self.shape_validate()
511 self.__algorithm.run(
519 Parameters = self.__Parameters,
523 # -----------------------------------------------------------
524 def get(self, key=None):
526 Renvoie les résultats disponibles après l'exécution de la méthode
527 d'assimilation, ou les diagnostics disponibles. Attention, quand un
528 diagnostic porte le même nom qu'une variable stockée, c'est la variable
529 stockée qui est renvoyée, et le diagnostic est inatteignable.
532 if self.__algorithm.has_key(key):
533 return self.__algorithm.get( key )
534 elif self.__StoredInputs.has_key(key):
535 return self.__StoredInputs[key]
536 elif self.__StoredDiagnostics.has_key(key):
537 return self.__StoredDiagnostics[key]
539 raise ValueError("The requested key \"%s\" does not exists as an input, a diagnostic or a stored variable."%key)
541 allvariables = self.__algorithm.get()
542 allvariables.update( self.__StoredDiagnostics )
543 allvariables.update( self.__StoredInputs )
546 def get_available_variables(self):
548 Renvoie les variables potentiellement utilisables pour l'étude,
549 initialement stockées comme données d'entrées ou dans les algorithmes,
550 identifiés par les chaînes de caractères. L'algorithme doit avoir été
551 préalablement choisi sinon la méthode renvoie "None".
553 if len( self.__algorithm.keys()) == 0 and len( self.__StoredInputs.keys() ) == 0:
557 if len( self.__algorithm.keys()) > 0:
558 variables.extend( self.__algorithm.get().keys() )
559 if len( self.__StoredInputs.keys() ) > 0:
560 variables.extend( self.__StoredInputs.keys() )
564 def get_available_algorithms(self):
566 Renvoie la liste des algorithmes potentiellement utilisables, identifiés
567 par les chaînes de caractères.
570 for directory in sys.path:
571 if os.path.isdir(os.path.join(directory,"daAlgorithms")):
572 for fname in os.listdir(os.path.join(directory,"daAlgorithms")):
573 root, ext = os.path.splitext(fname)
574 if ext == '.py' and root != '__init__':
579 def get_available_diagnostics(self):
581 Renvoie la liste des diagnostics potentiellement utilisables, identifiés
582 par les chaînes de caractères.
585 for directory in sys.path:
586 if os.path.isdir(os.path.join(directory,"daDiagnostics")):
587 for fname in os.listdir(os.path.join(directory,"daDiagnostics")):
588 root, ext = os.path.splitext(fname)
589 if ext == '.py' and root != '__init__':
594 # -----------------------------------------------------------
595 def get_algorithms_main_path(self):
597 Renvoie le chemin pour le répertoire principal contenant les algorithmes
598 dans un sous-répertoire "daAlgorithms"
602 def add_algorithms_path(self, asPath=None):
604 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
605 se trouve un sous-répertoire "daAlgorithms"
607 Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est
608 pas indispensable de le rajouter ici.
610 if not os.path.isdir(asPath):
611 raise ValueError("The given "+asPath+" argument must exist as a directory")
612 if not os.path.isdir(os.path.join(asPath,"daAlgorithms")):
613 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"")
614 if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")):
615 raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"")
616 sys.path.insert(0, os.path.abspath(asPath))
617 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
620 def get_diagnostics_main_path(self):
622 Renvoie le chemin pour le répertoire principal contenant les diagnostics
623 dans un sous-répertoire "daDiagnostics"
627 def add_diagnostics_path(self, asPath=None):
629 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
630 se trouve un sous-répertoire "daDiagnostics"
632 Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est
633 pas indispensable de le rajouter ici.
635 if not os.path.isdir(asPath):
636 raise ValueError("The given "+asPath+" argument must exist as a directory")
637 if not os.path.isdir(os.path.join(asPath,"daDiagnostics")):
638 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"")
639 if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")):
640 raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"")
641 sys.path.insert(0, os.path.abspath(asPath))
642 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
645 # -----------------------------------------------------------
646 def setDataObserver(self,
649 HookParameters = None,
653 Permet d'associer un observer à une ou des variables nommées gérées en
654 interne, activable selon des règles définies dans le Scheduler.
657 if type( self.__algorithm ) is dict:
658 raise ValueError("No observer can be build before choosing an algorithm.")
660 # Vérification du nom de variable et typage
661 # -----------------------------------------
662 if type( VariableName ) is str:
663 VariableNames = [VariableName,]
664 elif type( VariableName ) is list:
665 VariableNames = map( str, VariableName )
667 raise ValueError("The observer requires a name or a list of names of variables.")
669 # Association interne de l'observer à la variable
670 # -----------------------------------------------
671 for n in VariableNames:
672 if not self.__algorithm.has_key( n ):
673 raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n)
675 self.__algorithm.StoredVariables[ n ].setDataObserver(
676 Scheduler = Scheduler,
677 HookFunction = HookFunction,
678 HookParameters = HookParameters,
681 def removeDataObserver(self,
686 Permet de retirer un observer à une ou des variable nommée.
689 if type( self.__algorithm ) is dict:
690 raise ValueError("No observer can be removed before choosing an algorithm.")
692 # Vérification du nom de variable et typage
693 # -----------------------------------------
694 if type( VariableName ) is str:
695 VariableNames = [VariableName,]
696 elif type( VariableName ) is list:
697 VariableNames = map( str, VariableName )
699 raise ValueError("The observer requires a name or a list of names of variables.")
701 # Association interne de l'observer à la variable
702 # -----------------------------------------------
703 for n in VariableNames:
704 if not self.__algorithm.has_key( n ):
705 raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%n)
707 self.__algorithm.StoredVariables[ n ].removeDataObserver(
708 HookFunction = HookFunction,
711 # -----------------------------------------------------------
712 def setDebug(self, level=10):
714 Utiliser par exemple "import logging ; level = logging.DEBUG" avant cet
715 appel pour changer le niveau de verbosité, avec :
716 NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50
719 log = logging.getLogger()
720 log.setLevel( level )
722 def unsetDebug(self):
724 Remet le logger au niveau par défaut
727 log = logging.getLogger()
728 log.setLevel( logging.WARNING )
730 def prepare_to_pickle(self):
731 self.__algorithmFile = None
732 self.__diagnosticFile = None
735 # ==============================================================================
736 if __name__ == "__main__":
737 print '\n AUTODIAGNOSTIC \n'
739 ADD = AssimilationStudy("Ma premiere etude BLUE")
741 ADD.setBackground (asVector = [0, 1, 2])
742 ADD.setBackgroundError (asCovariance = "1 0 0;0 1 0;0 0 1")
743 ADD.setObservation (asVector = [0.5, 1.5, 2.5])
744 ADD.setObservationError (asCovariance = "1 0 0;0 1 0;0 0 1")
745 ADD.setObservationOperator(asMatrix = "1 0 0;0 1 0;0 0 1")
747 ADD.setAlgorithm(choice="Blue")
751 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
752 print "Ebauche :", [0, 1, 2]
753 print "Observation :", [0.5, 1.5, 2.5]
754 print "Demi-somme :", list((numpy.array([0, 1, 2])+numpy.array([0.5, 1.5, 2.5]))/2)
755 print " qui doit être identique à :"
756 print "Analyse résultante :", ADD.get("Analysis").valueserie(0)
757 print "Innovation :", ADD.get("Innovation").valueserie(0)
760 print "Algorithmes disponibles.......................:", ADD.get_available_algorithms()
761 # print " Chemin des algorithmes.....................:", ADD.get_algorithms_main_path()
762 print "Diagnostics types disponibles.................:", ADD.get_available_diagnostics()
763 # print " Chemin des diagnostics.....................:", ADD.get_diagnostics_main_path()
764 print "Variables disponibles.........................:", ADD.get_available_variables()
767 ADD.setDiagnostic("RMS", "Ma RMS")
769 liste = ADD.get().keys()
771 print "Variables et diagnostics nommés disponibles...:", liste
774 print "Exemple de mise en place d'un observeur :"
775 def obs(var=None,info=None):
776 print " ---> Mise en oeuvre de l'observer"
777 print " var =",var.valueserie(-1)
779 ADD.setDataObserver( 'Analysis', HookFunction=obs, Scheduler = [2, 4], HookParameters = "Second observer")
780 # Attention, il faut décaler le stockage de 1 pour suivre le pas interne
781 # car le pas 0 correspond à l'analyse ci-dessus.
784 print "Action sur la variable observée, étape :",i
785 ADD.get('Analysis').store( [i, i, i] )
788 print "Mise en debug et hors debug"
789 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
793 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
795 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()