Salome HOME
83b4813e62b6eae9d49c9cf8b0b118a3fc955f14
[modules/adao.git] / src / daComposant / daCore / AssimilationStudy.py
1 #-*-coding:iso-8859-1-*-
2 #
3 #  Copyright (C) 2008-2009  EDF R&D
4 #
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.
9 #
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.
14 #
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
18 #
19 #  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21 __doc__ = """
22     Définit les outils généraux élémentaires.
23     
24     Ce module est destiné à etre appelée par AssimilationStudy pour constituer
25     les objets élémentaires de l'algorithme.
26 """
27 __author__ = "Jean-Philippe ARGAUD - Mars 2008"
28
29 import os, sys
30 import numpy
31 import Logging ; Logging.Logging() # A importer en premier
32 import Persistence
33 from BasicObjects import Operator
34
35 # ==============================================================================
36 class AssimilationStudy:
37     """
38     Cette classe sert d'interface pour l'utilisation de l'assimilation de
39     données. Elle contient les méthodes ou accesseurs nécessaires à la
40     construction d'un calcul d'assimilation.
41     """
42     def __init__(self, name=""):
43         """
44         Prévoit de conserver l'ensemble des variables nécssaires à un algorithme
45         élémentaire. Ces variables sont ensuite disponibles pour implémenter un
46         algorithme élémentaire particulier.
47
48         Background............: vecteur Xb
49         Observation...........: vecteur Y (potentiellement temporel)
50             d'observations
51         State.................: vecteur d'état dont une partie est le vecteur de
52             contrôle. Cette information n'est utile que si l'on veut faire des
53             calculs sur l'état complet, mais elle n'est pas indispensable pour
54             l'assimilation.
55         Control...............: vecteur X contenant toutes les variables de
56             contrôle, i.e. les paramètres ou l'état dont on veut estimer la
57             valeur pour obtenir les observations
58         ObservationOperator...: opérateur d'observation H
59
60         Les observations présentent une erreur dont la matrice de covariance est
61         R. L'ébauche du vecteur de contrôle présente une erreur dont la matrice
62         de covariance est B.
63         """
64         self.__name = str(name)
65         self.__Xb = None
66         self.__Y  = None
67         self.__B  = None
68         self.__R  = None
69         self.__Q  = None
70         self.__H  = {}
71         self.__M  = {}
72         #
73         self.__X  = Persistence.OneVector()
74         self.__Parameters = {}
75         self.__StoredDiagnostics = {}
76         #
77         # Variables temporaires
78         self.__algorithm     = {}
79         self.__algorithmFile = None
80         self.__algorithmName = None
81         self.__diagnosticFile = None
82         #
83         # Récupère le chemin du répertoire parent et l'ajoute au path
84         # (Cela complète l'action de la classe PathManagement dans PlatformInfo,
85         # qui est activée dans Persistence)
86         self.__parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
87         sys.path.insert(0, self.__parent)
88         sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
89
90     # ---------------------------------------------------------
91     def setBackground(self,
92             asVector           = None,
93             asPersistentVector = None,
94             Scheduler          = None,
95             ):
96         """
97         Permet de définir l'estimation a priori :
98         - asVector : entrée des données, comme un vecteur compatible avec le
99           constructeur de numpy.matrix
100         - asPersistentVector : entrée des données, comme un vecteur de type
101           persistent contruit avec la classe ad-hoc "Persistence"
102         - Scheduler est le contrôle temporel des données
103         """
104         if asVector is not None:
105             if type( asVector ) is type( numpy.matrix([]) ):
106                 self.__Xb = numpy.matrix( asVector.A1, numpy.float ).T
107             else:
108                 self.__Xb = numpy.matrix( asVector,    numpy.float ).T
109         elif asPersistentVector is not None:
110             self.__Xb = asPersistentVector
111         else:
112             raise ValueError("Error: improperly defined background")
113         return 0
114     
115     def setBackgroundError(self, asCovariance=None):
116         """
117         Permet de définir la covariance des erreurs d'ébauche :
118         - asCovariance : entrée des données, comme une matrice compatible avec
119           le constructeur de numpy.matrix
120         """
121         self.__B  = numpy.matrix( asCovariance, numpy.float )
122         return 0
123
124     # -----------------------------------------------------------
125     def setObservation(self,
126             asVector           = None,
127             asPersistentVector = None,
128             Scheduler          = None,
129             ):
130         """
131         Permet de définir les observations :
132         - asVector : entrée des données, comme un vecteur compatible avec le
133           constructeur de numpy.matrix
134         - asPersistentVector : entrée des données, comme un vecteur de type
135           persistent contruit avec la classe ad-hoc "Persistence"
136         - Scheduler est le contrôle temporel des données disponibles
137         """
138         if asVector is not None:
139             if type( asVector ) is type( numpy.matrix([]) ):
140                 self.__Y = numpy.matrix( asVector.A1, numpy.float ).T
141             else:
142                 self.__Y = numpy.matrix( asVector,    numpy.float ).T
143         elif asPersistentVector is not None:
144             self.__Y = asPersistentVector
145         else:
146             raise ValueError("Error: improperly defined observations")
147         return 0
148
149     def setObservationError(self, asCovariance=None):
150         """
151         Permet de définir la covariance des erreurs d'observations :
152         - asCovariance : entrée des données, comme une matrice compatible avec
153           le constructeur de numpy.matrix
154         """
155         self.__R  = numpy.matrix( asCovariance, numpy.float )
156         return 0
157
158     def setObservationOperator(self,
159             asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
160             asMatrix   = None,
161             appliedToX = None,
162             ):
163         """
164         Permet de définir un opérateur d'observation H. L'ordre de priorité des
165         définitions et leur sens sont les suivants :
166         - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
167           alors on définit l'opérateur à l'aide de fonctions. Si la fonction
168           "Direct" n'est pas définie, on prend la fonction "Tangent".
169         - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
170           None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
171           la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
172           matrice fournie doit être sous une forme compatible avec le
173           constructeur de numpy.matrix.
174         - si l'argument "appliedToX" n'est pas None, alors on définit, pour des
175           X divers, l'opérateur par sa valeur appliquée à cet X particulier,
176           sous la forme d'un dictionnaire appliedToX[NAME] avec NAME un nom.
177           L'opérateur doit néanmoins déjà avoir été défini comme d'habitude.
178         """
179         if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
180             if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
181                 self.__H["Direct"]  = Operator( fromMethod = asFunction["Tangent"]  )
182             else:
183                 self.__H["Direct"] = Operator( fromMethod = asFunction["Direct"]  )
184             self.__H["Tangent"]    = Operator( fromMethod = asFunction["Tangent"] )
185             self.__H["Adjoint"]    = Operator( fromMethod = asFunction["Adjoint"] )
186         elif asMatrix is not None:
187             mat = numpy.matrix( asMatrix, numpy.float )
188             self.__H["Direct"]  = Operator( fromMatrix = mat )
189             self.__H["Tangent"] = Operator( fromMatrix = mat )
190             self.__H["Adjoint"] = Operator( fromMatrix = mat.T )
191         else:
192             raise ValueError("Error: improperly defined observation operator")
193         #
194         if appliedToX is not None:
195             self.__H["AppliedToX"] = {}
196             if type(appliedToX) is not dict:
197                 raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.")
198             for key in appliedToX.keys():
199                 if type( appliedToX[key] ) is type( numpy.matrix([]) ):
200                     # Pour le cas où l'on a une vraie matrice
201                     self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T
202                 elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1:
203                     # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions
204                     self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T
205                 else:
206                     self.__H["AppliedToX"][key] = numpy.matrix( appliedToX[key],    numpy.float ).T
207         else:
208             self.__H["AppliedToX"] = None
209         #
210         return 0
211
212     # -----------------------------------------------------------
213     def setEvolutionModel(self,
214             asFunction = {"Direct":None, "Tangent":None, "Adjoint":None},
215             asMatrix   = None,
216             Scheduler  = None,
217             ):
218         """
219         Permet de définir un opérateur d'évolution M. L'ordre de priorité des
220         définitions et leur sens sont les suivants :
221         - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
222           alors on définit l'opérateur à l'aide de fonctions. Si la fonction
223           "Direct" n'est pas définie, on prend la fonction "Tangent".
224         - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
225           None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
226           la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
227           matrice fournie doit être sous une forme compatible avec le
228           constructeur de numpy.matrix.
229         """
230         if (type(asFunction) is type({})) and (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
231             if not asFunction.has_key("Direct") or (asFunction["Direct"] is None):
232                 self.__M["Direct"] = Operator( fromMethod = asFunction["Tangent"]  )
233             else:
234                 self.__M["Direct"] = Operator( fromMethod = asFunction["Direct"]  )
235             self.__M["Tangent"]    = Operator( fromMethod = asFunction["Tangent"] )
236             self.__M["Adjoint"]    = Operator( fromMethod = asFunction["Adjoint"] )
237         elif asMatrix is not None:
238             matrice = numpy.matrix( asMatrix, numpy.float )
239             self.__M["Direct"]  = Operator( fromMatrix = matrice )
240             self.__M["Tangent"] = Operator( fromMatrix = matrice )
241             self.__M["Adjoint"] = Operator( fromMatrix = matrice.T )
242         else:
243             raise ValueError("Error: improperly defined evolution operator")
244         return 0
245
246     def setEvolutionError(self, asCovariance=None):
247         """
248         Permet de définir la covariance des erreurs de modèle :
249         - asCovariance : entrée des données, comme une matrice compatible avec
250           le constructeur de numpy.matrix
251         """
252         self.__Q  = numpy.matrix( asCovariance, numpy.float )
253         return 0
254
255     # -----------------------------------------------------------
256     def setControls (self, asVector = None ):
257         """
258         Permet de définir la valeur initiale du vecteur X contenant toutes les
259         variables de contrôle, i.e. les paramètres ou l'état dont on veut
260         estimer la valeur pour obtenir les observations. C'est utile pour un
261         algorithme itératif/incrémental
262         - asVector : entrée des données, comme un vecteur compatible avec le
263           constructeur de numpy.matrix.
264         """
265         if asVector is not None:
266             self.__X.store( asVector )
267         return 0
268
269     # -----------------------------------------------------------
270     def setAlgorithm(self, choice = None ):
271         """
272         Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
273         d'assimilation. L'argument est un champ caractère se rapportant au nom
274         d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération
275         d'assimilation sur les arguments (Xb,Y,H,R,B,Xa).
276         """
277         if choice is None:
278             raise ValueError("Error: algorithm choice has to be given")
279         if self.__algorithmName is not None:
280             raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
281         daDirectory = "daAlgorithms"
282         #
283         # Recherche explicitement le fichier complet
284         # ------------------------------------------
285         module_path = None
286         for directory in sys.path:
287             if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
288                 module_path = os.path.abspath(os.path.join(directory, daDirectory))
289         if module_path is None:
290             raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n             The search path is %s"%(choice, daDirectory, sys.path))
291         #
292         # Importe le fichier complet comme un module
293         # ------------------------------------------
294         try:
295             sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
296             self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
297             self.__algorithmName = str(choice)
298             sys.path = sys_path_tmp ; del sys_path_tmp
299         except ImportError, e:
300             raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n             The import error message is: %s"%(choice,e))
301         #
302         # Instancie un objet du type élémentaire du fichier
303         # -------------------------------------------------
304         self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
305         return 0
306
307     def setAlgorithmParameters(self, asDico=None):
308         """
309         Permet de définir les paramètres de l'algorithme, sous la forme d'un
310         dictionnaire.
311         """
312         self.__Parameters = dict( asDico )
313         return 0
314
315     # -----------------------------------------------------------
316     def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ):
317         """
318         Permet de sélectionner un diagnostic a effectuer.
319         """
320         if choice is None:
321             raise ValueError("Error: diagnostic choice has to be given")
322         daDirectory = "daDiagnostics"
323         #
324         # Recherche explicitement le fichier complet
325         # ------------------------------------------
326         module_path = None
327         for directory in sys.path:
328             if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
329                 module_path = os.path.abspath(os.path.join(directory, daDirectory))
330         if module_path is None:
331             raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n             The search path is %s"%(choice, daDirectory, sys.path))
332         #
333         # Importe le fichier complet comme un module
334         # ------------------------------------------
335         try:
336             sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
337             self.__diagnosticFile = __import__(str(choice), globals(), locals(), [])
338             sys.path = sys_path_tmp ; del sys_path_tmp
339         except ImportError, e:
340             raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n             The import error message is: %s"%(choice,e))
341         #
342         # Instancie un objet du type élémentaire du fichier
343         # -------------------------------------------------
344         if self.__StoredDiagnostics.has_key(name):
345             raise ValueError("A diagnostic with the same name already exists")
346         else:
347             self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic(
348                 name       = name,
349                 unit       = unit,
350                 basetype   = basetype,
351                 parameters = parameters )
352         return 0
353
354     # -----------------------------------------------------------
355     def shape_validate(self):
356         """
357         Validation de la correspondance correcte des tailles des variables et
358         des matrices s'il y en a.
359         """
360         if self.__Xb is None:                  __Xb_shape = (0,)
361         elif hasattr(self.__Xb,"shape"):
362             if type(self.__Xb.shape) is tuple: __Xb_shape = self.__Xb.shape
363             else:                              __Xb_shape = self.__Xb.shape()
364         else: raise TypeError("Xb has no attribute of shape: problem !")
365         #
366         if self.__Y is None:                  __Y_shape = (0,)
367         elif hasattr(self.__Y,"shape"):
368             if type(self.__Y.shape) is tuple: __Y_shape = self.__Y.shape
369             else:                             __Y_shape = self.__Y.shape()
370         else: raise TypeError("Y has no attribute of shape: problem !")
371         #
372         if self.__B is None:                  __B_shape = (0,0)
373         elif hasattr(self.__B,"shape"):
374             if type(self.__B.shape) is tuple: __B_shape = self.__B.shape
375             else:                             __B_shape = self.__B.shape()
376         else: raise TypeError("B has no attribute of shape: problem !")
377         #
378         if self.__R is None:                  __R_shape = (0,0)
379         elif hasattr(self.__R,"shape"):
380             if type(self.__R.shape) is tuple: __R_shape = self.__R.shape
381             else:                             __R_shape = self.__R.shape()
382         else: raise TypeError("R has no attribute of shape: problem !")
383         #
384         if self.__Q is None:                  __Q_shape = (0,0)
385         elif hasattr(self.__Q,"shape"):
386             if type(self.__Q.shape) is tuple: __Q_shape = self.__Q.shape
387             else:                             __Q_shape = self.__Q.shape()
388         else: raise TypeError("Q has no attribute of shape: problem !")
389         #
390         if len(self.__H) == 0:                          __H_shape = (0,0)
391         elif type(self.__H) is type({}):                __H_shape = (0,0)
392         elif hasattr(self.__H["Direct"],"shape"):
393             if type(self.__H["Direct"].shape) is tuple: __H_shape = self.__H["Direct"].shape
394             else:                                       __H_shape = self.__H["Direct"].shape()
395         else: raise TypeError("H has no attribute of shape: problem !")
396         #
397         if len(self.__M) == 0:                          __M_shape = (0,0)
398         elif type(self.__M) is type({}):                __M_shape = (0,0)
399         elif hasattr(self.__M["Direct"],"shape"):
400             if type(self.__M["Direct"].shape) is tuple: __M_shape = self.__M["Direct"].shape
401             else:                                       __M_shape = self.__M["Direct"].shape()
402         else: raise TypeError("M has no attribute of shape: problem !")
403         #
404         # Vérification des conditions
405         # ---------------------------
406         if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
407             raise ValueError("Shape characteristic of Xb is incorrect: \"%s\""%(__Xb_shape,))
408         if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
409             raise ValueError("Shape characteristic of Y is incorrect: \"%s\""%(__Y_shape,))
410         #
411         if not( min(__B_shape) == max(__B_shape) ):
412             raise ValueError("Shape characteristic of B is incorrect: \"%s\""%(__B_shape,))
413         if not( min(__R_shape) == max(__R_shape) ):
414             raise ValueError("Shape characteristic of R is incorrect: \"%s\""%(__R_shape,))
415         if not( min(__Q_shape) == max(__Q_shape) ):
416             raise ValueError("Shape characteristic of Q is incorrect: \"%s\""%(__Q_shape,))
417         if not( min(__M_shape) == max(__M_shape) ):
418             raise ValueError("Shape characteristic of M is incorrect: \"%s\""%(__M_shape,))
419         #
420         if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[1] == max(__Xb_shape) ):
421             raise ValueError("Shape characteristic of H \"%s\" and X \"%s\" are incompatible"%(__H_shape,__Xb_shape))
422         if len(self.__H) > 0 and not(type(self.__H) is type({})) and not( __H_shape[0] == max(__Y_shape) ):
423             raise ValueError("Shape characteristic of H \"%s\" and Y \"%s\" are incompatible"%(__H_shape,__Y_shape))
424         if len(self.__H) > 0 and not(type(self.__H) is type({})) and len(self.__B) > 0 and not( __H_shape[1] == __B_shape[0] ):
425             raise ValueError("Shape characteristic of H \"%s\" and B \"%s\" are incompatible"%(__H_shape,__B_shape))
426         if len(self.__H) > 0 and not(type(self.__H) is type({})) and len(self.__R) > 0 and not( __H_shape[0] == __R_shape[1] ):
427             raise ValueError("Shape characteristic of H \"%s\" and R \"%s\" are incompatible"%(__H_shape,__R_shape))
428         #
429         if len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
430             raise ValueError("Shape characteristic of B \"%s\" and Xb \"%s\" are incompatible"%(__B_shape,__Xb_shape))
431         #
432         if len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
433             raise ValueError("Shape characteristic of R \"%s\" and Y \"%s\" are incompatible"%(__R_shape,__Y_shape))
434         #
435         if len(self.__M) > 0 and not(type(self.__M) is type({})) and not( __M_shape[1] == max(__Xb_shape) ):
436             raise ValueError("Shape characteristic of M \"%s\" and X \"%s\" are incompatible"%(__M_shape,__Xb_shape))
437         #
438         return 1
439
440     # -----------------------------------------------------------
441     def analyze(self):
442         """
443         Permet de lancer le calcul d'assimilation.
444         
445         Le nom de la méthode à activer est toujours "run". Les paramètres en
446         arguments de la méthode sont fixés. En sortie, on obtient les résultats
447         dans la variable de type dictionnaire "StoredVariables", qui contient en
448         particulier des objets de Persistence pour les analyses, OMA...
449         """
450         self.shape_validate()
451         #
452         self.__algorithm.run(
453             Xb  = self.__Xb,
454             Y   = self.__Y,
455             H   = self.__H,
456             M   = self.__M,
457             R   = self.__R,
458             B   = self.__B,
459             Q   = self.__Q,
460             Par = self.__Parameters,
461             )
462         return 0
463
464     # -----------------------------------------------------------
465     def get(self, key=None):
466         """
467         Renvoie les résultats disponibles après l'exécution de la méthode
468         d'assimilation, ou les diagnostics disponibles. Attention, quand un
469         diagnostic porte le même nom qu'un variable stockée, c'est la variable
470         stockée qui est renvoyée, et le diagnostic est inatteignable.
471         """
472         if key is not None:
473             if self.__algorithm.has_key(key):
474                 return self.__algorithm.get( key )
475             elif self.__StoredDiagnostics.has_key(key):
476                 return self.__StoredDiagnostics[key]
477             else:
478                 raise ValueError("The requested key \"%s\" does not exists as a diagnostic or as a stored variable."%key)
479         else:
480             allvariables = self.__algorithm.get()
481             allvariables.update( self.__StoredDiagnostics )
482             return allvariables
483     
484     def get_available_algorithms(self):
485         """
486         Renvoie la liste des algorithmes identifiés par les chaînes de
487         caractères
488         """
489         files = []
490         for directory in sys.path:
491             if os.path.isdir(os.path.join(directory,"daAlgorithms")):
492                 for fname in os.listdir(os.path.join(directory,"daAlgorithms")):
493                     root, ext = os.path.splitext(fname)
494                     if ext == '.py' and root != '__init__':
495                         files.append(root)
496         files.sort()
497         return files
498         
499     def get_available_diagnostics(self):
500         """
501         Renvoie la liste des diagnostics identifiés par les chaînes de
502         caractères
503         """
504         files = []
505         for directory in sys.path:
506             if os.path.isdir(os.path.join(directory,"daDiagnostics")):
507                 for fname in os.listdir(os.path.join(directory,"daDiagnostics")):
508                     root, ext = os.path.splitext(fname)
509                     if ext == '.py' and root != '__init__':
510                         files.append(root)
511         files.sort()
512         return files
513
514     # -----------------------------------------------------------
515     def get_algorithms_main_path(self):
516         """
517         Renvoie le chemin pour le répertoire principal contenant les algorithmes
518         dans un sous-répertoire "daAlgorithms"
519         """
520         return self.__parent
521
522     def add_algorithms_path(self, asPath=None):
523         """
524         Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
525         se trouve un sous-répertoire "daAlgorithms"
526         
527         Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est
528         pas indispensable de le rajouter ici.
529         """
530         if not os.path.isdir(asPath):
531             raise ValueError("The given "+asPath+" argument must exist as a directory")
532         if not os.path.isdir(os.path.join(asPath,"daAlgorithms")):
533             raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"")
534         if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")):
535             raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"")
536         sys.path.insert(0, os.path.abspath(asPath))
537         sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
538         return 1
539
540     def get_diagnostics_main_path(self):
541         """
542         Renvoie le chemin pour le répertoire principal contenant les diagnostics
543         dans un sous-répertoire "daDiagnostics"
544         """
545         return self.__parent
546
547     def add_diagnostics_path(self, asPath=None):
548         """
549         Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
550         se trouve un sous-répertoire "daDiagnostics"
551         
552         Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est
553         pas indispensable de le rajouter ici.
554         """
555         if not os.path.isdir(asPath):
556             raise ValueError("The given "+asPath+" argument must exist as a directory")
557         if not os.path.isdir(os.path.join(asPath,"daDiagnostics")):
558             raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"")
559         if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")):
560             raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"")
561         sys.path.insert(0, os.path.abspath(asPath))
562         sys.path = list(set(sys.path)) # Conserve en unique exemplaire chaque chemin
563         return 1
564
565 # ==============================================================================
566 if __name__ == "__main__":
567     print '\n AUTODIAGNOSTIC \n'
568     
569     ADD = AssimilationStudy("Ma premiere etude BLUE")
570     
571     ADD.setBackground         (asVector     = [0, 1, 2])
572     ADD.setBackgroundError    (asCovariance = "1 0 0;0 1 0;0 0 1")
573     ADD.setObservation        (asVector     = [0.5, 1.5, 2.5])
574     ADD.setObservationError   (asCovariance = "1 0 0;0 1 0;0 0 1")
575     ADD.setObservationOperator(asMatrix     = "1 0 0;0 1 0;0 0 1")
576     
577     ADD.setAlgorithm(choice="Blue")
578     
579     ADD.analyze()
580     
581     print "Nombre d'analyses  :", ADD.get("Analysis").stepnumber()
582     print "Analyse résultante :", ADD.get("Analysis").valueserie(0)
583     print "Innovation         :", ADD.get("Innovation").valueserie(0)
584     print
585     
586     print "Algorithmes disponibles :", ADD.get_available_algorithms()
587     # print " Chemin des algorithmes :", ADD.get_algorithms_main_path()
588     print "Diagnostics disponibles :", ADD.get_available_diagnostics()
589     # print " Chemin des diagnostics :", ADD.get_diagnostics_main_path()
590     print
591
592     ADD.setDiagnostic("RMS", "Ma RMS")
593     
594     liste = ADD.get().keys()
595     liste.sort()
596     print "Variables et diagnostics disponibles :", liste
597     print
598