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