Salome HOME
Light cleaning of python code after pychecking
[modules/adao.git] / src / daComposant / daCore / Persistence.py
1 #-*-coding:iso-8859-1-*-
2 #
3 # Copyright (C) 2008-2012 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 # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
22
23 __doc__ = """
24     Définit des outils de persistence et d'enregistrement de séries de valeurs
25     pour analyse ultérieure ou utilisation de calcul.
26 """
27 __author__ = "Jean-Philippe ARGAUD"
28
29 import numpy
30
31 from PlatformInfo import PathManagement ; PathManagement()
32
33 # ==============================================================================
34 class Persistence:
35     """
36     Classe générale de persistence définissant les accesseurs nécessaires
37     (Template)
38     """
39     def __init__(self, name="", unit="", basetype=str):
40         """
41         name : nom courant
42         unit : unité
43         basetype : type de base de l'objet stocké à chaque pas
44         
45         La gestion interne des données est exclusivement basée sur les variables
46         initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
47         objets comme des attributs) :
48         __step   : numérotation par défaut du pas courant
49         __basetype : le type de base de chaque valeur, sous la forme d'un type
50                      permettant l'instanciation ou le casting Python 
51         __steps  : les pas de stockage. Par défaut, c'est __step
52         __values : les valeurs de stockage. Par défaut, c'est None
53         """
54         self.__name = str(name)
55         self.__unit = str(unit)
56         #
57         self.__step     = -1
58         self.__basetype = basetype
59         #
60         self.__steps    = []
61         self.__values   = []
62         self.__tags     = []
63         self.__tagkeys  = {}
64         #
65         self.__dynamic  = False
66         #
67         self.__dataobservers = []
68     
69     def basetype(self, basetype=None):
70         """
71         Renvoie ou met en place le type de base des objets stockés
72         """
73         if basetype is None:
74             return self.__basetype
75         else:
76             self.__basetype = basetype
77
78     def store(self, value=None, step=None, tags={}):
79         """
80         Stocke une valeur à un pas. Une instanciation est faite avec le type de
81         base pour stocker l'objet. Si le pas n'est pas fournit, on utilise
82         l'étape de stockage comme valeur de pas.
83         """
84         if value is None: raise ValueError("Value argument required")
85         self.__step += 1
86         if step is not None:
87             self.__steps.append(step)
88         else:
89             self.__steps.append(self.__step)
90         #
91         self.__values.append(self.__basetype(value))
92         #
93         self.__tags.append(   dict(tags))
94         self.__tagkeys.update(dict(tags))
95         #
96         if self.__dynamic: self.__replots()
97         for hook, parameters, scheduler in self.__dataobservers:
98             if self.__step in scheduler:
99                 hook( self, parameters )
100
101     def shape(self):
102         """
103         Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
104         objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
105         complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
106         longueur. Par défaut, renvoie 1.
107         """
108         if len(self.__values) > 0:
109             if self.__basetype in [numpy.matrix, numpy.array]:
110                 return self.__values[-1].shape
111             elif self.__basetype in [int, float]:
112                 return (1,)
113             elif self.__basetype in [list, dict]:
114                 return (len(self.__values[-1]),)
115             else:
116                 return (1,)
117         else:
118             raise ValueError("Object has no shape before its first storage")
119
120     def __len__(self):
121         """
122         Renvoie le nombre d'éléments dans un séquence ou la plus grande
123         dimension d'une matrice
124         """
125         return max( self.shape() )
126
127     # ---------------------------------------------------------
128     def itemserie(self, item=None, step=None, tags=None,
129                         allSteps=False):
130         """
131         Les "item" sont les index de la liste des pas de "step". Ils sont
132         renvoyés par cette fonction selon les filtres définis par les mots-clés.
133         
134         Les comportements sont les suivants :
135             - Renvoie par défaut toute la liste des index.
136             - Si l'argument "item" est valide, renvoie uniquement cet index.
137             - Si l'argument "step" existe dans la liste des pas de stockage,
138               renvoie le premier index (si allSteps=False) ou tous les index
139               (si allSteps=True) de ce "step" dans les pas de stockage.
140             - Si l'argument "tags" est un dictionnaire correct, renvoie les
141               index des pas caractérisés par toutes les paires "tag/valeur" des
142               tags indiqués, ou rien sinon.
143         
144         Cette méthode est à vocation interne pour simplifier les accès aux pas
145         par la méthode "stepserie", aux attributs par la méthode "tagserie" et
146         aux valeurs par la méthode "valueserie".
147         """
148         #
149         # Cherche l'item demandé
150         if item is not None and item < len(self.__steps):
151             return [item,]
152         #
153         # Cherche le ou les items dont le "step" est demandé
154         elif step is not None and step in self.__steps:
155             if allSteps:
156                 allIndexes = []
157                 searchFrom = 0
158                 try:
159                     while self.__steps.index(step,searchFrom) >= 0:
160                         searchFrom = self.__steps.index(step,searchFrom)
161                         allIndexes.append( searchFrom )
162                         searchFrom +=1
163                 except ValueError, e:
164                     pass
165                 return allIndexes
166             else:
167                 return [self.__steps.index(step),]
168         #
169         # Cherche le ou les items dont les "tags" sont demandés
170         elif tags is not None and type(tags) is dict :
171             allIndexes = []
172             for i, attributs in enumerate(self.__tags):           # Boucle sur les attributs de chaque pas
173                 selection = True                                  # Booleen permettant de traiter la combinaison "ET" des tags
174                 for key in tags.keys():                           # Boucle sur tous les tags de filtrage
175                     if key not in self.__tagkeys.keys(): continue # Passe au suivant s'il n'existe nulle part
176                     if not( key in attributs.keys() and attributs[key] == tags[key] ):
177                         selection = False
178                 if selection:
179                     allIndexes.append(i)
180             allIndexes = list(set(allIndexes))
181             allIndexes.sort()
182             return allIndexes
183         #
184         # Renvoie par défaut tous les items valides
185         else:
186             return range(len(self.__steps))
187
188     def stepserie(self, item=None, step=None, tags=None):
189         """
190         Les "step" sont les pas nommés de stockage. Par défaut, s'il ne sont pas
191         définis explicitement, ils sont identiques aux index de stockage. Ils
192         sont renvoyés par cette fonction selon les filtres définis par les
193         mots-clés.
194         
195         Les comportements sont les suivants :
196             - Renvoie par défaut toute la liste des pas.
197             - Si l'argument "item" est valide, renvoie le pas à cet index.
198             - Si l'argument "step" existe dans la liste des pas, le renvoie.
199             - Si l'argument "tags" est un dictionnaire correct, renvoie les pas
200               caractérisés par toutes les paires "tag/valeur" des tags indiqués,
201               ou rien sinon.
202         """
203         if item is not None and item < len(self.__steps):
204             return self.__steps[item]
205         elif step is not None and step in self.__steps:
206             return step
207         elif tags is not None:
208             allIndexes = self.itemserie(tags = tags)
209             return [self.__steps[index] for index in allIndexes]
210         else:
211             return self.__steps
212
213     def valueserie(self, item=None, step=None, tags=None,
214                          allSteps=False):
215         """
216         Les valeurs stockées sont renvoyées par cette fonction selon les filtres
217         définis par les mots-clés.
218         
219         Les comportements sont les suivants :
220             - Renvoie par défaut toute la liste des valeurs.
221             - Si l'argument "item" est valide, renvoie la valeur à cet index.
222             - Si l'argument "step" existe dans la liste des pas de stockage,
223               renvoie la première valeur (si allSteps=False) ou toutes les
224               valeurs (si allSteps=True).
225             - Si l'argument "tags" est un dictionnaire correct, renvoie les
226               valeurs aux pas caractérisés par toutes les paires "tag/valeur"
227               des tags indiqués, ou rien sinon.
228         """
229         if item is not None and item < len(self.__values):
230             return self.__values[item]
231         elif step is not None:
232             allIndexes = self.itemserie(step = step, allSteps = allSteps)
233             if allSteps:
234                 return [self.__values[index] for index in allIndexes]
235             else:
236                 return self.__values[allIndexes[0]]
237         elif tags is not None:
238             allIndexes = self.itemserie(tags = tags)
239             return [self.__values[index] for index in allIndexes]
240         else:
241             return self.__values
242     
243     def tagserie(self, item=None, step=None, tags=None,
244                        allSteps=False, withValues=False,
245                        outputTag=None):
246         """
247         Les "tag" sont les attributs nommés, sous forme de paires "clé/valeur",
248         qu'il est possible d'associer avec chaque pas de stockage. Par défaut,
249         s'il ne sont pas définis explicitement, il n'y en a pas. Ils sont
250         renvoyés par cette fonction selon les filtres définis par les mots-clés.
251         On obtient uniquement la liste des clés de tags avec "withValues=False"
252         ou la liste des paires "clé/valeurs" avec "withValues=True".
253         
254         On peut aussi obtenir les valeurs d'un tag satisfaisant aux conditions
255         de filtrage en "item/step/tags" en donnant le nom du tag dans
256         "outputTag".
257
258         Les comportements sont les suivants :
259             - Renvoie par défaut toute la liste des tags.
260             - Si l'argument "item" est valide, renvoie le tag à cet index.
261             - Si l'argument "step" existe dans la liste des pas de stockage,
262               renvoie les tags du premier pas (si allSteps=False) ou la liste
263               des tags de tous les pas (si allSteps=True).
264             - Si l'argument "tags" est un dictionnaire correct, renvoie les
265               valeurs aux pas caractérisés par toutes les paires "tag/valeur"
266               des tags indiqués, ou rien sinon.
267         """
268         #
269         # Cherche tous les index satisfaisant les conditions
270         allIndexes = self.itemserie(item = item, step = step, tags = tags, allSteps = allSteps)
271         #
272         # Dans le cas où la sortie donne les valeurs d'un "outputTag"
273         if outputTag is not None and type(outputTag) is str :
274             outputValues = []
275             for index in allIndexes:
276                 if outputTag in self.__tags[index].keys():
277                     outputValues.append( self.__tags[index][outputTag] )
278             outputValues = list(set(outputValues))
279             outputValues.sort()
280             return outputValues
281         #
282         # Dans le cas où la sortie donne les tags satisfaisants aux conditions
283         else:
284             if withValues:
285                 return [self.__tags[index] for index in allIndexes]
286             else:
287                 allTags = {}
288                 for index in allIndexes:
289                     allTags.update( self.__tags[index] )
290                 allKeys = allTags.keys()
291                 allKeys.sort()
292                 return allKeys
293
294     def stepnumber(self):
295         """
296         Renvoie le nombre de pas de stockage.
297         """
298         return len(self.__steps)
299
300     # ---------------------------------------------------------
301     # Méthodes d'accès de type dictionnaire
302     def keys(self):
303         return self.stepserie()
304
305     def values(self):
306         return self.valueserie()
307
308     def items(self):
309         pairs = []
310         for i in xrange(self.stepnumber()):
311             pairs.append( (self.stepserie(item=i), self.valueserie(item=i)) )
312         return pairs
313
314     # ---------------------------------------------------------
315     def means(self):
316         """
317         Renvoie la série, contenant à chaque pas, la valeur moyenne des données
318         au pas. Il faut que le type de base soit compatible avec les types
319         élémentaires numpy.
320         """
321         try:
322             return [numpy.matrix(item).mean() for item in self.__values]
323         except:
324             raise TypeError("Base type is incompatible with numpy")
325
326     def stds(self, ddof=0):
327         """
328         Renvoie la série, contenant à chaque pas, l'écart-type des données
329         au pas. Il faut que le type de base soit compatible avec les types
330         élémentaires numpy.
331         
332         ddof : c'est le nombre de degrés de liberté pour le calcul de
333                l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
334         """
335         try:
336             if numpy.version.version >= '1.1.0':
337                 return [numpy.matrix(item).std(ddof=ddof) for item in self.__values]
338             else:
339                 return [numpy.matrix(item).std() for item in self.__values]
340         except:
341             raise TypeError("Base type is incompatible with numpy")
342
343     def sums(self):
344         """
345         Renvoie la série, contenant à chaque pas, la somme des données au pas.
346         Il faut que le type de base soit compatible avec les types élémentaires
347         numpy.
348         """
349         try:
350             return [numpy.matrix(item).sum() for item in self.__values]
351         except:
352             raise TypeError("Base type is incompatible with numpy")
353
354     def mins(self):
355         """
356         Renvoie la série, contenant à chaque pas, le minimum des données au pas.
357         Il faut que le type de base soit compatible avec les types élémentaires
358         numpy.
359         """
360         try:
361             return [numpy.matrix(item).min() for item in self.__values]
362         except:
363             raise TypeError("Base type is incompatible with numpy")
364
365     def maxs(self):
366         """
367         Renvoie la série, contenant à chaque pas, la maximum des données au pas.
368         Il faut que le type de base soit compatible avec les types élémentaires
369         numpy.
370         """
371         try:
372             return [numpy.matrix(item).max() for item in self.__values]
373         except:
374             raise TypeError("Base type is incompatible with numpy")
375
376     def __preplots(self,
377             title    = "",
378             xlabel   = "",
379             ylabel   = "",
380             ltitle   = None,
381             geometry = "600x400",
382             persist  = False,
383             pause    = True,
384             ):
385         #
386         # Vérification de la disponibilité du module Gnuplot
387         try:
388             import Gnuplot
389             self.__gnuplot = Gnuplot
390         except:
391             raise ImportError("The Gnuplot module is required to plot the object.")
392         #
393         # Vérification et compléments sur les paramètres d'entrée
394         if persist:
395             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
396         else:
397             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
398         if ltitle is None:
399             ltitle = ""
400         self.__g = self.__gnuplot.Gnuplot() # persist=1
401         self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
402         self.__g('set style data lines')
403         self.__g('set grid')
404         self.__g('set autoscale')
405         self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
406         self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
407         self.__title  = title
408         self.__ltitle = ltitle
409         self.__pause  = pause
410
411     def plots(self, item=None, step=None,
412             steps    = None,
413             title    = "",
414             xlabel   = "",
415             ylabel   = "",
416             ltitle   = None,
417             geometry = "600x400",
418             filename = "",
419             dynamic  = False,
420             persist  = False,
421             pause    = True,
422             ):
423         """
424         Renvoie un affichage de la valeur à chaque pas, si elle est compatible
425         avec un affichage Gnuplot (donc essentiellement un vecteur). Si
426         l'argument "step" existe dans la liste des pas de stockage effectués,
427         renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
428         "item" est correct, renvoie l'affichage de la valeur stockée au numéro
429         "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
430         affichage successif de tous les pas.
431
432         Arguments :
433             - step     : valeur du pas à afficher
434             - item     : index de la valeur à afficher
435             - steps    : liste unique des pas de l'axe des X, ou None si c'est
436                          la numérotation par défaut
437             - title    : base du titre général, qui sera automatiquement
438                          complétée par la mention du pas
439             - xlabel   : label de l'axe des X
440             - ylabel   : label de l'axe des Y
441             - ltitle   : titre associé au vecteur tracé
442             - geometry : taille en pixels de la fenêtre et position du coin haut
443                          gauche, au format X11 : LxH+X+Y (défaut : 600x400)
444             - filename : base de nom de fichier Postscript pour une sauvegarde,
445                          qui est automatiquement complétée par le numéro du
446                          fichier calculé par incrément simple de compteur
447             - dynamic  : effectue un affichage des valeurs à chaque stockage
448                          (au-delà du second). La méthode "plots" permet de
449                          déclarer l'affichage dynamique, et c'est la méthode
450                          "__replots" qui est utilisée pour l'effectuer
451             - persist  : booléen indiquant que la fenêtre affichée sera
452                          conservée lors du passage au dessin suivant
453                          Par défaut, persist = False
454             - pause    : booléen indiquant une pause après chaque tracé, et
455                          attendant un Return
456                          Par défaut, pause = True
457         """
458         import os
459         if not self.__dynamic:
460             self.__preplots(title, xlabel, ylabel, ltitle, geometry, persist, pause )
461             if dynamic:
462                 self.__dynamic = True
463                 if len(self.__values) == 0: return 0
464         #
465         # Tracé du ou des vecteurs demandés
466         indexes = []
467         if step is not None and step in self.__steps:
468             indexes.append(self.__steps.index(step))
469         elif item is not None and item < len(self.__values):
470             indexes.append(item)
471         else:
472             indexes = indexes + range(len(self.__values))
473         #
474         i = -1
475         for index in indexes:
476             self.__g('set title  "'+str(title).encode('ascii','replace')+' (pas '+str(index)+')"')
477             if ( type(steps) is list ) or ( type(steps) is type(numpy.array([])) ):
478                 Steps = list(steps)
479             else:
480                 Steps = range(len(self.__values[index]))
481             #
482             self.__g.plot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
483             #
484             if filename != "":
485                 i += 1
486                 stepfilename = "%s_%03i.ps"%(filename,i)
487                 if os.path.isfile(stepfilename):
488                     raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
489                 self.__g.hardcopy(filename=stepfilename, color=1)
490             if self.__pause:
491                 raw_input('Please press return to continue...\n')
492
493     def __replots(self):
494         """
495         Affichage dans le cas du suivi dynamique de la variable
496         """
497         if self.__dynamic and len(self.__values) < 2: return 0
498         #
499         self.__g('set title  "'+str(self.__title).encode('ascii','replace'))
500         Steps = range(len(self.__values))
501         self.__g.plot( self.__gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
502         #
503         if self.__pause:
504             raw_input('Please press return to continue...\n')
505
506     # ---------------------------------------------------------
507     def mean(self):
508         """
509         Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
510         longueur des pas. Il faut que le type de base soit compatible avec
511         les types élémentaires numpy.
512         """
513         try:
514             if self.__basetype in [int, float]:
515                 return float( numpy.array(self.__values).mean() )
516             else:
517                 return numpy.array(self.__values).mean(axis=0)
518         except:
519             raise TypeError("Base type is incompatible with numpy")
520
521     def std(self, ddof=0):
522         """
523         Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
524         longueur des pas. Il faut que le type de base soit compatible avec
525         les types élémentaires numpy.
526         
527         ddof : c'est le nombre de degrés de liberté pour le calcul de
528                l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
529         """
530         try:
531             if numpy.version.version >= '1.1.0':
532                 return numpy.array(self.__values).std(ddof=ddof,axis=0)
533             else:
534                 return numpy.array(self.__values).std(axis=0)
535         except:
536             raise TypeError("Base type is incompatible with numpy")
537
538     def sum(self):
539         """
540         Renvoie la somme de toutes les valeurs sans tenir compte de la
541         longueur des pas. Il faut que le type de base soit compatible avec
542         les types élémentaires numpy.
543         """
544         try:
545             return numpy.array(self.__values).sum(axis=0)
546         except:
547             raise TypeError("Base type is incompatible with numpy")
548
549     def min(self):
550         """
551         Renvoie le minimum de toutes les valeurs sans tenir compte de la
552         longueur des pas. Il faut que le type de base soit compatible avec
553         les types élémentaires numpy.
554         """
555         try:
556             return numpy.array(self.__values).min(axis=0)
557         except:
558             raise TypeError("Base type is incompatible with numpy")
559
560     def max(self):
561         """
562         Renvoie le maximum de toutes les valeurs sans tenir compte de la
563         longueur des pas. Il faut que le type de base soit compatible avec
564         les types élémentaires numpy.
565         """
566         try:
567             return numpy.array(self.__values).max(axis=0)
568         except:
569             raise TypeError("Base type is incompatible with numpy")
570
571     def cumsum(self):
572         """
573         Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
574         longueur des pas. Il faut que le type de base soit compatible avec
575         les types élémentaires numpy.
576         """
577         try:
578             return numpy.array(self.__values).cumsum(axis=0)
579         except:
580             raise TypeError("Base type is incompatible with numpy")
581
582     # On pourrait aussi utiliser les autres attributs d'une "matrix", comme
583     # "tofile", "min"...
584
585     def plot(self,
586             steps    = None,
587             title    = "",
588             xlabel   = "",
589             ylabel   = "",
590             ltitle   = None,
591             geometry = "600x400",
592             filename = "",
593             persist  = False,
594             pause    = True,
595             ):
596         """
597         Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
598         elles sont compatibles avec un affichage Gnuplot (donc essentiellement
599         un vecteur). Si l'argument "step" existe dans la liste des pas de
600         stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
601         "step". Si l'argument "item" est correct, renvoie l'affichage de la
602         valeur stockée au numéro "item".
603
604         Arguments :
605             - steps    : liste unique des pas de l'axe des X, ou None si c'est
606                          la numérotation par défaut
607             - title    : base du titre général, qui sera automatiquement
608                          complétée par la mention du pas
609             - xlabel   : label de l'axe des X
610             - ylabel   : label de l'axe des Y
611             - ltitle   : titre associé au vecteur tracé
612             - geometry : taille en pixels de la fenêtre et position du coin haut
613                          gauche, au format X11 : LxH+X+Y (défaut : 600x400)
614             - filename : nom de fichier Postscript pour une sauvegarde
615             - persist  : booléen indiquant que la fenêtre affichée sera
616                          conservée lors du passage au dessin suivant
617                          Par défaut, persist = False
618             - pause    : booléen indiquant une pause après chaque tracé, et
619                          attendant un Return
620                          Par défaut, pause = True
621         """
622         #
623         # Vérification de la disponibilité du module Gnuplot
624         try:
625             import Gnuplot
626             self.__gnuplot = Gnuplot
627         except:
628             raise ImportError("The Gnuplot module is required to plot the object.")
629         #
630         # Vérification et compléments sur les paramètres d'entrée
631         if persist:
632             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
633         else:
634             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
635         if ltitle is None:
636             ltitle = ""
637         if ( type(steps) is list ) or ( type(steps) is type(numpy.array([])) ):
638             Steps = list(steps)
639         else:
640             Steps = range(len(self.__values[0]))
641         self.__g = self.__gnuplot.Gnuplot() # persist=1
642         self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
643         self.__g('set style data lines')
644         self.__g('set grid')
645         self.__g('set autoscale')
646         self.__g('set title  "'+str(title).encode('ascii','replace') +'"')
647         self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
648         self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
649         #
650         # Tracé du ou des vecteurs demandés
651         indexes = range(len(self.__values))
652         self.__g.plot( self.__gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
653         for index in indexes:
654             self.__g.replot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
655         #
656         if filename != "":
657             self.__g.hardcopy(filename=filename, color=1)
658         if pause:
659             raw_input('Please press return to continue...\n')
660
661     # ---------------------------------------------------------
662     def setDataObserver(self,
663         HookFunction   = None,
664         HookParameters = None,
665         Scheduler      = None,
666         ):
667         """
668         Association à la variable d'un triplet définissant un observer
669         
670         Le Scheduler attendu est une fréquence, une simple liste d'index ou un
671         xrange des index.
672         """
673         #
674         # Vérification du Scheduler
675         # -------------------------
676         maxiter = int( 1e9 )
677         if type(Scheduler) is int:    # Considéré comme une fréquence à partir de 0
678             Schedulers = xrange( 0, maxiter, int(Scheduler) )
679         elif type(Scheduler) is xrange: # Considéré comme un itérateur
680             Schedulers = Scheduler
681         elif type(Scheduler) is list: # Considéré comme des index explicites
682             Schedulers = map( long, Scheduler )
683         else:                         # Dans tous les autres cas, activé par défaut
684             Schedulers = xrange( 0, maxiter )
685         #
686         # Stockage interne de l'observer dans la variable
687         # -----------------------------------------------
688         self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
689
690     def removeDataObserver(self,
691         HookFunction   = None,
692         ):
693         """
694         Suppression d'un observer nommé sur la variable.
695         
696         On peut donner dans HookFunction la meme fonction que lors de la
697         définition, ou un simple string qui est le nom de la fonction.
698         
699         """
700         if hasattr(HookFunction,"func_name"):
701             name = str( HookFunction.func_name )
702         elif type(HookFunction) is str:
703             name = str( HookFunction )
704         else:
705             name = None
706         #
707         i = -1
708         index_to_remove = []
709         for [hf, hp, hs] in self.__dataobservers:
710             i = i + 1
711             if name is hf.func_name: index_to_remove.append( i )
712         index_to_remove.reverse()
713         for i in index_to_remove:
714                 self.__dataobservers.pop( i )
715
716 # ==============================================================================
717 class OneScalar(Persistence):
718     """
719     Classe définissant le stockage d'une valeur unique réelle (float) par pas
720     
721     Le type de base peut être changé par la méthode "basetype", mais il faut que
722     le nouveau type de base soit compatible avec les types par éléments de 
723     numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
724     ou des matrices comme dans les classes suivantes, mais c'est déconseillé
725     pour conserver une signification claire des noms.
726     """
727     def __init__(self, name="", unit="", basetype = float):
728         Persistence.__init__(self, name, unit, basetype)
729
730 class OneVector(Persistence):
731     """
732     Classe définissant le stockage d'une liste (list) de valeurs homogènes par
733     hypothèse par pas. Pour éviter les confusions, ne pas utiliser la classe
734     "OneVector" pour des données hétérogènes, mais bien "OneList".
735     """
736     def __init__(self, name="", unit="", basetype = list):
737         Persistence.__init__(self, name, unit, basetype)
738
739 class OneMatrix(Persistence):
740     """
741     Classe définissant le stockage d'une matrice de valeurs (numpy.matrix) par
742     pas
743     """
744     def __init__(self, name="", unit="", basetype = numpy.matrix):
745         Persistence.__init__(self, name, unit, basetype)
746
747 class OneList(Persistence):
748     """
749     Classe définissant le stockage d'une liste de valeurs potentiellement
750     hétérogènes (list) par pas. Pour éviter les confusions, ne pas utiliser la
751     classe "OneVector" pour des données hétérogènes, mais bien "OneList".
752     """
753     def __init__(self, name="", unit="", basetype = list):
754         Persistence.__init__(self, name, unit, basetype)
755
756 def NoType( value ): return value
757
758 class OneNoType(Persistence):
759     """
760     Classe définissant le stockage d'un objet sans modification (cast) de type.
761     Attention, selon le véritable type de l'objet stocké à chaque pas, les
762     opérations arithmétiques à base de numpy peuvent être invalides ou donner
763     des résultats inatendus. Cette classe n'est donc à utiliser qu'à bon escient
764     volontairement, et pas du tout par défaut.
765     """
766     def __init__(self, name="", unit="", basetype = NoType):
767         Persistence.__init__(self, name, unit, basetype)
768
769 # ==============================================================================
770 class CompositePersistence:
771     """
772     Structure de stockage permettant de rassembler plusieurs objets de
773     persistence.
774     
775     Des objets par défaut sont prévus, et des objets supplémentaires peuvent
776     être ajoutés.
777     """
778     def __init__(self, name="", defaults=True):
779         """
780         name : nom courant
781         
782         La gestion interne des données est exclusivement basée sur les variables
783         initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
784         objets comme des attributs) :
785         __StoredObjects : objets de type persistence collectés dans cet objet
786         """
787         self.__name = str(name)
788         #
789         self.__StoredObjects = {}
790         #
791         # Definition des objets par defaut
792         # --------------------------------
793         if defaults:
794             self.__StoredObjects["Informations"]     = OneNoType("Informations")
795             self.__StoredObjects["Background"]       = OneVector("Background", basetype=numpy.array)
796             self.__StoredObjects["BackgroundError"]  = OneMatrix("BackgroundError")
797             self.__StoredObjects["Observation"]      = OneVector("Observation", basetype=numpy.array)
798             self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
799             self.__StoredObjects["Analysis"]         = OneVector("Analysis", basetype=numpy.array)
800             self.__StoredObjects["AnalysisError"]    = OneMatrix("AnalysisError")
801             self.__StoredObjects["Innovation"]       = OneVector("Innovation", basetype=numpy.array)
802             self.__StoredObjects["KalmanGainK"]      = OneMatrix("KalmanGainK")
803             self.__StoredObjects["OperatorH"]        = OneMatrix("OperatorH")
804             self.__StoredObjects["RmsOMA"]           = OneScalar("RmsOMA")
805             self.__StoredObjects["RmsOMB"]           = OneScalar("RmsOMB")
806             self.__StoredObjects["RmsBMA"]           = OneScalar("RmsBMA")
807         #
808
809     def store(self, name=None, value=None, step=None, tags={}):
810         """
811         Stockage d'une valeur "value" pour le "step" dans la variable "name".
812         """
813         if name is None: raise ValueError("Storable object name is required for storage.")
814         if name not in self.__StoredObjects.keys():
815             raise ValueError("No such name '%s' exists in storable objects."%name)
816         self.__StoredObjects[name].store( value=value, step=step, tags=tags )
817
818     def add_object(self, name=None, persistenceType=Persistence, basetype=numpy.array ):
819         """
820         Ajoute dans les objets stockables un nouvel objet défini par son nom, son
821         type de Persistence et son type de base à chaque pas.
822         """
823         if name is None: raise ValueError("Object name is required for adding an object.")
824         if name in self.__StoredObjects.keys():
825             raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
826         self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
827
828     def get_object(self, name=None ):
829         """
830         Renvoie l'objet de type Persistence qui porte le nom demandé.
831         """
832         if name is None: raise ValueError("Object name is required for retrieving an object.")
833         if name not in self.__StoredObjects.keys():
834             raise ValueError("No such name '%s' exists in stored objects."%name)
835         return self.__StoredObjects[name]
836
837     def set_object(self, name=None, objet=None ):
838         """
839         Affecte directement un 'objet' qui porte le nom 'name' demandé.
840         Attention, il n'est pas effectué de vérification sur le type, qui doit
841         comporter les méthodes habituelles de Persistence pour que cela
842         fonctionne.
843         """
844         if name is None: raise ValueError("Object name is required for setting an object.")
845         if name in self.__StoredObjects.keys():
846             raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
847         self.__StoredObjects[name] = objet
848
849     def del_object(self, name=None ):
850         """
851         Supprime un objet de la liste des objets stockables.
852         """
853         if name is None: raise ValueError("Object name is required for retrieving an object.")
854         if name not in self.__StoredObjects.keys():
855             raise ValueError("No such name '%s' exists in stored objects."%name)
856         del self.__StoredObjects[name]
857
858     # ---------------------------------------------------------
859     # Méthodes d'accès de type dictionnaire
860     def __getitem__(self, name=None ):
861         return self.get_object( name )
862
863     def __setitem__(self, name=None, objet=None ):
864         self.set_object( name, objet )
865
866     def keys(self):
867         return self.get_stored_objects(hideVoidObjects = False)
868
869     def values(self):
870         return self.__StoredObjects.values()
871
872     def items(self):
873         return self.__StoredObjects.items()
874
875     # ---------------------------------------------------------
876     def get_stored_objects(self, hideVoidObjects = False):
877         objs = self.__StoredObjects.keys()
878         if hideVoidObjects:
879             usedObjs = []
880             for k in objs:
881                 try:
882                     if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
883                 except:
884                     pass
885             objs = usedObjs
886         objs.sort()
887         return objs
888
889     # ---------------------------------------------------------
890     def save_composite(self, filename=None, mode="pickle", compress="gzip"):
891         """
892         Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
893         et renvoi le nom du fichier
894         """
895         import os
896         if filename is None:
897             if compress == "gzip":
898                 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
899             elif compress == "bzip2":
900                 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
901             else:
902                 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
903         else:
904             filename = os.path.abspath( filename )
905         #
906         import cPickle
907         if mode == "pickle":
908             if compress == "gzip":
909                 import gzip
910                 output = gzip.open( filename, 'wb')
911             elif compress == "bzip2":
912                 import bz2
913                 output = bz2.BZ2File( filename, 'wb')
914             else:
915                 output = open( filename, 'wb')
916             cPickle.dump(self, output)
917             output.close()
918         else:
919             raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
920         #
921         return filename
922
923     def load_composite(self, filename=None, mode="pickle", compress="gzip"):
924         """
925         Recharge un objet composite sauvé en fichier
926         """
927         import os
928         if filename is None:
929             raise ValueError("A file name if requested to load a composite.")
930         else:
931             filename = os.path.abspath( filename )
932         #
933         import cPickle
934         if mode == "pickle":
935             if compress == "gzip":
936                 import gzip
937                 pkl_file = gzip.open( filename, 'rb')
938             elif compress == "bzip2":
939                 import bz2
940                 pkl_file = bz2.BZ2File( filename, 'rb')
941             else:
942                 pkl_file = open(filename, 'rb')
943             output = cPickle.load(pkl_file)
944             for k in output.keys():
945                 self[k] = output[k]
946         else:
947             raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
948         #
949         return filename
950
951 # ==============================================================================
952 if __name__ == "__main__":
953     print '\n AUTODIAGNOSTIC \n'
954
955     print "======> Un flottant"
956     OBJET_DE_TEST = OneScalar("My float", unit="cm")
957     OBJET_DE_TEST.store( 5.)
958     OBJET_DE_TEST.store(-5.)
959     OBJET_DE_TEST.store( 1.)
960     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
961     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
962     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
963     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
964     print "Valeurs par pas :"
965     print "  La moyenne        :", OBJET_DE_TEST.means()
966     print "  L'écart-type      :", OBJET_DE_TEST.stds()
967     print "  La somme          :", OBJET_DE_TEST.sums()
968     print "  Le minimum        :", OBJET_DE_TEST.mins()
969     print "  Le maximum        :", OBJET_DE_TEST.maxs()
970     print "Valeurs globales :"
971     print "  La moyenne        :", OBJET_DE_TEST.mean()
972     print "  L'écart-type      :", OBJET_DE_TEST.std()
973     print "  La somme          :", OBJET_DE_TEST.sum()
974     print "  Le minimum        :", OBJET_DE_TEST.min()
975     print "  Le maximum        :", OBJET_DE_TEST.max()
976     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
977     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
978     print "Taille \"len\"        :", len(OBJET_DE_TEST)
979     del OBJET_DE_TEST
980     print
981
982     print "======> Un flottant"
983     OBJET_DE_TEST = OneScalar("My float", unit="cm")
984     OBJET_DE_TEST.store( 5., step="azerty")
985     OBJET_DE_TEST.store(-5., step="poiuyt")
986     OBJET_DE_TEST.store( 1., step="azerty")
987     OBJET_DE_TEST.store( 0., step="xxxxxx")
988     OBJET_DE_TEST.store( 5., step="poiuyt")
989     OBJET_DE_TEST.store(-5., step="azerty")
990     OBJET_DE_TEST.store( 1., step="poiuyt")
991     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
992     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
993     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
994     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
995     print "Premier index       :", OBJET_DE_TEST.valueserie( step = "azerty", allSteps = False )
996     print "Valeurs identiques  :", OBJET_DE_TEST.valueserie( step = "azerty", allSteps = True )
997     print "Premier index       :", OBJET_DE_TEST.valueserie( step = "poiuyt", allSteps = False )
998     print "Valeurs identiques  :", OBJET_DE_TEST.valueserie( step = "poiuyt", allSteps = True )
999     del OBJET_DE_TEST
1000     print
1001
1002     print "======> Un entier"
1003     OBJET_DE_TEST = OneScalar("My int", unit="cm", basetype=int)
1004     OBJET_DE_TEST.store( 5 )
1005     OBJET_DE_TEST.store(-5 )
1006     OBJET_DE_TEST.store( 1.)
1007     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1008     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1009     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1010     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1011     print "Valeurs par pas :"
1012     print "  La moyenne        :", OBJET_DE_TEST.means()
1013     print "  L'écart-type      :", OBJET_DE_TEST.stds()
1014     print "  La somme          :", OBJET_DE_TEST.sums()
1015     print "  Le minimum        :", OBJET_DE_TEST.mins()
1016     print "  Le maximum        :", OBJET_DE_TEST.maxs()
1017     print "Valeurs globales :"
1018     print "  La moyenne        :", OBJET_DE_TEST.mean()
1019     print "  L'écart-type      :", OBJET_DE_TEST.std()
1020     print "  La somme          :", OBJET_DE_TEST.sum()
1021     print "  Le minimum        :", OBJET_DE_TEST.min()
1022     print "  Le maximum        :", OBJET_DE_TEST.max()
1023     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
1024     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1025     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1026     del OBJET_DE_TEST
1027     print
1028
1029     print "======> Un booléen"
1030     OBJET_DE_TEST = OneScalar("My bool", unit="", basetype=bool)
1031     OBJET_DE_TEST.store( True  )
1032     OBJET_DE_TEST.store( False )
1033     OBJET_DE_TEST.store( True  )
1034     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1035     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1036     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1037     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1038     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1039     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1040     del OBJET_DE_TEST
1041     print
1042
1043     print "======> Un vecteur de flottants"
1044     OBJET_DE_TEST = OneVector("My float vector", unit="cm")
1045     OBJET_DE_TEST.store( (5 , -5) )
1046     OBJET_DE_TEST.store( (-5, 5 ) )
1047     OBJET_DE_TEST.store( (1., 1.) )
1048     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1049     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1050     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1051     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1052     print "Valeurs par pas :"
1053     print "  La moyenne        :", OBJET_DE_TEST.means()
1054     print "  L'écart-type      :", OBJET_DE_TEST.stds()
1055     print "  La somme          :", OBJET_DE_TEST.sums()
1056     print "  Le minimum        :", OBJET_DE_TEST.mins()
1057     print "  Le maximum        :", OBJET_DE_TEST.maxs()
1058     print "Valeurs globales :"
1059     print "  La moyenne        :", OBJET_DE_TEST.mean()
1060     print "  L'écart-type      :", OBJET_DE_TEST.std()
1061     print "  La somme          :", OBJET_DE_TEST.sum()
1062     print "  Le minimum        :", OBJET_DE_TEST.min()
1063     print "  Le maximum        :", OBJET_DE_TEST.max()
1064     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
1065     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1066     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1067     del OBJET_DE_TEST
1068     print
1069
1070     print "======> Une liste hétérogène"
1071     OBJET_DE_TEST = OneList("My list", unit="bool/cm")
1072     OBJET_DE_TEST.store( (True , -5) )
1073     OBJET_DE_TEST.store( (False,  5 ) )
1074     OBJET_DE_TEST.store( (True ,  1.) )
1075     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1076     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1077     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1078     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1079     print "Valeurs par pas : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
1080     print "  La moyenne        :", OBJET_DE_TEST.means()
1081     print "  L'écart-type      :", OBJET_DE_TEST.stds()
1082     print "  La somme          :", OBJET_DE_TEST.sums()
1083     print "  Le minimum        :", OBJET_DE_TEST.mins()
1084     print "  Le maximum        :", OBJET_DE_TEST.maxs()
1085     print "Valeurs globales : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
1086     print "  La moyenne        :", OBJET_DE_TEST.mean()
1087     print "  L'écart-type      :", OBJET_DE_TEST.std()
1088     print "  La somme          :", OBJET_DE_TEST.sum()
1089     print "  Le minimum        :", OBJET_DE_TEST.min()
1090     print "  Le maximum        :", OBJET_DE_TEST.max()
1091     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
1092     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1093     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1094     del OBJET_DE_TEST
1095     print
1096
1097     print "======> Utilisation directe de la classe Persistence"
1098     OBJET_DE_TEST = Persistence("My object", unit="", basetype=int )
1099     OBJET_DE_TEST.store( 1  )
1100     OBJET_DE_TEST.store( 3 )
1101     OBJET_DE_TEST.store( 7  )
1102     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1103     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1104     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1105     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1106     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1107     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1108     del OBJET_DE_TEST
1109     print
1110
1111     print "======> Utilisation des méthodes d'accès de type dictionnaire"
1112     OBJET_DE_TEST = OneScalar("My int", unit="cm", basetype=int)
1113     for i in range(5):
1114         OBJET_DE_TEST.store( 7+i )
1115     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1116     print "Les pas de stockage :", OBJET_DE_TEST.keys()
1117     print "Les valeurs         :", OBJET_DE_TEST.values()
1118     print "Les paires          :", OBJET_DE_TEST.items()
1119     del OBJET_DE_TEST
1120     print
1121
1122     print "======> Persistence composite"
1123     OBJET_DE_TEST = CompositePersistence("My CompositePersistence")
1124     print "Objets stockables :", OBJET_DE_TEST.get_stored_objects()
1125     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1126     print "--> Stockage d'une valeur de Background"
1127     OBJET_DE_TEST.store("Background",numpy.zeros(5))
1128     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1129     print "--> Ajout d'un objet nouveau par defaut, de type vecteur numpy par pas"
1130     OBJET_DE_TEST.add_object("ValeursVectorielles")
1131     OBJET_DE_TEST.store("ValeursVectorielles",numpy.zeros(5))
1132     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1133     print "--> Ajout d'un objet nouveau de type liste par pas"
1134     OBJET_DE_TEST.add_object("ValeursList", persistenceType=OneList )
1135     OBJET_DE_TEST.store("ValeursList",range(5))
1136     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1137     print "--> Ajout d'un objet nouveau, de type vecteur string par pas"
1138     OBJET_DE_TEST.add_object("ValeursStr", persistenceType=Persistence, basetype=str )
1139     OBJET_DE_TEST.store("ValeursStr","IGN3")
1140     OBJET_DE_TEST.store("ValeursStr","c021")
1141     print "Les valeurs       :", OBJET_DE_TEST.get_object("ValeursStr").valueserie()
1142     print "Acces comme dict  :", OBJET_DE_TEST["ValeursStr"].stepserie()
1143     print "Acces comme dict  :", OBJET_DE_TEST["ValeursStr"].valueserie()
1144     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1145     print "--> Suppression d'un objet"
1146     OBJET_DE_TEST.del_object("ValeursVectorielles")
1147     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1148     print "--> Enregistrement de l'objet complet de Persistence composite"
1149     OBJET_DE_TEST.save_composite("composite.pkl", compress="None")
1150     print
1151
1152     print "======> Affichage graphique d'objets stockés"
1153     OBJET_DE_TEST = Persistence("My object", unit="", basetype=numpy.array)
1154     D = OBJET_DE_TEST
1155     vect1 = [1, 2, 1, 2, 1]
1156     vect2 = [-3, -3, 0, -3, -3]
1157     vect3 = [-1, 1, -5, 1, -1]
1158     vect4 = 100*[0.29, 0.97, 0.73, 0.01, 0.20]
1159     print "Stockage de 3 vecteurs de longueur identique"
1160     D.store(vect1)
1161     D.store(vect2)
1162     D.store(vect3)
1163     print "Affichage graphique de l'ensemble du stockage sur une même image"
1164     D.plot(
1165         title = "Tous les vecteurs",
1166         filename="vecteurs.ps",
1167         xlabel = "Axe X",
1168         ylabel = "Axe Y",
1169         pause = False )
1170     print "Stockage d'un quatrième vecteur de longueur différente"
1171     D.store(vect4)
1172     print "Affichage graphique séparé du dernier stockage"
1173     D.plots(
1174         item  = 3,
1175         title = "Vecteurs",
1176         filename = "vecteur",
1177         xlabel = "Axe X",
1178         ylabel = "Axe Y",
1179         pause = False )
1180     print "Les images ont été stockées en fichiers Postscript"
1181     print "Taille \"shape\" du dernier objet stocké",OBJET_DE_TEST.shape()
1182     print "Taille \"len\" du dernier objet stocké",len(OBJET_DE_TEST)
1183     del OBJET_DE_TEST
1184     print
1185
1186     print "======> Affichage graphique dynamique d'objets"
1187     OBJET_DE_TEST = Persistence("My object", unit="", basetype=float)
1188     D = OBJET_DE_TEST
1189     D.plots(
1190         dynamic = True,
1191         title   = "Valeur suivie",
1192         xlabel  = "Pas",
1193         ylabel  = "Valeur",
1194         pause   = False,
1195         )
1196     for i in range(1,11):
1197         D.store( i*i )
1198     print "Taille \"shape\" du dernier objet stocké",OBJET_DE_TEST.shape()
1199     print "Taille \"len\" du dernier objet stocké",len(OBJET_DE_TEST)
1200     print "Nombre d'objets stockés",OBJET_DE_TEST.stepnumber()
1201     del OBJET_DE_TEST
1202     print
1203
1204     print "======> Affectation simple d'observateurs dynamiques"
1205     def obs(var=None,info=None):
1206         print "  ---> Mise en oeuvre de l'observer"
1207         print "       var  =",var.valueserie(-1)
1208         print "       info =",info
1209     OBJET_DE_TEST = Persistence("My object", unit="", basetype=list)
1210     D = OBJET_DE_TEST
1211     D.setDataObserver( HookFunction = obs )
1212     for i in range(5):
1213         # print
1214         print "Action de 1 observer sur la variable observée, étape :",i
1215         D.store( [i, i, i] )
1216     del OBJET_DE_TEST
1217     print
1218
1219     print "======> Affectation multiple d'observateurs dynamiques"
1220     def obs(var=None,info=None):
1221         print "  ---> Mise en oeuvre de l'observer"
1222         print "       var  =",var.valueserie(-1)
1223         print "       info =",info
1224     def obs_bis(var=None,info=None):
1225         print "  ---> Mise en oeuvre de l'observer"
1226         print "       var  =",var.valueserie(-1)
1227         print "       info =",info
1228     OBJET_DE_TEST = Persistence("My object", unit="", basetype=list)
1229     D = OBJET_DE_TEST
1230     D.setDataObserver(
1231         HookFunction   = obs,
1232         Scheduler      = [2, 4],
1233         HookParameters = "Premier observer",
1234         )
1235     D.setDataObserver(
1236         HookFunction   = obs,
1237         Scheduler      = xrange(1,3),
1238         HookParameters = "Second observer",
1239         )
1240     D.setDataObserver(
1241         HookFunction   = obs_bis,
1242         Scheduler      = range(1,3)+range(7,9),
1243         HookParameters = "Troisième observer",
1244         )
1245     for i in range(5):
1246         print "Action de 3 observers sur la variable observée, étape :",i
1247         D.store( [i, i, i] )
1248     D.removeDataObserver(
1249         HookFunction   = obs,
1250         )
1251     for i in range(5,10):
1252         print "Action d'un seul observer sur la variable observée, étape :",i
1253         D.store( [i, i, i] )
1254     del OBJET_DE_TEST
1255     print
1256
1257     print "======> Utilisation des tags/attributs et stockage puis récupération de l'ensemble"
1258     OBJET_DE_TEST = CompositePersistence("My CompositePersistence", defaults=False)
1259     OBJET_DE_TEST.add_object("My ecarts", basetype = numpy.array)
1260
1261     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5),   tags = {"Camp":"Base","Carte":"IGN3","Niveau":1024,"Palier":"Premier"} )
1262     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5)+1, tags = {"Camp":"Base","Carte":"IGN4","Niveau": 210,"Palier":"Premier"} )
1263     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5)+2, tags = {"Camp":"Base","Carte":"IGN1","Niveau":1024} )
1264     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5)+3, tags = {"Camp":"Sommet","Carte":"IGN2","Niveau":4024,"Palier":"Second","FullMap":True} )
1265
1266     print "Les pas de stockage :", OBJET_DE_TEST["My ecarts"].stepserie()
1267     print "Les valeurs         :", OBJET_DE_TEST["My ecarts"].valueserie()
1268     print "La 2ème valeur      :", OBJET_DE_TEST["My ecarts"].valueserie(1)
1269     print "La dernière valeur  :", OBJET_DE_TEST["My ecarts"].valueserie(-1)
1270     print "Liste des attributs :", OBJET_DE_TEST["My ecarts"].tagserie()
1271     print "Taille \"shape\"      :", OBJET_DE_TEST["My ecarts"].shape()
1272     print "Taille \"len\"        :", len(OBJET_DE_TEST["My ecarts"])
1273     print
1274
1275     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Palier":"Premier"} )
1276     print "Valeurs pour tag    :", OBJET_DE_TEST["My ecarts"].valueserie( tags={"Palier":"Premier"} )
1277     print
1278     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1279     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Niveau":1024} )
1280     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Base"} )
1281     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"TOTO"} )
1282     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Toto":"Premier"} )
1283     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1284     print
1285
1286     print "Combinaison 'ET' de plusieurs Tags"
1287     print "Attendu : [0, 1],    trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Base", "Palier":"Premier"} )
1288     print "Attendu : [],        trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Sommet", "Palier":"Premier"} )
1289     # Attention : {"Camp":"Sommet", "Camp":"Base"} == {"Camp":"Base"}
1290     print "Attendu : [0, 1, 2], trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Sommet", "Camp":"Base"} )
1291     print "Attendu : [2],       trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1", "Niveau":1024} )
1292     print
1293       
1294     print "Liste des tags pour le pas (item) 1  :",OBJET_DE_TEST["My ecarts"].tagserie(item = 1)
1295     print "Liste des tags pour le pas (item) 2  :",OBJET_DE_TEST["My ecarts"].tagserie(item = 2)
1296     print "Comme le step et l'item sont identiques par défaut, on doit avoir la même chose :"
1297     print "Liste des tags pour le pas (step) 1  :",OBJET_DE_TEST["My ecarts"].tagserie(step = 1)
1298     print "Liste des tags pour le pas (step) 2  :",OBJET_DE_TEST["My ecarts"].tagserie(step = 2)
1299     print
1300     print "Liste des tags/valeurs pour le pas 1 :",OBJET_DE_TEST["My ecarts"].tagserie(item = 1, withValues=True)
1301     print "Liste des tags/valeurs pour le pas 2 :",OBJET_DE_TEST["My ecarts"].tagserie(item = 2, withValues=True)
1302     print
1303
1304     print "Liste des valeurs possibles pour 1 tag donné 'Camp'   :",OBJET_DE_TEST["My ecarts"].tagserie(outputTag="Camp")
1305     print "Liste des valeurs possibles pour 1 tag donné 'Toto'   :",OBJET_DE_TEST["My ecarts"].tagserie(outputTag="Toto")
1306     print "Liste des valeurs possibles pour 1 tag donné 'Niveau' :",OBJET_DE_TEST["My ecarts"].tagserie(outputTag="Niveau")
1307     print
1308
1309     OBJET_DE_TEST.add_object("My other ecarts", basetype = numpy.array)
1310     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5),   tags = {"Camp":"Base","Carte":"IGN3","Niveau":1024,"Palier":"Premier"} )
1311     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5)+1, tags = {"Camp":"Base","Carte":"IGN4","Niveau": 210,"Palier":"Premier"} )
1312     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5)+2, tags = {"Camp":"Base","Carte":"IGN1","Niveau":1024} )
1313     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5)+3, tags = {"Camp":"Sommet","Carte":"IGN2","Niveau":4024,"Palier":"Second"} )
1314
1315     print "Objets présents dans le composite :",OBJET_DE_TEST.get_stored_objects()
1316     fichier = "composite.pkl.gz"
1317     print "Sauvegarde sur \"%s\"..."%fichier
1318     OBJET_DE_TEST.save_composite( fichier )
1319     print "Effacement de l'objet en memoire"
1320     del OBJET_DE_TEST
1321     print
1322
1323     print "Relecture de l'objet sur \"%s\"..."%fichier
1324     OBJET_DE_TEST = CompositePersistence("My CompositePersistence bis", defaults=False)
1325     OBJET_DE_TEST.load_composite( fichier )
1326     print "Objets présents dans le composite :",OBJET_DE_TEST.get_stored_objects()
1327     print "Taille des objets contenus :"
1328     for name in OBJET_DE_TEST.get_stored_objects():
1329         print "  Objet \"%s\" : taille unitaire de %i"%(name,len(OBJET_DE_TEST[name]))
1330
1331     print
1332     print "Les pas de stockage :", OBJET_DE_TEST["My ecarts"].stepserie()
1333     print "Les valeurs         :", OBJET_DE_TEST["My ecarts"].valueserie()
1334     print "La 2ème valeur      :", OBJET_DE_TEST["My ecarts"].valueserie(1)
1335     print "La dernière valeur  :", OBJET_DE_TEST["My ecarts"].valueserie(-1)
1336     print "Liste des attributs :", OBJET_DE_TEST["My ecarts"].tagserie()
1337     print "Taille \"shape\"      :", OBJET_DE_TEST["My ecarts"].shape()
1338     print "Taille \"len\"        :", len(OBJET_DE_TEST["My ecarts"])
1339     print
1340
1341     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Palier":"Premier"} )
1342     print "Valeurs pour tag    :", OBJET_DE_TEST["My ecarts"].valueserie( tags={"Palier":"Premier"} )
1343     print
1344     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1345     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Niveau":1024} )
1346     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Base"} )
1347     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"TOTO"} )
1348     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Toto":"Premier"} )
1349     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1350     print
1351     print "Attributs                 :", OBJET_DE_TEST["My ecarts"].tagserie()
1352     print "Attributs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( tags={"Camp":"Base"} )
1353     print "Attributs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( tags={"Niveau":4024} )
1354     print
1355     print "Attributs et valeurs                 :", OBJET_DE_TEST["My ecarts"].tagserie( withValues=True )
1356     print "Attributs et valeurs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( withValues=True, tags={"Camp":"Base"} )
1357     print "Attributs et valeurs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( withValues=True, tags={"Niveau":4024} )
1358     print
1359     print "Valeur d'attribut pour un tag donné 'BU'           :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Niveau" )
1360     print "Valeur d'attribut pour un tag donné 'BU' filtré    :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Niveau", tags={"Camp":"Base"} )
1361     print "Valeur d'attribut pour un tag donné 'BU' filtré    :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Niveau", tags={"Palier":"Second"} )
1362     print "Valeur d'attribut pour un tag donné 'Camp' filtré  :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Camp", tags={"Palier":"Premier"} )
1363     print "Valeur d'attribut pour un tag donné 'Carte' filtré :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Carte", tags={"Palier":"Premier"} )
1364     print "Valeur d'attribut pour un tag donné 'Carte' filtré :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Carte", tags={"Palier":"Premier","Niveau":4024} )
1365     print "Valeur d'attribut pour un tag donné 'Carte' filtré :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Carte", tags={"Palier":"Premier","Niveau":210} )
1366     print