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