Salome HOME
Changing internally treatment of algorithm parameters
[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.__replot()
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 __preplot(self,
377             title    = "",
378             xlabel   = "",
379             ylabel   = "",
380             ltitle   = None,
381             geometry = "600x400",
382             persist  = False,
383             pause    = True,
384             ):
385         import os
386         #
387         # Vérification de la disponibilité du module Gnuplot
388         try:
389             import Gnuplot
390             self.__gnuplot = Gnuplot
391         except:
392             raise ImportError("The Gnuplot module is required to plot the object.")
393         #
394         # Vérification et compléments sur les paramètres d'entrée
395         if persist:
396             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
397         else:
398             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
399         if ltitle is None:
400             ltitle = ""
401         self.__g = self.__gnuplot.Gnuplot() # persist=1
402         self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
403         self.__g('set style data lines')
404         self.__g('set grid')
405         self.__g('set autoscale')
406         self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
407         self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
408         self.__title  = title
409         self.__ltitle = ltitle
410         self.__pause  = pause
411
412     def plot(self, item=None, step=None,
413             steps    = None,
414             title    = "",
415             xlabel   = "",
416             ylabel   = "",
417             ltitle   = None,
418             geometry = "600x400",
419             filename = "",
420             dynamic  = False,
421             persist  = False,
422             pause    = True,
423             ):
424         """
425         Renvoie un affichage de la valeur à chaque pas, si elle est compatible
426         avec un affichage Gnuplot (donc essentiellement un vecteur). Si
427         l'argument "step" existe dans la liste des pas de stockage effectués,
428         renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
429         "item" est correct, renvoie l'affichage de la valeur stockée au numéro
430         "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
431         affichage successif de tous les pas.
432
433         Arguments :
434             - step     : valeur du pas à afficher
435             - item     : index de la valeur à afficher
436             - steps    : liste unique des pas de l'axe des X, ou None si c'est
437                          la numérotation par défaut
438             - title    : base du titre général, qui sera automatiquement
439                          complétée par la mention du pas
440             - xlabel   : label de l'axe des X
441             - ylabel   : label de l'axe des Y
442             - ltitle   : titre associé au vecteur tracé
443             - geometry : taille en pixels de la fenêtre et position du coin haut
444                          gauche, au format X11 : LxH+X+Y (défaut : 600x400)
445             - filename : base de nom de fichier Postscript pour une sauvegarde,
446                          qui est automatiquement complétée par le numéro du
447                          fichier calculé par incrément simple de compteur
448             - dynamic  : effectue un affichage des valeurs à chaque stockage
449                          (au-delà du second) La méthode "plot" permet de déclarer
450                          l'affichage dynamique, et c'est la méthode "__replot"
451                          qui est utilisée pour l'effectuer
452             - persist  : booléen indiquant que la fenêtre affichée sera
453                          conservée lors du passage au dessin suivant
454                          Par défaut, persist = False
455             - pause    : booléen indiquant une pause après chaque tracé, et
456                          attendant un Return
457                          Par défaut, pause = True
458         """
459         import os
460         if not self.__dynamic:
461             self.__preplot(title, xlabel, ylabel, ltitle, geometry, persist, pause )
462             if dynamic:
463                 self.__dynamic = True
464                 if len(self.__values) == 0: return 0
465         #
466         # Tracé du ou des vecteurs demandés
467         indexes = []
468         if step is not None and step in self.__steps:
469             indexes.append(self.__steps.index(step))
470         elif item is not None and item < len(self.__values):
471             indexes.append(item)
472         else:
473             indexes = indexes + range(len(self.__values))
474         #
475         i = -1
476         for index in indexes:
477             self.__g('set title  "'+str(title).encode('ascii','replace')+' (pas '+str(index)+')"')
478             if ( type(steps) is list ) or ( type(steps) is type(numpy.array([])) ):
479                 Steps = list(steps)
480             else:
481                 Steps = range(len(self.__values[index]))
482             #
483             self.__g.plot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
484             #
485             if filename != "":
486                 i += 1
487                 stepfilename = "%s_%03i.ps"%(filename,i)
488                 if os.path.isfile(stepfilename):
489                     raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
490                 self.__g.hardcopy(filename=stepfilename, color=1)
491             if self.__pause:
492                 raw_input('Please press return to continue...\n')
493
494     def __replot(self):
495         """
496         Affichage dans le cas du suivi dynamique de la variable
497         """
498         if self.__dynamic and len(self.__values) < 2: return 0
499         #
500         import os
501         self.__g('set title  "'+str(self.__title).encode('ascii','replace'))
502         Steps = range(len(self.__values))
503         self.__g.plot( self.__gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
504         #
505         if self.__pause:
506             raw_input('Please press return to continue...\n')
507
508     # ---------------------------------------------------------
509     def mean(self):
510         """
511         Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
512         longueur des pas. Il faut que le type de base soit compatible avec
513         les types élémentaires numpy.
514         """
515         try:
516             if self.__basetype in [int, float]:
517                 return float( numpy.array(self.__values).mean() )
518             else:
519                 return numpy.array(self.__values).mean(axis=0)
520         except:
521             raise TypeError("Base type is incompatible with numpy")
522
523     def std(self, ddof=0):
524         """
525         Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
526         longueur des pas. Il faut que le type de base soit compatible avec
527         les types élémentaires numpy.
528         
529         ddof : c'est le nombre de degrés de liberté pour le calcul de
530                l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
531         """
532         try:
533             if numpy.version.version >= '1.1.0':
534                 return numpy.array(self.__values).std(ddof=ddof,axis=0)
535             else:
536                 return numpy.array(self.__values).std(axis=0)
537         except:
538             raise TypeError("Base type is incompatible with numpy")
539
540     def sum(self):
541         """
542         Renvoie la somme de toutes les valeurs sans tenir compte de la
543         longueur des pas. Il faut que le type de base soit compatible avec
544         les types élémentaires numpy.
545         """
546         try:
547             return numpy.array(self.__values).sum(axis=0)
548         except:
549             raise TypeError("Base type is incompatible with numpy")
550
551     def min(self):
552         """
553         Renvoie le minimum de toutes les valeurs sans tenir compte de la
554         longueur des pas. Il faut que le type de base soit compatible avec
555         les types élémentaires numpy.
556         """
557         try:
558             return numpy.array(self.__values).min(axis=0)
559         except:
560             raise TypeError("Base type is incompatible with numpy")
561
562     def max(self):
563         """
564         Renvoie le maximum de toutes les valeurs sans tenir compte de la
565         longueur des pas. Il faut que le type de base soit compatible avec
566         les types élémentaires numpy.
567         """
568         try:
569             return numpy.array(self.__values).max(axis=0)
570         except:
571             raise TypeError("Base type is incompatible with numpy")
572
573     def cumsum(self):
574         """
575         Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
576         longueur des pas. Il faut que le type de base soit compatible avec
577         les types élémentaires numpy.
578         """
579         try:
580             return numpy.array(self.__values).cumsum(axis=0)
581         except:
582             raise TypeError("Base type is incompatible with numpy")
583
584     # On pourrait aussi utiliser les autres attributs d'une "matrix", comme
585     # "tofile", "min"...
586
587     def stepplot(self,
588             steps    = None,
589             title    = "",
590             xlabel   = "",
591             ylabel   = "",
592             ltitle   = None,
593             geometry = "600x400",
594             filename = "",
595             persist  = False,
596             pause    = True,
597             ):
598         """
599         Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
600         elles sont compatibles avec un affichage Gnuplot (donc essentiellement
601         un vecteur). Si l'argument "step" existe dans la liste des pas de
602         stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
603         "step". Si l'argument "item" est correct, renvoie l'affichage de la
604         valeur stockée au numéro "item".
605
606         Arguments :
607             - steps    : liste unique des pas de l'axe des X, ou None si c'est
608                          la numérotation par défaut
609             - title    : base du titre général, qui sera automatiquement
610                          complétée par la mention du pas
611             - xlabel   : label de l'axe des X
612             - ylabel   : label de l'axe des Y
613             - ltitle   : titre associé au vecteur tracé
614             - geometry : taille en pixels de la fenêtre et position du coin haut
615                          gauche, au format X11 : LxH+X+Y (défaut : 600x400)
616             - filename : nom de fichier Postscript pour une sauvegarde
617             - persist  : booléen indiquant que la fenêtre affichée sera
618                          conservée lors du passage au dessin suivant
619                          Par défaut, persist = False
620             - pause    : booléen indiquant une pause après chaque tracé, et
621                          attendant un Return
622                          Par défaut, pause = True
623         """
624         import os
625         #
626         # Vérification de la disponibilité du module Gnuplot
627         try:
628             import Gnuplot
629             self.__gnuplot = Gnuplot
630         except:
631             raise ImportError("The Gnuplot module is required to plot the object.")
632         #
633         # Vérification et compléments sur les paramètres d'entrée
634         if persist:
635             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
636         else:
637             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
638         if ltitle is None:
639             ltitle = ""
640         if ( type(steps) is list ) or ( type(steps) is type(numpy.array([])) ):
641             Steps = list(steps)
642         else:
643             Steps = range(len(self.__values[0]))
644         self.__g = self.__gnuplot.Gnuplot() # persist=1
645         self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
646         self.__g('set style data lines')
647         self.__g('set grid')
648         self.__g('set autoscale')
649         self.__g('set title  "'+str(title).encode('ascii','replace') +'"')
650         self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
651         self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
652         #
653         # Tracé du ou des vecteurs demandés
654         indexes = range(len(self.__values))
655         self.__g.plot( self.__gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
656         for index in indexes:
657             self.__g.replot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
658         #
659         if filename != "":
660             self.__g.hardcopy(filename=filename, color=1)
661         if pause:
662             raw_input('Please press return to continue...\n')
663
664     # ---------------------------------------------------------
665     def setDataObserver(self,
666         HookFunction   = None,
667         HookParameters = None,
668         Scheduler      = None,
669         ):
670         """
671         Association à la variable d'un triplet définissant un observer
672         
673         Le Scheduler attendu est une fréquence, une simple liste d'index ou un
674         xrange des index.
675         """
676         #
677         # Vérification du Scheduler
678         # -------------------------
679         maxiter = int( 1e9 )
680         if type(Scheduler) is int:    # Considéré comme une fréquence à partir de 0
681             Schedulers = xrange( 0, maxiter, int(Scheduler) )
682         elif type(Scheduler) is xrange: # Considéré comme un itérateur
683             Schedulers = Scheduler
684         elif type(Scheduler) is list: # Considéré comme des index explicites
685             Schedulers = map( long, Scheduler )
686         else:                         # Dans tous les autres cas, activé par défaut
687             Schedulers = xrange( 0, maxiter )
688         #
689         # Stockage interne de l'observer dans la variable
690         # -----------------------------------------------
691         self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
692
693     def removeDataObserver(self,
694         HookFunction   = None,
695         ):
696         """
697         Suppression d'un observer nommé sur la variable.
698         
699         On peut donner dans HookFunction la meme fonction que lors de la
700         définition, ou un simple string qui est le nom de la fonction.
701         
702         """
703         if hasattr(HookFunction,"func_name"):
704             name = str( HookFunction.func_name )
705         elif type(HookFunction) is str:
706             name = str( HookFunction )
707         else:
708             name = None
709         #
710         i = -1
711         index_to_remove = []
712         for [hf, hp, hs] in self.__dataobservers:
713             i = i + 1
714             if name is hf.func_name: index_to_remove.append( i )
715         index_to_remove.reverse()
716         for i in index_to_remove:
717                 self.__dataobservers.pop( i )
718
719 # ==============================================================================
720 class OneScalar(Persistence):
721     """
722     Classe définissant le stockage d'une valeur unique réelle (float) par pas
723     
724     Le type de base peut être changé par la méthode "basetype", mais il faut que
725     le nouveau type de base soit compatible avec les types par éléments de 
726     numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
727     ou des matrices comme dans les classes suivantes, mais c'est déconseillé
728     pour conserver une signification claire des noms.
729     """
730     def __init__(self, name="", unit="", basetype = float):
731         Persistence.__init__(self, name, unit, basetype)
732
733 class OneVector(Persistence):
734     """
735     Classe définissant le stockage d'une liste (list) de valeurs homogènes par
736     hypothèse par pas. Pour éviter les confusions, ne pas utiliser la classe
737     "OneVector" pour des données hétérogènes, mais bien "OneList".
738     """
739     def __init__(self, name="", unit="", basetype = list):
740         Persistence.__init__(self, name, unit, basetype)
741
742 class OneMatrix(Persistence):
743     """
744     Classe définissant le stockage d'une matrice de valeurs (numpy.matrix) par
745     pas
746     """
747     def __init__(self, name="", unit="", basetype = numpy.matrix):
748         Persistence.__init__(self, name, unit, basetype)
749
750 class OneList(Persistence):
751     """
752     Classe définissant le stockage d'une liste de valeurs potentiellement
753     hétérogènes (list) par pas. Pour éviter les confusions, ne pas utiliser la
754     classe "OneVector" pour des données hétérogènes, mais bien "OneList".
755     """
756     def __init__(self, name="", unit="", basetype = list):
757         Persistence.__init__(self, name, unit, basetype)
758
759 def NoType( value ): return value
760
761 class OneNoType(Persistence):
762     """
763     Classe définissant le stockage d'un objet sans modification (cast) de type.
764     Attention, selon le véritable type de l'objet stocké à chaque pas, les
765     opérations arithmétiques à base de numpy peuvent être invalides ou donner
766     des résultats inatendus. Cette classe n'est donc à utiliser qu'à bon escient
767     volontairement, et pas du tout par défaut.
768     """
769     def __init__(self, name="", unit="", basetype = NoType):
770         Persistence.__init__(self, name, unit, basetype)
771
772 # ==============================================================================
773 class CompositePersistence:
774     """
775     Structure de stockage permettant de rassembler plusieurs objets de
776     persistence.
777     
778     Des objets par défaut sont prévus, et des objets supplémentaires peuvent
779     être ajoutés.
780     """
781     def __init__(self, name="", defaults=True):
782         """
783         name : nom courant
784         
785         La gestion interne des données est exclusivement basée sur les variables
786         initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
787         objets comme des attributs) :
788         __StoredObjects : objets de type persistence collectés dans cet objet
789         """
790         self.__name = str(name)
791         #
792         self.__StoredObjects = {}
793         #
794         # Definition des objets par defaut
795         # --------------------------------
796         if defaults:
797             self.__StoredObjects["Informations"]     = OneNoType("Informations")
798             self.__StoredObjects["Background"]       = OneVector("Background", basetype=numpy.array)
799             self.__StoredObjects["BackgroundError"]  = OneMatrix("BackgroundError")
800             self.__StoredObjects["Observation"]      = OneVector("Observation", basetype=numpy.array)
801             self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
802             self.__StoredObjects["Analysis"]         = OneVector("Analysis", basetype=numpy.array)
803             self.__StoredObjects["AnalysisError"]    = OneMatrix("AnalysisError")
804             self.__StoredObjects["Innovation"]       = OneVector("Innovation", basetype=numpy.array)
805             self.__StoredObjects["KalmanGainK"]      = OneMatrix("KalmanGainK")
806             self.__StoredObjects["OperatorH"]        = OneMatrix("OperatorH")
807             self.__StoredObjects["RmsOMA"]           = OneScalar("RmsOMA")
808             self.__StoredObjects["RmsOMB"]           = OneScalar("RmsOMB")
809             self.__StoredObjects["RmsBMA"]           = OneScalar("RmsBMA")
810         #
811
812     def store(self, name=None, value=None, step=None, tags={}):
813         """
814         Stockage d'une valeur "value" pour le "step" dans la variable "name".
815         """
816         if name is None: raise ValueError("Storable object name is required for storage.")
817         if name not in self.__StoredObjects.keys():
818             raise ValueError("No such name '%s' exists in storable objects."%name)
819         self.__StoredObjects[name].store( value=value, step=step, tags=tags )
820
821     def add_object(self, name=None, persistenceType=Persistence, basetype=numpy.array ):
822         """
823         Ajoute dans les objets stockables un nouvel objet défini par son nom, son
824         type de Persistence et son type de base à chaque pas.
825         """
826         if name is None: raise ValueError("Object name is required for adding an object.")
827         if name in self.__StoredObjects.keys():
828             raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
829         self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
830
831     def get_object(self, name=None ):
832         """
833         Renvoie l'objet de type Persistence qui porte le nom demandé.
834         """
835         if name is None: raise ValueError("Object name is required for retrieving an object.")
836         if name not in self.__StoredObjects.keys():
837             raise ValueError("No such name '%s' exists in stored objects."%name)
838         return self.__StoredObjects[name]
839
840     def set_object(self, name=None, objet=None ):
841         """
842         Affecte directement un 'objet' qui porte le nom 'name' demandé.
843         Attention, il n'est pas effectué de vérification sur le type, qui doit
844         comporter les méthodes habituelles de Persistence pour que cela
845         fonctionne.
846         """
847         if name is None: raise ValueError("Object name is required for setting an object.")
848         if name in self.__StoredObjects.keys():
849             raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
850         self.__StoredObjects[name] = objet
851
852     def del_object(self, name=None ):
853         """
854         Supprime un objet de la liste des objets stockables.
855         """
856         if name is None: raise ValueError("Object name is required for retrieving an object.")
857         if name not in self.__StoredObjects.keys():
858             raise ValueError("No such name '%s' exists in stored objects."%name)
859         del self.__StoredObjects[name]
860
861     # ---------------------------------------------------------
862     # Méthodes d'accès de type dictionnaire
863     def __getitem__(self, name=None ):
864         return self.get_object( name )
865
866     def __setitem__(self, name=None, objet=None ):
867         self.set_object( name, objet )
868
869     def keys(self):
870         return self.get_stored_objects(hideVoidObjects = False)
871
872     def values(self):
873         return self.__StoredObjects.values()
874
875     def items(self):
876         return self.__StoredObjects.items()
877
878     # ---------------------------------------------------------
879     def get_stored_objects(self, hideVoidObjects = False):
880         objs = self.__StoredObjects.keys()
881         if hideVoidObjects:
882             usedObjs = []
883             for k in objs:
884                 try:
885                     if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
886                 except:
887                     pass
888             objs = usedObjs
889         objs.sort()
890         return objs
891
892     # ---------------------------------------------------------
893     def save_composite(self, filename=None, mode="pickle", compress="gzip"):
894         """
895         Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
896         et renvoi le nom du fichier
897         """
898         import os
899         if filename is None:
900             if compress == "gzip":
901                 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
902             elif compress == "bzip2":
903                 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
904             else:
905                 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
906         else:
907             filename = os.path.abspath( filename )
908         #
909         import cPickle
910         if mode == "pickle":
911             if compress == "gzip":
912                 import gzip
913                 output = gzip.open( filename, 'wb')
914             elif compress == "bzip2":
915                 import bz2
916                 output = bz2.BZ2File( filename, 'wb')
917             else:
918                 output = open( filename, 'wb')
919             cPickle.dump(self, output)
920             output.close()
921         else:
922             raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
923         #
924         return filename
925
926     def load_composite(self, filename=None, mode="pickle", compress="gzip"):
927         """
928         Recharge un objet composite sauvé en fichier
929         """
930         import os
931         if filename is None:
932             raise ValueError("A file name if requested to load a composite.")
933         else:
934             filename = os.path.abspath( filename )
935         #
936         import cPickle
937         if mode == "pickle":
938             if compress == "gzip":
939                 import gzip
940                 pkl_file = gzip.open( filename, 'rb')
941             elif compress == "bzip2":
942                 import bz2
943                 pkl_file = bz2.BZ2File( filename, 'rb')
944             else:
945                 pkl_file = open(filename, 'rb')
946             output = cPickle.load(pkl_file)
947             for k in output.keys():
948                 self[k] = output[k]
949         else:
950             raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
951         #
952         return filename
953
954 # ==============================================================================
955 if __name__ == "__main__":
956     print '\n AUTODIAGNOSTIC \n'
957
958     print "======> Un flottant"
959     OBJET_DE_TEST = OneScalar("My float", unit="cm")
960     OBJET_DE_TEST.store( 5.)
961     OBJET_DE_TEST.store(-5.)
962     OBJET_DE_TEST.store( 1.)
963     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
964     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
965     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
966     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
967     print "Valeurs par pas :"
968     print "  La moyenne        :", OBJET_DE_TEST.means()
969     print "  L'écart-type      :", OBJET_DE_TEST.stds()
970     print "  La somme          :", OBJET_DE_TEST.sums()
971     print "  Le minimum        :", OBJET_DE_TEST.mins()
972     print "  Le maximum        :", OBJET_DE_TEST.maxs()
973     print "Valeurs globales :"
974     print "  La moyenne        :", OBJET_DE_TEST.mean()
975     print "  L'écart-type      :", OBJET_DE_TEST.std()
976     print "  La somme          :", OBJET_DE_TEST.sum()
977     print "  Le minimum        :", OBJET_DE_TEST.min()
978     print "  Le maximum        :", OBJET_DE_TEST.max()
979     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
980     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
981     print "Taille \"len\"        :", len(OBJET_DE_TEST)
982     del OBJET_DE_TEST
983     print
984
985     print "======> Un flottant"
986     OBJET_DE_TEST = OneScalar("My float", unit="cm")
987     OBJET_DE_TEST.store( 5., step="azerty")
988     OBJET_DE_TEST.store(-5., step="poiuyt")
989     OBJET_DE_TEST.store( 1., step="azerty")
990     OBJET_DE_TEST.store( 0., step="xxxxxx")
991     OBJET_DE_TEST.store( 5., step="poiuyt")
992     OBJET_DE_TEST.store(-5., step="azerty")
993     OBJET_DE_TEST.store( 1., step="poiuyt")
994     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
995     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
996     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
997     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
998     print "Premier index       :", OBJET_DE_TEST.valueserie( step = "azerty", allSteps = False )
999     print "Valeurs identiques  :", OBJET_DE_TEST.valueserie( step = "azerty", allSteps = True )
1000     print "Premier index       :", OBJET_DE_TEST.valueserie( step = "poiuyt", allSteps = False )
1001     print "Valeurs identiques  :", OBJET_DE_TEST.valueserie( step = "poiuyt", allSteps = True )
1002     del OBJET_DE_TEST
1003     print
1004
1005     print "======> Un entier"
1006     OBJET_DE_TEST = OneScalar("My int", unit="cm", basetype=int)
1007     OBJET_DE_TEST.store( 5 )
1008     OBJET_DE_TEST.store(-5 )
1009     OBJET_DE_TEST.store( 1.)
1010     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1011     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1012     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1013     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1014     print "Valeurs par pas :"
1015     print "  La moyenne        :", OBJET_DE_TEST.means()
1016     print "  L'écart-type      :", OBJET_DE_TEST.stds()
1017     print "  La somme          :", OBJET_DE_TEST.sums()
1018     print "  Le minimum        :", OBJET_DE_TEST.mins()
1019     print "  Le maximum        :", OBJET_DE_TEST.maxs()
1020     print "Valeurs globales :"
1021     print "  La moyenne        :", OBJET_DE_TEST.mean()
1022     print "  L'écart-type      :", OBJET_DE_TEST.std()
1023     print "  La somme          :", OBJET_DE_TEST.sum()
1024     print "  Le minimum        :", OBJET_DE_TEST.min()
1025     print "  Le maximum        :", OBJET_DE_TEST.max()
1026     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
1027     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1028     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1029     del OBJET_DE_TEST
1030     print
1031
1032     print "======> Un booléen"
1033     OBJET_DE_TEST = OneScalar("My bool", unit="", basetype=bool)
1034     OBJET_DE_TEST.store( True  )
1035     OBJET_DE_TEST.store( False )
1036     OBJET_DE_TEST.store( True  )
1037     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1038     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1039     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1040     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1041     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1042     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1043     del OBJET_DE_TEST
1044     print
1045
1046     print "======> Un vecteur de flottants"
1047     OBJET_DE_TEST = OneVector("My float vector", unit="cm")
1048     OBJET_DE_TEST.store( (5 , -5) )
1049     OBJET_DE_TEST.store( (-5, 5 ) )
1050     OBJET_DE_TEST.store( (1., 1.) )
1051     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1052     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1053     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1054     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1055     print "Valeurs par pas :"
1056     print "  La moyenne        :", OBJET_DE_TEST.means()
1057     print "  L'écart-type      :", OBJET_DE_TEST.stds()
1058     print "  La somme          :", OBJET_DE_TEST.sums()
1059     print "  Le minimum        :", OBJET_DE_TEST.mins()
1060     print "  Le maximum        :", OBJET_DE_TEST.maxs()
1061     print "Valeurs globales :"
1062     print "  La moyenne        :", OBJET_DE_TEST.mean()
1063     print "  L'écart-type      :", OBJET_DE_TEST.std()
1064     print "  La somme          :", OBJET_DE_TEST.sum()
1065     print "  Le minimum        :", OBJET_DE_TEST.min()
1066     print "  Le maximum        :", OBJET_DE_TEST.max()
1067     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
1068     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1069     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1070     del OBJET_DE_TEST
1071     print
1072
1073     print "======> Une liste hétérogène"
1074     OBJET_DE_TEST = OneList("My list", unit="bool/cm")
1075     OBJET_DE_TEST.store( (True , -5) )
1076     OBJET_DE_TEST.store( (False,  5 ) )
1077     OBJET_DE_TEST.store( (True ,  1.) )
1078     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1079     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1080     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1081     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1082     print "Valeurs par pas : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
1083     print "  La moyenne        :", OBJET_DE_TEST.means()
1084     print "  L'écart-type      :", OBJET_DE_TEST.stds()
1085     print "  La somme          :", OBJET_DE_TEST.sums()
1086     print "  Le minimum        :", OBJET_DE_TEST.mins()
1087     print "  Le maximum        :", OBJET_DE_TEST.maxs()
1088     print "Valeurs globales : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
1089     print "  La moyenne        :", OBJET_DE_TEST.mean()
1090     print "  L'écart-type      :", OBJET_DE_TEST.std()
1091     print "  La somme          :", OBJET_DE_TEST.sum()
1092     print "  Le minimum        :", OBJET_DE_TEST.min()
1093     print "  Le maximum        :", OBJET_DE_TEST.max()
1094     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
1095     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1096     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1097     del OBJET_DE_TEST
1098     print
1099
1100     print "======> Utilisation directe de la classe Persistence"
1101     OBJET_DE_TEST = Persistence("My object", unit="", basetype=int )
1102     OBJET_DE_TEST.store( 1  )
1103     OBJET_DE_TEST.store( 3 )
1104     OBJET_DE_TEST.store( 7  )
1105     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
1106     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
1107     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
1108     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
1109     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
1110     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1111     del OBJET_DE_TEST
1112     print
1113
1114     print "======> Utilisation des méthodes d'accès de type dictionnaire"
1115     OBJET_DE_TEST = OneScalar("My int", unit="cm", basetype=int)
1116     for i in range(5):
1117         OBJET_DE_TEST.store( 7+i )
1118     print "Taille \"len\"        :", len(OBJET_DE_TEST)
1119     print "Les pas de stockage :", OBJET_DE_TEST.keys()
1120     print "Les valeurs         :", OBJET_DE_TEST.values()
1121     print "Les paires          :", OBJET_DE_TEST.items()
1122     del OBJET_DE_TEST
1123     print
1124
1125     print "======> Persistence composite"
1126     OBJET_DE_TEST = CompositePersistence("My CompositePersistence")
1127     print "Objets stockables :", OBJET_DE_TEST.get_stored_objects()
1128     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1129     print "--> Stockage d'une valeur de Background"
1130     OBJET_DE_TEST.store("Background",numpy.zeros(5))
1131     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1132     print "--> Ajout d'un objet nouveau par defaut, de type vecteur numpy par pas"
1133     OBJET_DE_TEST.add_object("ValeursVectorielles")
1134     OBJET_DE_TEST.store("ValeursVectorielles",numpy.zeros(5))
1135     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1136     print "--> Ajout d'un objet nouveau de type liste par pas"
1137     OBJET_DE_TEST.add_object("ValeursList", persistenceType=OneList )
1138     OBJET_DE_TEST.store("ValeursList",range(5))
1139     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1140     print "--> Ajout d'un objet nouveau, de type vecteur string par pas"
1141     OBJET_DE_TEST.add_object("ValeursStr", persistenceType=Persistence, basetype=str )
1142     OBJET_DE_TEST.store("ValeursStr","IGN3")
1143     OBJET_DE_TEST.store("ValeursStr","c021")
1144     print "Les valeurs       :", OBJET_DE_TEST.get_object("ValeursStr").valueserie()
1145     print "Acces comme dict  :", OBJET_DE_TEST["ValeursStr"].stepserie()
1146     print "Acces comme dict  :", OBJET_DE_TEST["ValeursStr"].valueserie()
1147     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1148     print "--> Suppression d'un objet"
1149     OBJET_DE_TEST.del_object("ValeursVectorielles")
1150     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
1151     print "--> Enregistrement de l'objet complet de Persistence composite"
1152     OBJET_DE_TEST.save_composite("composite.pkl", compress="None")
1153     print
1154
1155     print "======> Affichage graphique d'objets stockés"
1156     OBJET_DE_TEST = Persistence("My object", unit="", basetype=numpy.array)
1157     D = OBJET_DE_TEST
1158     vect1 = [1, 2, 1, 2, 1]
1159     vect2 = [-3, -3, 0, -3, -3]
1160     vect3 = [-1, 1, -5, 1, -1]
1161     vect4 = 100*[0.29, 0.97, 0.73, 0.01, 0.20]
1162     print "Stockage de 3 vecteurs de longueur identique"
1163     D.store(vect1)
1164     D.store(vect2)
1165     D.store(vect3)
1166     print "Affichage graphique de l'ensemble du stockage sur une même image"
1167     D.stepplot(
1168         title = "Tous les vecteurs",
1169         filename="vecteurs.ps",
1170         xlabel = "Axe X",
1171         ylabel = "Axe Y",
1172         pause = False )
1173     print "Stockage d'un quatrième vecteur de longueur différente"
1174     D.store(vect4)
1175     print "Affichage graphique séparé du dernier stockage"
1176     D.plot(
1177         item  = 3,
1178         title = "Vecteurs",
1179         filename = "vecteur",
1180         xlabel = "Axe X",
1181         ylabel = "Axe Y",
1182         pause = False )
1183     print "Les images ont été stockées en fichiers Postscript"
1184     print "Taille \"shape\" du dernier objet stocké",OBJET_DE_TEST.shape()
1185     print "Taille \"len\" du dernier objet stocké",len(OBJET_DE_TEST)
1186     del OBJET_DE_TEST
1187     print
1188
1189     print "======> Affichage graphique dynamique d'objets"
1190     OBJET_DE_TEST = Persistence("My object", unit="", basetype=float)
1191     D = OBJET_DE_TEST
1192     D.plot(
1193         dynamic = True,
1194         title   = "Valeur suivie",
1195         xlabel  = "Pas",
1196         ylabel  = "Valeur",
1197         pause   = False,
1198         )
1199     for i in range(1,11):
1200         D.store( i*i )
1201     print "Taille \"shape\" du dernier objet stocké",OBJET_DE_TEST.shape()
1202     print "Taille \"len\" du dernier objet stocké",len(OBJET_DE_TEST)
1203     print "Nombre d'objets stockés",OBJET_DE_TEST.stepnumber()
1204     del OBJET_DE_TEST
1205     print
1206
1207     print "======> Affectation simple d'observateurs dynamiques"
1208     def obs(var=None,info=None):
1209         print "  ---> Mise en oeuvre de l'observer"
1210         print "       var  =",var.valueserie(-1)
1211         print "       info =",info
1212     OBJET_DE_TEST = Persistence("My object", unit="", basetype=list)
1213     D = OBJET_DE_TEST
1214     D.setDataObserver( HookFunction = obs )
1215     for i in range(5):
1216         # print
1217         print "Action de 1 observer sur la variable observée, étape :",i
1218         D.store( [i, i, i] )
1219     del OBJET_DE_TEST
1220     print
1221
1222     print "======> Affectation multiple d'observateurs dynamiques"
1223     def obs(var=None,info=None):
1224         print "  ---> Mise en oeuvre de l'observer"
1225         print "       var  =",var.valueserie(-1)
1226         print "       info =",info
1227     def obs_bis(var=None,info=None):
1228         print "  ---> Mise en oeuvre de l'observer"
1229         print "       var  =",var.valueserie(-1)
1230         print "       info =",info
1231     OBJET_DE_TEST = Persistence("My object", unit="", basetype=list)
1232     D = OBJET_DE_TEST
1233     D.setDataObserver(
1234         HookFunction   = obs,
1235         Scheduler      = [2, 4],
1236         HookParameters = "Premier observer",
1237         )
1238     D.setDataObserver(
1239         HookFunction   = obs,
1240         Scheduler      = xrange(1,3),
1241         HookParameters = "Second observer",
1242         )
1243     D.setDataObserver(
1244         HookFunction   = obs_bis,
1245         Scheduler      = range(1,3)+range(7,9),
1246         HookParameters = "Troisième observer",
1247         )
1248     for i in range(5):
1249         print "Action de 3 observers sur la variable observée, étape :",i
1250         D.store( [i, i, i] )
1251     D.removeDataObserver(
1252         HookFunction   = obs,
1253         )
1254     for i in range(5,10):
1255         print "Action d'un seul observer sur la variable observée, étape :",i
1256         D.store( [i, i, i] )
1257     del OBJET_DE_TEST
1258     print
1259
1260     print "======> Utilisation des tags/attributs et stockage puis récupération de l'ensemble"
1261     OBJET_DE_TEST = CompositePersistence("My CompositePersistence", defaults=False)
1262     OBJET_DE_TEST.add_object("My ecarts", basetype = numpy.array)
1263
1264     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5),   tags = {"Camp":"Base","Carte":"IGN3","Niveau":1024,"Palier":"Premier"} )
1265     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5)+1, tags = {"Camp":"Base","Carte":"IGN4","Niveau": 210,"Palier":"Premier"} )
1266     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5)+2, tags = {"Camp":"Base","Carte":"IGN1","Niveau":1024} )
1267     OBJET_DE_TEST.store( "My ecarts", numpy.arange(1,5)+3, tags = {"Camp":"Sommet","Carte":"IGN2","Niveau":4024,"Palier":"Second","FullMap":True} )
1268
1269     print "Les pas de stockage :", OBJET_DE_TEST["My ecarts"].stepserie()
1270     print "Les valeurs         :", OBJET_DE_TEST["My ecarts"].valueserie()
1271     print "La 2ème valeur      :", OBJET_DE_TEST["My ecarts"].valueserie(1)
1272     print "La dernière valeur  :", OBJET_DE_TEST["My ecarts"].valueserie(-1)
1273     print "Liste des attributs :", OBJET_DE_TEST["My ecarts"].tagserie()
1274     print "Taille \"shape\"      :", OBJET_DE_TEST["My ecarts"].shape()
1275     print "Taille \"len\"        :", len(OBJET_DE_TEST["My ecarts"])
1276     print
1277
1278     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Palier":"Premier"} )
1279     print "Valeurs pour tag    :", OBJET_DE_TEST["My ecarts"].valueserie( tags={"Palier":"Premier"} )
1280     print
1281     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1282     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Niveau":1024} )
1283     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Base"} )
1284     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"TOTO"} )
1285     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Toto":"Premier"} )
1286     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1287     print
1288
1289     print "Combinaison 'ET' de plusieurs Tags"
1290     print "Attendu : [0, 1],    trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Base", "Palier":"Premier"} )
1291     print "Attendu : [],        trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Sommet", "Palier":"Premier"} )
1292     # Attention : {"Camp":"Sommet", "Camp":"Base"} == {"Camp":"Base"}
1293     print "Attendu : [0, 1, 2], trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Sommet", "Camp":"Base"} )
1294     print "Attendu : [2],       trouvé :",OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1", "Niveau":1024} )
1295     print
1296       
1297     print "Liste des tags pour le pas (item) 1  :",OBJET_DE_TEST["My ecarts"].tagserie(item = 1)
1298     print "Liste des tags pour le pas (item) 2  :",OBJET_DE_TEST["My ecarts"].tagserie(item = 2)
1299     print "Comme le step et l'item sont identiques par défaut, on doit avoir la même chose :"
1300     print "Liste des tags pour le pas (step) 1  :",OBJET_DE_TEST["My ecarts"].tagserie(step = 1)
1301     print "Liste des tags pour le pas (step) 2  :",OBJET_DE_TEST["My ecarts"].tagserie(step = 2)
1302     print
1303     print "Liste des tags/valeurs pour le pas 1 :",OBJET_DE_TEST["My ecarts"].tagserie(item = 1, withValues=True)
1304     print "Liste des tags/valeurs pour le pas 2 :",OBJET_DE_TEST["My ecarts"].tagserie(item = 2, withValues=True)
1305     print
1306
1307     print "Liste des valeurs possibles pour 1 tag donné 'Camp'   :",OBJET_DE_TEST["My ecarts"].tagserie(outputTag="Camp")
1308     print "Liste des valeurs possibles pour 1 tag donné 'Toto'   :",OBJET_DE_TEST["My ecarts"].tagserie(outputTag="Toto")
1309     print "Liste des valeurs possibles pour 1 tag donné 'Niveau' :",OBJET_DE_TEST["My ecarts"].tagserie(outputTag="Niveau")
1310     print
1311
1312     OBJET_DE_TEST.add_object("My other ecarts", basetype = numpy.array)
1313     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5),   tags = {"Camp":"Base","Carte":"IGN3","Niveau":1024,"Palier":"Premier"} )
1314     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5)+1, tags = {"Camp":"Base","Carte":"IGN4","Niveau": 210,"Palier":"Premier"} )
1315     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5)+2, tags = {"Camp":"Base","Carte":"IGN1","Niveau":1024} )
1316     OBJET_DE_TEST.store( "My other ecarts", numpy.arange(-1,5)+3, tags = {"Camp":"Sommet","Carte":"IGN2","Niveau":4024,"Palier":"Second"} )
1317
1318     print "Objets présents dans le composite :",OBJET_DE_TEST.get_stored_objects()
1319     fichier = "composite.pkl.gz"
1320     print "Sauvegarde sur \"%s\"..."%fichier
1321     OBJET_DE_TEST.save_composite( fichier )
1322     print "Effacement de l'objet en memoire"
1323     del OBJET_DE_TEST
1324     print
1325
1326     print "Relecture de l'objet sur \"%s\"..."%fichier
1327     OBJET_DE_TEST = CompositePersistence("My CompositePersistence bis", defaults=False)
1328     OBJET_DE_TEST.load_composite( fichier )
1329     print "Objets présents dans le composite :",OBJET_DE_TEST.get_stored_objects()
1330     print "Taille des objets contenus :"
1331     for name in OBJET_DE_TEST.get_stored_objects():
1332         print "  Objet \"%s\" : taille unitaire de %i"%(name,len(OBJET_DE_TEST[name]))
1333
1334     print
1335     print "Les pas de stockage :", OBJET_DE_TEST["My ecarts"].stepserie()
1336     print "Les valeurs         :", OBJET_DE_TEST["My ecarts"].valueserie()
1337     print "La 2ème valeur      :", OBJET_DE_TEST["My ecarts"].valueserie(1)
1338     print "La dernière valeur  :", OBJET_DE_TEST["My ecarts"].valueserie(-1)
1339     print "Liste des attributs :", OBJET_DE_TEST["My ecarts"].tagserie()
1340     print "Taille \"shape\"      :", OBJET_DE_TEST["My ecarts"].shape()
1341     print "Taille \"len\"        :", len(OBJET_DE_TEST["My ecarts"])
1342     print
1343
1344     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Palier":"Premier"} )
1345     print "Valeurs pour tag    :", OBJET_DE_TEST["My ecarts"].valueserie( tags={"Palier":"Premier"} )
1346     print
1347     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1348     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Niveau":1024} )
1349     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"Base"} )
1350     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Camp":"TOTO"} )
1351     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Toto":"Premier"} )
1352     print "Pas pour tag        :", OBJET_DE_TEST["My ecarts"].stepserie( tags={"Carte":"IGN1"} )
1353     print
1354     print "Attributs                 :", OBJET_DE_TEST["My ecarts"].tagserie()
1355     print "Attributs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( tags={"Camp":"Base"} )
1356     print "Attributs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( tags={"Niveau":4024} )
1357     print
1358     print "Attributs et valeurs                 :", OBJET_DE_TEST["My ecarts"].tagserie( withValues=True )
1359     print "Attributs et valeurs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( withValues=True, tags={"Camp":"Base"} )
1360     print "Attributs et valeurs pour tag filtré :", OBJET_DE_TEST["My ecarts"].tagserie( withValues=True, tags={"Niveau":4024} )
1361     print
1362     print "Valeur d'attribut pour un tag donné 'BU'           :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Niveau" )
1363     print "Valeur d'attribut pour un tag donné 'BU' filtré    :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Niveau", tags={"Camp":"Base"} )
1364     print "Valeur d'attribut pour un tag donné 'BU' filtré    :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Niveau", tags={"Palier":"Second"} )
1365     print "Valeur d'attribut pour un tag donné 'Camp' filtré  :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Camp", tags={"Palier":"Premier"} )
1366     print "Valeur d'attribut pour un tag donné 'Carte' filtré :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Carte", tags={"Palier":"Premier"} )
1367     print "Valeur d'attribut pour un tag donné 'Carte' filtré :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Carte", tags={"Palier":"Premier","Niveau":4024} )
1368     print "Valeur d'attribut pour un tag donné 'Carte' filtré :", OBJET_DE_TEST["My ecarts"].tagserie( outputTag = "Carte", tags={"Palier":"Premier","Niveau":210} )
1369     print