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 \
210 asFunction.has_key("Tangent") and asFunction.has_key("Adjoint") and \
211 (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
212 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
213 self.__H["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
215 self.__H["Direct"] = Operator( fromMethod = asFunction["Direct"] )
216 self.__H["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
217 self.__H["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
218 elif asMatrix is not None:
219 mat = numpy.matrix( asMatrix, numpy.float )
220 self.__H["Direct"] = Operator( fromMatrix = mat )
221 self.__H["Tangent"] = Operator( fromMatrix = mat )
222 self.__H["Adjoint"] = Operator( fromMatrix = mat.T )
224 raise ValueError("Error: improperly defined observation operator")
226 if appliedToX is not None:
227 self.__H["AppliedToX"] = {}
228 if type(appliedToX) is not dict:
229 raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.")
230 for key in appliedToX.keys():
231 if type( appliedToX[key] ) is type( numpy.matrix([]) ):
232 # Pour le cas où l'on a une vraie matrice
233 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T
234 elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1:
235 # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions
236 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T
238 self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key], numpy.float ).T
240 self.__H["AppliedToX"] = None
243 self.__StoredInputs["ObservationOperator"] = self.__H
246 # -----------------------------------------------------------
247 def setEvolutionModel(self,
248 asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
254 Permet de définir un opérateur d'évolution M. L'ordre de priorité des
255 définitions et leur sens sont les suivants :
256 - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
257 alors on définit l'opérateur à l'aide de fonctions. Si la fonction
258 "Direct" n'est pas définie, on prend la fonction "Tangent".
259 - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
260 None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
261 la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
262 matrice fournie doit être sous une forme compatible avec le
263 constructeur de numpy.matrix.
264 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
265 être rendue disponible au même titre que les variables de calcul
267 if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
268 if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
269 self.__M["Direct"] = Operator( fromMethod = asFunction["Tangent"] )
271 self.__M["Direct"] = Operator( fromMethod = asFunction["Direct"] )
272 self.__M["Tangent"] = Operator( fromMethod = asFunction["Tangent"] )
273 self.__M["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] )
274 elif asMatrix is not None:
275 matrice = numpy.matrix( asMatrix, numpy.float )
276 self.__M["Direct"] = Operator( fromMatrix = matrice )
277 self.__M["Tangent"] = Operator( fromMatrix = matrice )
278 self.__M["Adjoint"] = Operator( fromMatrix = matrice.T )
280 raise ValueError("Error: improperly defined evolution operator")
283 self.__StoredInputs["EvolutionModel"] = self.__M
286 def setEvolutionError(self,
291 Permet de définir la covariance des erreurs de modèle :
292 - asCovariance : entrée des données, comme une matrice compatible avec
293 le constructeur de numpy.matrix
294 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
295 être rendue disponible au même titre que les variables de calcul
297 self.__Q = numpy.matrix( asCovariance, numpy.float )
299 self.__StoredInputs["EvolutionError"] = self.__Q
302 # -----------------------------------------------------------
303 def setControls (self,
308 Permet de définir la valeur initiale du vecteur X contenant toutes les
309 variables de contrôle, i.e. les paramètres ou l'état dont on veut
310 estimer la valeur pour obtenir les observations. C'est utile pour un
311 algorithme itératif/incrémental
312 - asVector : entrée des données, comme un vecteur compatible avec le
313 constructeur de numpy.matrix.
314 - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
315 être rendue disponible au même titre que les variables de calcul
317 if asVector is not None:
318 self.__X.store( asVector )
320 self.__StoredInputs["Controls"] = self.__X
323 # -----------------------------------------------------------
324 def setAlgorithm(self, choice = None ):
326 Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
327 d'assimilation. L'argument est un champ caractère se rapportant au nom
328 d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération
329 d'assimilation sur les arguments (Xb,Y,H,R,B,Xa).
332 raise ValueError("Error: algorithm choice has to be given")
333 if self.__algorithmName is not None:
334 raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
335 daDirectory = "daAlgorithms"
337 # Recherche explicitement le fichier complet
338 # ------------------------------------------
340 for directory in sys.path:
341 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
342 module_path = os.path.abspath(os.path.join(directory, daDirectory))
343 if module_path is None:
344 raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
346 # Importe le fichier complet comme un module
347 # ------------------------------------------
349 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
350 self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
351 self.__algorithmName = str(choice)
352 sys.path = sys_path_tmp ; del sys_path_tmp
353 except ImportError, e:
354 raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
356 # Instancie un objet du type élémentaire du fichier
357 # -------------------------------------------------
358 self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
359 self.__StoredInputs["AlgorithmName"] = str(choice)
362 def setAlgorithmParameters(self, asDico=None):
364 Permet de définir les paramètres de l'algorithme, sous la forme d'un
367 if asDico is not None:
368 self.__Parameters = dict( asDico )
370 self.__Parameters = {}
371 self.__StoredInputs["AlgorithmParameters"] = self.__Parameters
374 # -----------------------------------------------------------
375 def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ):
377 Permet de sélectionner un diagnostic a effectuer.
380 raise ValueError("Error: diagnostic choice has to be given")
381 daDirectory = "daDiagnostics"
383 # Recherche explicitement le fichier complet
384 # ------------------------------------------
386 for directory in sys.path:
387 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
388 module_path = os.path.abspath(os.path.join(directory, daDirectory))
389 if module_path is None:
390 raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path))
392 # Importe le fichier complet comme un module
393 # ------------------------------------------
395 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
396 self.__diagnosticFile = __import__(str(choice), globals(), locals(), [])
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 if self.__StoredInputs.has_key(name):
404 raise ValueError("A default input with the same name \"%s\" already exists."%str(name))
405 elif self.__StoredDiagnostics.has_key(name):
406 raise ValueError("A diagnostic with the same name \"%s\" already exists."%str(name))
408 self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic(
412 parameters = parameters )
415 # -----------------------------------------------------------
416 def shape_validate(self):
418 Validation de la correspondance correcte des tailles des variables et
419 des matrices s'il y en a.
421 if self.__Xb is None: __Xb_shape = (0,)
422 elif hasattr(self.__Xb,"shape"):
423 if type(self.__Xb.shape) is tuple: __Xb_shape = self.__Xb.shape
424 else: __Xb_shape = self.__Xb.shape()
425 else: raise TypeError("Xb has no attribute of shape: problem !")
427 if self.__Y is None: __Y_shape = (0,)
428 elif hasattr(self.__Y,"shape"):
429 if type(self.__Y.shape) is tuple: __Y_shape = self.__Y.shape
430 else: __Y_shape = self.__Y.shape()
431 else: raise TypeError("Y has no attribute of shape: problem !")
433 if self.__B is None: __B_shape = (0,0)
434 elif hasattr(self.__B,"shape"):
435 if type(self.__B.shape) is tuple: __B_shape = self.__B.shape
436 else: __B_shape = self.__B.shape()
437 else: raise TypeError("B has no attribute of shape: problem !")
439 if self.__R is None: __R_shape = (0,0)
440 elif hasattr(self.__R,"shape"):
441 if type(self.__R.shape) is tuple: __R_shape = self.__R.shape
442 else: __R_shape = self.__R.shape()
443 else: raise TypeError("R has no attribute of shape: problem !")
445 if self.__Q is None: __Q_shape = (0,0)
446 elif hasattr(self.__Q,"shape"):
447 if type(self.__Q.shape) is tuple: __Q_shape = self.__Q.shape
448 else: __Q_shape = self.__Q.shape()
449 else: raise TypeError("Q has no attribute of shape: problem !")
451 if len(self.__H) == 0: __H_shape = (0,0)
452 elif type(self.__H) is type({}): __H_shape = (0,0)
453 elif hasattr(self.__H["Direct"],"shape"):
454 if type(self.__H["Direct"].shape) is tuple: __H_shape = self.__H["Direct"].shape
455 else: __H_shape = self.__H["Direct"].shape()
456 else: raise TypeError("H has no attribute of shape: problem !")
458 if len(self.__M) == 0: __M_shape = (0,0)
459 elif type(self.__M) is type({}): __M_shape = (0,0)
460 elif hasattr(self.__M["Direct"],"shape"):
461 if type(self.__M["Direct"].shape) is tuple: __M_shape = self.__M["Direct"].shape
462 else: __M_shape = self.__M["Direct"].shape()
463 else: raise TypeError("M has no attribute of shape: problem !")
465 # Vérification des conditions
466 # ---------------------------
467 if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
468 raise ValueError("Shape characteristic of Xb is incorrect: \"%s\""%(__Xb_shape,))
469 if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
470 raise ValueError("Shape characteristic of Y is incorrect: \"%s\""%(__Y_shape,))
472 if not( min(__B_shape) == max(__B_shape) ):
473 raise ValueError("Shape characteristic of B is incorrect: \"%s\""%(__B_shape,))
474 if not( min(__R_shape) == max(__R_shape) ):
475 raise ValueError("Shape characteristic of R is incorrect: \"%s\""%(__R_shape,))
476 if not( min(__Q_shape) == max(__Q_shape) ):
477 raise ValueError("Shape characteristic of Q is incorrect: \"%s\""%(__Q_shape,))
478 if not( min(__M_shape) == max(__M_shape) ):
479 raise ValueError("Shape characteristic of M is incorrect: \"%s\""%(__M_shape,))
481 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[1] == max(__Xb_shape) ):
482 raise ValueError("Shape characteristic of H \"%s\" and X \"%s\" are incompatible"%(__H_shape,__Xb_shape))
483 if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[0] == max(__Y_shape) ):
484 raise ValueError("Shape characteristic of H \"%s\" and Y \"%s\" are incompatible"%(__H_shape,__Y_shape))
485 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] ):
486 raise ValueError("Shape characteristic of H \"%s\" and B \"%s\" are incompatible"%(__H_shape,__B_shape))
487 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] ):
488 raise ValueError("Shape characteristic of H \"%s\" and R \"%s\" are incompatible"%(__H_shape,__R_shape))
490 if self.__B is not None and len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
491 raise ValueError("Shape characteristic of B \"%s\" and Xb \"%s\" are incompatible"%(__B_shape,__Xb_shape))
493 if self.__R is not None and len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
494 raise ValueError("Shape characteristic of R \"%s\" and Y \"%s\" are incompatible"%(__R_shape,__Y_shape))
496 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) ):
497 raise ValueError("Shape characteristic of M \"%s\" and X \"%s\" are incompatible"%(__M_shape,__Xb_shape))
501 # -----------------------------------------------------------
504 Permet de lancer le calcul d'assimilation.
506 Le nom de la méthode à activer est toujours "run". Les paramètres en
507 arguments de la méthode sont fixés. En sortie, on obtient les résultats
508 dans la variable de type dictionnaire "StoredVariables", qui contient en
509 particulier des objets de Persistence pour les analyses, OMA...
511 self.shape_validate()
513 self.__algorithm.run(
521 Parameters = self.__Parameters,
525 # -----------------------------------------------------------
526 def get(self, key=None):
528 Renvoie les résultats disponibles après l'exécution de la méthode
529 d'assimilation, ou les diagnostics disponibles. Attention, quand un
530 diagnostic porte le même nom qu'une variable stockée, c'est la variable
531 stockée qui est renvoyée, et le diagnostic est inatteignable.
534 if self.__algorithm.has_key(key):
535 return self.__algorithm.get( key )
536 elif self.__StoredInputs.has_key(key):
537 return self.__StoredInputs[key]
538 elif self.__StoredDiagnostics.has_key(key):
539 return self.__StoredDiagnostics[key]
541 raise ValueError("The requested key \"%s\" does not exists as an input, a diagnostic or a stored variable."%key)
543 allvariables = self.__algorithm.get()
544 allvariables.update( self.__StoredDiagnostics )
545 allvariables.update( self.__StoredInputs )
548 def get_available_variables(self):
550 Renvoie les variables potentiellement utilisables pour l'étude,
551 initialement stockées comme données d'entrées ou dans les algorithmes,
552 identifiés par les chaînes de caractères. L'algorithme doit avoir été
553 préalablement choisi sinon la méthode renvoie "None".
555 if len( self.__algorithm.keys()) == 0 and len( self.__StoredInputs.keys() ) == 0:
559 if len( self.__algorithm.keys()) > 0:
560 variables.extend( self.__algorithm.get().keys() )
561 if len( self.__StoredInputs.keys() ) > 0:
562 variables.extend( self.__StoredInputs.keys() )
566 def get_available_algorithms(self):
568 Renvoie la liste des algorithmes potentiellement utilisables, identifiés
569 par les chaînes de caractères.
572 for directory in sys.path:
573 if os.path.isdir(os.path.join(directory,"daAlgorithms")):
574 for fname in os.listdir(os.path.join(directory,"daAlgorithms")):
575 root, ext = os.path.splitext(fname)
576 if ext == '.py' and root != '__init__':
581 def get_available_diagnostics(self):
583 Renvoie la liste des diagnostics potentiellement utilisables, identifiés
584 par les chaînes de caractères.
587 for directory in sys.path:
588 if os.path.isdir(os.path.join(directory,"daDiagnostics")):
589 for fname in os.listdir(os.path.join(directory,"daDiagnostics")):
590 root, ext = os.path.splitext(fname)
591 if ext == '.py' and root != '__init__':
596 # -----------------------------------------------------------
597 def get_algorithms_main_path(self):
599 Renvoie le chemin pour le répertoire principal contenant les algorithmes
600 dans un sous-répertoire "daAlgorithms"
604 def add_algorithms_path(self, asPath=None):
606 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
607 se trouve un sous-répertoire "daAlgorithms"
609 Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est
610 pas indispensable de le rajouter ici.
612 if not os.path.isdir(asPath):
613 raise ValueError("The given "+asPath+" argument must exist as a directory")
614 if not os.path.isdir(os.path.join(asPath,"daAlgorithms")):
615 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"")
616 if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")):
617 raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"")
618 sys.path.insert(0, os.path.abspath(asPath))
619 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
622 def get_diagnostics_main_path(self):
624 Renvoie le chemin pour le répertoire principal contenant les diagnostics
625 dans un sous-répertoire "daDiagnostics"
629 def add_diagnostics_path(self, asPath=None):
631 Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
632 se trouve un sous-répertoire "daDiagnostics"
634 Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est
635 pas indispensable de le rajouter ici.
637 if not os.path.isdir(asPath):
638 raise ValueError("The given "+asPath+" argument must exist as a directory")
639 if not os.path.isdir(os.path.join(asPath,"daDiagnostics")):
640 raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"")
641 if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")):
642 raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"")
643 sys.path.insert(0, os.path.abspath(asPath))
644 sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
647 # -----------------------------------------------------------
648 def setDataObserver(self,
651 HookParameters = None,
655 Permet d'associer un observer à une ou des variables nommées gérées en
656 interne, activable selon des règles définies dans le Scheduler.
659 if type( self.__algorithm ) is dict:
660 raise ValueError("No observer can be build before choosing an algorithm.")
662 # Vérification du nom de variable et typage
663 # -----------------------------------------
664 if type( VariableName ) is str:
665 VariableNames = [VariableName,]
666 elif type( VariableName ) is list:
667 VariableNames = map( str, VariableName )
669 raise ValueError("The observer requires a name or a list of names of variables.")
671 # Association interne de l'observer à la variable
672 # -----------------------------------------------
673 for n in VariableNames:
674 if not self.__algorithm.has_key( n ):
675 raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n)
677 self.__algorithm.StoredVariables[ n ].setDataObserver(
678 Scheduler = Scheduler,
679 HookFunction = HookFunction,
680 HookParameters = HookParameters,
683 def removeDataObserver(self,
688 Permet de retirer un observer à une ou des variable nommée.
691 if type( self.__algorithm ) is dict:
692 raise ValueError("No observer can be removed before choosing an algorithm.")
694 # Vérification du nom de variable et typage
695 # -----------------------------------------
696 if type( VariableName ) is str:
697 VariableNames = [VariableName,]
698 elif type( VariableName ) is list:
699 VariableNames = map( str, VariableName )
701 raise ValueError("The observer requires a name or a list of names of variables.")
703 # Association interne de l'observer à la variable
704 # -----------------------------------------------
705 for n in VariableNames:
706 if not self.__algorithm.has_key( n ):
707 raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%n)
709 self.__algorithm.StoredVariables[ n ].removeDataObserver(
710 HookFunction = HookFunction,
713 # -----------------------------------------------------------
714 def setDebug(self, level=10):
716 Utiliser par exemple "import logging ; level = logging.DEBUG" avant cet
717 appel pour changer le niveau de verbosité, avec :
718 NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50
721 log = logging.getLogger()
722 log.setLevel( level )
724 def unsetDebug(self):
726 Remet le logger au niveau par défaut
729 log = logging.getLogger()
730 log.setLevel( logging.WARNING )
732 def prepare_to_pickle(self):
733 self.__algorithmFile = None
734 self.__diagnosticFile = None
737 # ==============================================================================
738 if __name__ == "__main__":
739 print '\n AUTODIAGNOSTIC \n'
741 ADD = AssimilationStudy("Ma premiere etude BLUE")
743 ADD.setBackground (asVector = [0, 1, 2])
744 ADD.setBackgroundError (asCovariance = "1 0 0;0 1 0;0 0 1")
745 ADD.setObservation (asVector = [0.5, 1.5, 2.5])
746 ADD.setObservationError (asCovariance = "1 0 0;0 1 0;0 0 1")
747 ADD.setObservationOperator(asMatrix = "1 0 0;0 1 0;0 0 1")
749 ADD.setAlgorithm(choice="Blue")
753 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
754 print "Ebauche :", [0, 1, 2]
755 print "Observation :", [0.5, 1.5, 2.5]
756 print "Demi-somme :", list((numpy.array([0, 1, 2])+numpy.array([0.5, 1.5, 2.5]))/2)
757 print " qui doit être identique à :"
758 print "Analyse résultante :", ADD.get("Analysis").valueserie(0)
759 print "Innovation :", ADD.get("Innovation").valueserie(0)
762 print "Algorithmes disponibles.......................:", ADD.get_available_algorithms()
763 # print " Chemin des algorithmes.....................:", ADD.get_algorithms_main_path()
764 print "Diagnostics types disponibles.................:", ADD.get_available_diagnostics()
765 # print " Chemin des diagnostics.....................:", ADD.get_diagnostics_main_path()
766 print "Variables disponibles.........................:", ADD.get_available_variables()
769 ADD.setDiagnostic("RMS", "Ma RMS")
771 liste = ADD.get().keys()
773 print "Variables et diagnostics nommés disponibles...:", liste
776 print "Exemple de mise en place d'un observeur :"
777 def obs(var=None,info=None):
778 print " ---> Mise en oeuvre de l'observer"
779 print " var =",var.valueserie(-1)
781 ADD.setDataObserver( 'Analysis', HookFunction=obs, Scheduler = [2, 4], HookParameters = "Second observer")
782 # Attention, il faut décaler le stockage de 1 pour suivre le pas interne
783 # car le pas 0 correspond à l'analyse ci-dessus.
786 print "Action sur la variable observée, étape :",i
787 ADD.get('Analysis').store( [i, i, i] )
790 print "Mise en debug et hors debug"
791 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
795 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()
797 print "Nombre d'analyses :", ADD.get("Analysis").stepnumber()