]> SALOME platform Git repositories - modules/adao.git/blob - src/daComposant/daCore/Persistence.py
Salome HOME
79dae997ee8f08cda93eee0d9f9393baca4ed58c
[modules/adao.git] / src / daComposant / daCore / Persistence.py
1 #-*-coding:iso-8859-1-*-
2 #
3 #  Copyright (C) 2008-2010  EDF R&D
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 #  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21 __doc__ = """
22     Définit des outils de persistence et d'enregistrement de séries de valeurs
23     pour analyse ultérieure ou utilisation de calcul.
24 """
25 __author__ = "Jean-Philippe ARGAUD - Mars 2008"
26
27 import numpy
28
29 from PlatformInfo import PathManagement ; PathManagement()
30
31 # ==============================================================================
32 class Persistence:
33     """
34     Classe générale de persistence définissant les accesseurs nécessaires
35     (Template)
36     """
37     def __init__(self, name="", unit="", basetype=str):
38         """
39         name : nom courant
40         unit : unité
41         basetype : type de base de l'objet stocké à chaque pas
42         
43         La gestion interne des données est exclusivement basée sur les variables
44         initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
45         objets comme des attributs) :
46         __step   : numérotation par défaut du pas courant
47         __basetype : le type de base de chaque valeur, sous la forme d'un type
48                      permettant l'instanciation ou le casting Python 
49         __steps  : les pas de stockage. Par défaut, c'est __step
50         __values : les valeurs de stockage. Par défaut, c'est None
51         """
52         self.__name = str(name)
53         self.__unit = str(unit)
54         #
55         self.__step     = -1
56         self.__basetype = basetype
57         #
58         self.__steps    = []
59         self.__values   = []
60         #
61         self.__dynamic  = False
62         #
63         self.__dataobservers = []
64     
65     def basetype(self, basetype=None):
66         """
67         Renvoie ou met en place le type de base des objets stockés
68         """
69         if basetype is None:
70             return self.__basetype
71         else:
72             self.__basetype = basetype
73
74     def store(self, value=None, step=None):
75         """
76         Stocke une valeur à un pas. Une instanciation est faite avec le type de
77         base pour stocker l'objet. Si le pas n'est pas fournit, on utilise
78         l'étape de stockage comme valeur de pas.
79         """
80         if value is None: raise ValueError("Value argument required")
81         self.__step += 1
82         if step is not None:
83             self.__steps.append(step)
84         else:
85             self.__steps.append(self.__step)
86         #
87         self.__values.append(self.__basetype(value))
88         #
89         if self.__dynamic: self.__replot()
90         for hook, parameters, scheduler in self.__dataobservers:
91             if self.__step in scheduler:
92                 hook( self, parameters )
93
94     def shape(self):
95         """
96         Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
97         objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
98         complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
99         longueur. Par défaut, renvoie 1.
100         """
101         if len(self.__values) > 0:
102             if self.__basetype in [numpy.matrix, numpy.array]:
103                 return self.__values[-1].shape
104             elif self.__basetype in [int, float]:
105                 return (1,)
106             elif self.__basetype in [list, dict]:
107                 return (len(self.__values[-1]),)
108             else:
109                 return (1,)
110         else:
111             raise ValueError("Object has no shape before its first storage")
112
113     def __len__(self):
114         """
115         Renvoie le nombre d'éléments dans un séquence ou la plus grande
116         dimension d'une matrice
117         """
118         return max( self.shape() )
119
120     # ---------------------------------------------------------
121     def stepserie(self, item=None, step=None):
122         """
123         Renvoie par défaut toute la liste des pas de temps. Si l'argument "step"
124         existe dans la liste des pas de stockage effectués, renvoie ce pas
125         "step". Si l'argument "item" est correct, renvoie le pas stockée au
126         numéro "item".
127         """
128         if step is not None and step in self.__steps:
129             return step
130         elif item is not None and item < len(self.__steps):
131             return self.__steps[item]
132         else:
133             return self.__steps
134
135     def valueserie(self, item=None, step=None):
136         """
137         Renvoie par défaut toute la liste des valeurs/objets. Si l'argument
138         "step" existe dans la liste des pas de stockage effectués, renvoie la
139         valeur stockée à ce pas "step". Si l'argument "item" est correct,
140         renvoie la valeur stockée au numéro "item".
141         """
142         if step is not None and step in self.__steps:
143             index = self.__steps.index(step)
144             return self.__values[index]
145         elif item is not None and item < len(self.__values):
146             return self.__values[item]
147         else:
148             return self.__values
149     
150     def stepnumber(self):
151         """
152         Renvoie le nombre de pas de stockage.
153         """
154         return len(self.__steps)
155
156     # ---------------------------------------------------------
157     # Méthodes d'accès de type dictionnaire
158     def keys(self):
159         return self.stepserie()
160
161     def values(self):
162         return self.valueserie()
163
164     def items(self):
165         pairs = []
166         for i in xrange(self.stepnumber()):
167             pairs.append( (self.stepserie(item=i), self.valueserie(item=i)) )
168         return pairs
169
170     # ---------------------------------------------------------
171     def mean(self):
172         """
173         Renvoie la valeur moyenne des données à chaque pas. Il faut que le type
174         de base soit compatible avec les types élémentaires numpy.
175         """
176         try:
177             return [numpy.matrix(item).mean() for item in self.__values]
178         except:
179             raise TypeError("Base type is incompatible with numpy")
180
181     def std(self, ddof=0):
182         """
183         Renvoie l'écart-type des données à chaque pas. Il faut que le type de
184         base soit compatible avec les types élémentaires numpy.
185         
186         ddof : c'est le nombre de degrés de liberté pour le calcul de
187                l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
188         """
189         try:
190             if numpy.version.version >= '1.1.0':
191                 return [numpy.matrix(item).std(ddof=ddof) for item in self.__values]
192             else:
193                 return [numpy.matrix(item).std() for item in self.__values]
194         except:
195             raise TypeError("Base type is incompatible with numpy")
196
197     def sum(self):
198         """
199         Renvoie la somme des données à chaque pas. Il faut que le type de
200         base soit compatible avec les types élémentaires numpy.
201         """
202         try:
203             return [numpy.matrix(item).sum() for item in self.__values]
204         except:
205             raise TypeError("Base type is incompatible with numpy")
206
207     def min(self):
208         """
209         Renvoie le minimum des données à chaque pas. Il faut que le type de
210         base soit compatible avec les types élémentaires numpy.
211         """
212         try:
213             return [numpy.matrix(item).min() for item in self.__values]
214         except:
215             raise TypeError("Base type is incompatible with numpy")
216
217     def max(self):
218         """
219         Renvoie le maximum des données à chaque pas. Il faut que le type de
220         base soit compatible avec les types élémentaires numpy.
221         """
222         try:
223             return [numpy.matrix(item).max() for item in self.__values]
224         except:
225             raise TypeError("Base type is incompatible with numpy")
226
227     def __preplot(self,
228             title    = "",
229             xlabel   = "",
230             ylabel   = "",
231             ltitle   = None,
232             geometry = "600x400",
233             persist  = False,
234             pause    = True,
235             ):
236         import os
237         #
238         # Vérification de la disponibilité du module Gnuplot
239         try:
240             import Gnuplot
241             self.__gnuplot = Gnuplot
242         except:
243             raise ImportError("The Gnuplot module is required to plot the object.")
244         #
245         # Vérification et compléments sur les paramètres d'entrée
246         if persist:
247             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
248         else:
249             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
250         if ltitle is None:
251             ltitle = ""
252         self.__g = self.__gnuplot.Gnuplot() # persist=1
253         self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
254         self.__g('set style data lines')
255         self.__g('set grid')
256         self.__g('set autoscale')
257         self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
258         self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
259         self.__title  = title
260         self.__ltitle = ltitle
261         self.__pause  = pause
262
263     def plot(self, item=None, step=None,
264             steps    = None,
265             title    = "",
266             xlabel   = "",
267             ylabel   = "",
268             ltitle   = None,
269             geometry = "600x400",
270             filename = "",
271             dynamic  = False,
272             persist  = False,
273             pause    = True,
274             ):
275         """
276         Renvoie un affichage de la valeur à chaque pas, si elle est compatible
277         avec un affichage Gnuplot (donc essentiellement un vecteur). Si
278         l'argument "step" existe dans la liste des pas de stockage effectués,
279         renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
280         "item" est correct, renvoie l'affichage de la valeur stockée au numéro
281         "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
282         affichage successif de tous les pas.
283
284         Arguments :
285             - step     : valeur du pas à afficher
286             - item     : index de la valeur à afficher
287             - steps    : liste unique des pas de l'axe des X, ou None si c'est
288                          la numérotation par défaut
289             - title    : base du titre général, qui sera automatiquement
290                          complétée par la mention du pas
291             - xlabel   : label de l'axe des X
292             - ylabel   : label de l'axe des Y
293             - ltitle   : titre associé au vecteur tracé
294             - geometry : taille en pixels de la fenêtre et position du coin haut
295                          gauche, au format X11 : LxH+X+Y (défaut : 600x400)
296             - filename : base de nom de fichier Postscript pour une sauvegarde,
297                          qui est automatiquement complétée par le numéro du
298                          fichier calculé par incrément simple de compteur
299             - dynamic  : effectue un affichage des valeurs à chaque stockage
300                          (au-delà du second) La méthode "plot" permet de déclarer
301                          l'affichage dynamique, et c'est la méthode "__replot"
302                          qui est utilisée pour l'effectuer
303             - persist  : booléen indiquant que la fenêtre affichée sera
304                          conservée lors du passage au dessin suivant
305                          Par défaut, persist = False
306             - pause    : booléen indiquant une pause après chaque tracé, et
307                          attendant un Return
308                          Par défaut, pause = True
309         """
310         import os
311         if not self.__dynamic:
312             self.__preplot(title, xlabel, ylabel, ltitle, geometry, persist, pause )
313             if dynamic:
314                 self.__dynamic = True
315                 if len(self.__values) == 0: return 0
316         #
317         # Tracé du ou des vecteurs demandés
318         indexes = []
319         if step is not None and step in self.__steps:
320             indexes.append(self.__steps.index(step))
321         elif item is not None and item < len(self.__values):
322             indexes.append(item)
323         else:
324             indexes = indexes + range(len(self.__values))
325         #
326         i = -1
327         for index in indexes:
328             self.__g('set title  "'+str(title).encode('ascii','replace')+' (pas '+str(index)+')"')
329             if ( type(steps) is type([]) ) or ( type(steps) is type(numpy.array([])) ):
330                 Steps = list(steps)
331             else:
332                 Steps = range(len(self.__values[index]))
333             #
334             self.__g.plot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
335             #
336             if filename != "":
337                 i += 1
338                 stepfilename = "%s_%03i.ps"%(filename,i)
339                 if os.path.isfile(stepfilename):
340                     raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
341                 self.__g.hardcopy(filename=stepfilename, color=1)
342             if self.__pause:
343                 raw_input('Please press return to continue...\n')
344
345     def __replot(self):
346         """
347         Affichage dans le cas du suivi dynamique de la variable
348         """
349         if self.__dynamic and len(self.__values) < 2: return 0
350         #
351         import os
352         self.__g('set title  "'+str(self.__title).encode('ascii','replace'))
353         Steps = range(len(self.__values))
354         self.__g.plot( self.__gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
355         #
356         if self.__pause:
357             raw_input('Please press return to continue...\n')
358
359     # ---------------------------------------------------------
360     def stepmean(self):
361         """
362         Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
363         longueur des pas. Il faut que le type de base soit compatible avec
364         les types élémentaires numpy.
365         """
366         try:
367             return numpy.matrix(self.__values).mean()
368         except:
369             raise TypeError("Base type is incompatible with numpy")
370
371     def stepstd(self, ddof=0):
372         """
373         Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
374         longueur des pas. Il faut que le type de base soit compatible avec
375         les types élémentaires numpy.
376         
377         ddof : c'est le nombre de degrés de liberté pour le calcul de
378                l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
379         """
380         try:
381             if numpy.version.version >= '1.1.0':
382                 return numpy.matrix(self.__values).std(ddof=ddof)
383             else:
384                 return numpy.matrix(self.__values).std()
385         except:
386             raise TypeError("Base type is incompatible with numpy")
387
388     def stepsum(self):
389         """
390         Renvoie la somme de toutes les valeurs sans tenir compte de la
391         longueur des pas. Il faut que le type de base soit compatible avec
392         les types élémentaires numpy.
393         """
394         try:
395             return numpy.matrix(self.__values).sum()
396         except:
397             raise TypeError("Base type is incompatible with numpy")
398
399     def stepmin(self):
400         """
401         Renvoie le minimum de toutes les valeurs sans tenir compte de la
402         longueur des pas. Il faut que le type de base soit compatible avec
403         les types élémentaires numpy.
404         """
405         try:
406             return numpy.matrix(self.__values).min()
407         except:
408             raise TypeError("Base type is incompatible with numpy")
409
410     def stepmax(self):
411         """
412         Renvoie le maximum de toutes les valeurs sans tenir compte de la
413         longueur des pas. Il faut que le type de base soit compatible avec
414         les types élémentaires numpy.
415         """
416         try:
417             return numpy.matrix(self.__values).max()
418         except:
419             raise TypeError("Base type is incompatible with numpy")
420
421     def cumsum(self):
422         """
423         Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
424         longueur des pas. Il faut que le type de base soit compatible avec
425         les types élémentaires numpy.
426         """
427         try:
428             return numpy.matrix(self.__values).cumsum(axis=0)
429         except:
430             raise TypeError("Base type is incompatible with numpy")
431
432     # On pourrait aussi utiliser les autres attributs d'une "matrix", comme
433     # "tofile", "min"...
434
435     def stepplot(self,
436             steps    = None,
437             title    = "",
438             xlabel   = "",
439             ylabel   = "",
440             ltitle   = None,
441             geometry = "600x400",
442             filename = "",
443             persist  = False,
444             pause    = True,
445             ):
446         """
447         Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
448         elles sont compatibles avec un affichage Gnuplot (donc essentiellement
449         un vecteur). Si l'argument "step" existe dans la liste des pas de
450         stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
451         "step". Si l'argument "item" est correct, renvoie l'affichage de la
452         valeur stockée au numéro "item".
453
454         Arguments :
455             - steps    : liste unique des pas de l'axe des X, ou None si c'est
456                          la numérotation par défaut
457             - title    : base du titre général, qui sera automatiquement
458                          complétée par la mention du pas
459             - xlabel   : label de l'axe des X
460             - ylabel   : label de l'axe des Y
461             - ltitle   : titre associé au vecteur tracé
462             - geometry : taille en pixels de la fenêtre et position du coin haut
463                          gauche, au format X11 : LxH+X+Y (défaut : 600x400)
464             - filename : nom de fichier Postscript pour une sauvegarde
465             - persist  : booléen indiquant que la fenêtre affichée sera
466                          conservée lors du passage au dessin suivant
467                          Par défaut, persist = False
468             - pause    : booléen indiquant une pause après chaque tracé, et
469                          attendant un Return
470                          Par défaut, pause = True
471         """
472         import os
473         #
474         # Vérification de la disponibilité du module Gnuplot
475         try:
476             import Gnuplot
477             self.__gnuplot = Gnuplot
478         except:
479             raise ImportError("The Gnuplot module is required to plot the object.")
480         #
481         # Vérification et compléments sur les paramètres d'entrée
482         if persist:
483             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
484         else:
485             self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
486         if ltitle is None:
487             ltitle = ""
488         if ( type(steps) is type([]) ) or ( type(steps) is type(numpy.array([])) ):
489             Steps = list(steps)
490         else:
491             Steps = range(len(self.__values[0]))
492         self.__g = self.__gnuplot.Gnuplot() # persist=1
493         self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
494         self.__g('set style data lines')
495         self.__g('set grid')
496         self.__g('set autoscale')
497         self.__g('set title  "'+str(title).encode('ascii','replace') +'"')
498         self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
499         self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
500         #
501         # Tracé du ou des vecteurs demandés
502         indexes = range(len(self.__values))
503         self.__g.plot( self.__gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
504         for index in indexes:
505             self.__g.replot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
506         #
507         if filename != "":
508             self.__g.hardcopy(filename=filename, color=1)
509         if pause:
510             raw_input('Please press return to continue...\n')
511
512     # ---------------------------------------------------------
513     def setDataObserver(self,
514         HookFunction   = None,
515         HookParameters = None,
516         Scheduler      = None,
517         ):
518         """
519         Méthode d'association à la variable d'un triplet définissant un observer
520         
521         Le Scheduler attendu est une fréquence, une simple liste d'index ou un
522         xrange des index.
523         """
524         #
525         # Vérification du Scheduler
526         # -------------------------
527         maxiter = int( 1e9 )
528         if type(Scheduler) is int:    # Considéré comme une fréquence à partir de 0
529             Schedulers = xrange( 0, maxiter, int(Scheduler) )
530         elif type(Scheduler) is xrange: # Considéré comme un itérateur
531             Schedulers = Scheduler
532         elif type(Scheduler) is list: # Considéré comme des index explicites
533             Schedulers = map( long, Scheduler )
534         else:                         # Dans tous les autres cas, activé par défaut
535             Schedulers = xrange( 0, maxiter )
536         #
537         # Stockage interne de l'observer dans la variable
538         # -----------------------------------------------
539         self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
540
541 # ==============================================================================
542 class OneScalar(Persistence):
543     """
544     Classe définissant le stockage d'une valeur unique réelle (float) par pas
545     
546     Le type de base peut être changé par la méthode "basetype", mais il faut que
547     le nouveau type de base soit compatible avec les types par éléments de 
548     numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
549     ou des matrices comme dans les classes suivantes, mais c'est déconseillé
550     pour conserver une signification claire des noms.
551     """
552     def __init__(self, name="", unit="", basetype = float):
553         Persistence.__init__(self, name, unit, basetype)
554
555 class OneVector(Persistence):
556     """
557     Classe définissant le stockage d'une liste (list) de valeurs homogènes par
558     hypothèse par pas. Pour éviter les confusions, ne pas utiliser la classe
559     "OneVector" pour des données hétérogènes, mais bien "OneList".
560     """
561     def __init__(self, name="", unit="", basetype = list):
562         Persistence.__init__(self, name, unit, basetype)
563
564 class OneMatrix(Persistence):
565     """
566     Classe définissant le stockage d'une matrice de valeurs (numpy.matrix) par
567     pas
568     """
569     def __init__(self, name="", unit="", basetype = numpy.matrix):
570         Persistence.__init__(self, name, unit, basetype)
571
572 class OneList(Persistence):
573     """
574     Classe définissant le stockage d'une liste de valeurs potentiellement
575     hétérogènes (list) par pas. Pour éviter les confusions, ne pas utiliser la
576     classe "OneVector" pour des données hétérogènes, mais bien "OneList".
577     """
578     def __init__(self, name="", unit="", basetype = list):
579         Persistence.__init__(self, name, unit, basetype)
580
581 def NoType( value ): return value
582
583 class OneNoType(Persistence):
584     """
585     Classe définissant le stockage d'un objet sans modification (cast) de type.
586     Attention, selon le véritable type de l'objet stocké à chaque pas, les
587     opérations arithmétiques à base de numpy peuvent être invalides ou donner
588     des résultats inatendus. Cette classe n'est donc à utiliser qu'à bon escient
589     volontairement, et pas du tout par défaut.
590     """
591     def __init__(self, name="", unit="", basetype = NoType):
592         Persistence.__init__(self, name, unit, basetype)
593
594 # ==============================================================================
595 class CompositePersistence:
596     """
597     Structure de stockage permettant de rassembler plusieurs objets de
598     persistence.
599     
600     Des objets par défaut sont prévus, et des objets supplémentaires peuvent
601     être ajoutés.
602     """
603     def __init__(self, name=""):
604         """
605         name : nom courant
606         
607         La gestion interne des données est exclusivement basée sur les variables
608         initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
609         objets comme des attributs) :
610         __StoredObjects : objets de type persistence collectés dans cet objet
611         """
612         self.__name = str(name)
613         #
614         self.__StoredObjects = {}
615         #
616         # Definition des objets par defaut
617         # --------------------------------
618         self.__StoredObjects["Background"]       = OneVector("Background", basetype=numpy.array)
619         self.__StoredObjects["BackgroundError"]  = OneMatrix("BackgroundError")
620         self.__StoredObjects["Observation"]      = OneVector("Observation", basetype=numpy.array)
621         self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
622         self.__StoredObjects["Analysis"]         = OneVector("Analysis", basetype=numpy.array)
623         self.__StoredObjects["AnalysisError"]    = OneMatrix("AnalysisError")
624         self.__StoredObjects["Innovation"]       = OneVector("Innovation", basetype=numpy.array)
625         self.__StoredObjects["KalmanGainK"]      = OneMatrix("KalmanGainK")
626         self.__StoredObjects["OperatorH"]        = OneMatrix("OperatorH")
627         self.__StoredObjects["RmsOMA"]           = OneScalar("RmsOMA")
628         self.__StoredObjects["RmsOMB"]           = OneScalar("RmsOMB")
629         self.__StoredObjects["RmsBMA"]           = OneScalar("RmsBMA")
630         #
631
632     def store(self, name=None, value=None, step=None):
633         """
634         Stockage d'une valeur "value" pour le "step" dans la variable "name".
635         """
636         if name is None: raise ValueError("Storable object name is required for storage.")
637         if name not in self.__StoredObjects.keys():
638             raise ValueError("No such name '%s' exists in storable objects."%name)
639         self.__StoredObjects[name].store( value=value, step=step )
640
641     def add_object(self, name=None, persistenceType=Persistence, basetype=numpy.array ):
642         """
643         Ajoute dans les objets stockables un nouvel objet défini par son nom, son
644         type de Persistence et son type de base à chaque pas.
645         """
646         if name is None: raise ValueError("Object name is required for adding an object.")
647         if name in self.__StoredObjects.keys():
648             raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
649         self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
650
651     def get_object(self, name=None ):
652         """
653         Renvoie l'objet de type Persistence qui porte le nom demandé.
654         """
655         if name is None: raise ValueError("Object name is required for retrieving an object.")
656         if name not in self.__StoredObjects.keys():
657             raise ValueError("No such name '%s' exists in stored objects."%name)
658         return self.__StoredObjects[name]
659
660     def set_object(self, name=None, objet=None ):
661         """
662         Affecte directement un 'objet' qui porte le nom 'name' demandé.
663         Attention, il n'est pas effectué de vérification sur le type, qui doit
664         comporter les méthodes habituelles de Persistence pour que cela
665         fonctionne.
666         """
667         if name is None: raise ValueError("Object name is required for setting an object.")
668         if name in self.__StoredObjects.keys():
669             raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
670         self.__StoredObjects[name] = objet
671
672     def del_object(self, name=None ):
673         """
674         Supprime un objet de la liste des objets stockables.
675         """
676         if name is None: raise ValueError("Object name is required for retrieving an object.")
677         if name not in self.__StoredObjects.keys():
678             raise ValueError("No such name '%s' exists in stored objects."%name)
679         del self.__StoredObjects[name]
680
681     # ---------------------------------------------------------
682     # Méthodes d'accès de type dictionnaire
683     def __getitem__(self, name=None ):
684         return self.get_object( name )
685
686     def __setitem__(self, name=None, objet=None ):
687         self.set_object( name, objet )
688
689     def keys(self):
690         return self.get_stored_objects(hideVoidObjects = False)
691
692     def values(self):
693         return self.__StoredObjects.values()
694
695     def items(self):
696         return self.__StoredObjects.items()
697
698     # ---------------------------------------------------------
699     def get_stored_objects(self, hideVoidObjects = False):
700         objs = self.__StoredObjects.keys()
701         if hideVoidObjects:
702             usedObjs = []
703             for k in objs:
704                 try:
705                     if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
706                 except:
707                     pass
708             objs = usedObjs
709         objs.sort()
710         return objs
711
712     # ---------------------------------------------------------
713     def save_composite(self, filename=None, mode="pickle"):
714         """
715         Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
716         et renvoi le nom du fichier
717         """
718         import os
719         if filename is None:
720             filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
721         else:
722             filename = os.path.abspath( filename )
723         #
724         import cPickle
725         if mode is "pickle":
726             output = open( filename, 'wb')
727             cPickle.dump(self, output)
728             output.close()
729         else:
730             raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
731         #
732         return filename
733
734     def load_composite(self, filename=None, mode="pickle"):
735         """
736         Recharge un objet composite sauvé en fichier
737         """
738         import os
739         if filename is None:
740             raise ValueError("A file name if requested to load a composite.")
741         else:
742             filename = os.path.abspath( filename )
743         #
744         import cPickle
745         if mode is "pickle":
746             pkl_file = open(filename, 'rb')
747             output = cPickle.load(pkl_file)
748             for k in output.keys():
749                 self[k] = output[k]
750         else:
751             raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
752         #
753         return filename
754
755 # ==============================================================================
756 if __name__ == "__main__":
757     print '\n AUTODIAGNOSTIC \n'
758
759     print "======> Un flottant"
760     OBJET_DE_TEST = OneScalar("My float", unit="cm")
761     OBJET_DE_TEST.store( 5.)
762     OBJET_DE_TEST.store(-5.)
763     OBJET_DE_TEST.store( 1.)
764     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
765     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
766     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
767     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
768     print "Valeurs par pas :"
769     print "  La moyenne        :", OBJET_DE_TEST.mean()
770     print "  L'écart-type      :", OBJET_DE_TEST.std()
771     print "  La somme          :", OBJET_DE_TEST.sum()
772     print "  Le minimum        :", OBJET_DE_TEST.min()
773     print "  Le maximum        :", OBJET_DE_TEST.max()
774     print "Valeurs globales :"
775     print "  La moyenne        :", OBJET_DE_TEST.stepmean()
776     print "  L'écart-type      :", OBJET_DE_TEST.stepstd()
777     print "  La somme          :", OBJET_DE_TEST.stepsum()
778     print "  Le minimum        :", OBJET_DE_TEST.stepmin()
779     print "  Le maximum        :", OBJET_DE_TEST.stepmax()
780     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
781     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
782     print "Taille \"len\"        :", len(OBJET_DE_TEST)
783     del OBJET_DE_TEST
784     print
785
786     print "======> Un entier"
787     OBJET_DE_TEST = OneScalar("My int", unit="cm", basetype=int)
788     OBJET_DE_TEST.store( 5 )
789     OBJET_DE_TEST.store(-5 )
790     OBJET_DE_TEST.store( 1.)
791     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
792     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
793     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
794     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
795     print "Valeurs par pas :"
796     print "  La moyenne        :", OBJET_DE_TEST.mean()
797     print "  L'écart-type      :", OBJET_DE_TEST.std()
798     print "  La somme          :", OBJET_DE_TEST.sum()
799     print "  Le minimum        :", OBJET_DE_TEST.min()
800     print "  Le maximum        :", OBJET_DE_TEST.max()
801     print "Valeurs globales :"
802     print "  La moyenne        :", OBJET_DE_TEST.stepmean()
803     print "  L'écart-type      :", OBJET_DE_TEST.stepstd()
804     print "  La somme          :", OBJET_DE_TEST.stepsum()
805     print "  Le minimum        :", OBJET_DE_TEST.stepmin()
806     print "  Le maximum        :", OBJET_DE_TEST.stepmax()
807     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
808     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
809     print "Taille \"len\"        :", len(OBJET_DE_TEST)
810     del OBJET_DE_TEST
811     print
812
813     print "======> Un booléen"
814     OBJET_DE_TEST = OneScalar("My bool", unit="", basetype=bool)
815     OBJET_DE_TEST.store( True  )
816     OBJET_DE_TEST.store( False )
817     OBJET_DE_TEST.store( True  )
818     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
819     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
820     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
821     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
822     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
823     print "Taille \"len\"        :", len(OBJET_DE_TEST)
824     del OBJET_DE_TEST
825     print
826
827     print "======> Un vecteur de flottants"
828     OBJET_DE_TEST = OneVector("My float vector", unit="cm")
829     OBJET_DE_TEST.store( (5 , -5) )
830     OBJET_DE_TEST.store( (-5, 5 ) )
831     OBJET_DE_TEST.store( (1., 1.) )
832     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
833     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
834     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
835     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
836     print "Valeurs par pas :"
837     print "  La moyenne        :", OBJET_DE_TEST.mean()
838     print "  L'écart-type      :", OBJET_DE_TEST.std()
839     print "  La somme          :", OBJET_DE_TEST.sum()
840     print "  Le minimum        :", OBJET_DE_TEST.min()
841     print "  Le maximum        :", OBJET_DE_TEST.max()
842     print "Valeurs globales :"
843     print "  La moyenne        :", OBJET_DE_TEST.stepmean()
844     print "  L'écart-type      :", OBJET_DE_TEST.stepstd()
845     print "  La somme          :", OBJET_DE_TEST.stepsum()
846     print "  Le minimum        :", OBJET_DE_TEST.stepmin()
847     print "  Le maximum        :", OBJET_DE_TEST.stepmax()
848     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
849     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
850     print "Taille \"len\"        :", len(OBJET_DE_TEST)
851     del OBJET_DE_TEST
852     print
853
854     print "======> Une liste hétérogène"
855     OBJET_DE_TEST = OneList("My list", unit="bool/cm")
856     OBJET_DE_TEST.store( (True , -5) )
857     OBJET_DE_TEST.store( (False,  5 ) )
858     OBJET_DE_TEST.store( (True ,  1.) )
859     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
860     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
861     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
862     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
863     print "Valeurs par pas : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
864     print "  La moyenne        :", OBJET_DE_TEST.mean()
865     print "  L'écart-type      :", OBJET_DE_TEST.std()
866     print "  La somme          :", OBJET_DE_TEST.sum()
867     print "  Le minimum        :", OBJET_DE_TEST.min()
868     print "  Le maximum        :", OBJET_DE_TEST.max()
869     print "Valeurs globales : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
870     print "  La moyenne        :", OBJET_DE_TEST.stepmean()
871     print "  L'écart-type      :", OBJET_DE_TEST.stepstd()
872     print "  La somme          :", OBJET_DE_TEST.stepsum()
873     print "  Le minimum        :", OBJET_DE_TEST.stepmin()
874     print "  Le maximum        :", OBJET_DE_TEST.stepmax()
875     print "  La somme cumulée  :", OBJET_DE_TEST.cumsum()
876     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
877     print "Taille \"len\"        :", len(OBJET_DE_TEST)
878     del OBJET_DE_TEST
879     print
880
881     print "======> Utilisation directe de la classe Persistence"
882     OBJET_DE_TEST = Persistence("My object", unit="", basetype=int )
883     OBJET_DE_TEST.store( 1  )
884     OBJET_DE_TEST.store( 3 )
885     OBJET_DE_TEST.store( 7  )
886     print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
887     print "Les valeurs         :", OBJET_DE_TEST.valueserie()
888     print "La 2ème valeur      :", OBJET_DE_TEST.valueserie(1)
889     print "La dernière valeur  :", OBJET_DE_TEST.valueserie(-1)
890     print "Taille \"shape\"      :", OBJET_DE_TEST.shape()
891     print "Taille \"len\"        :", len(OBJET_DE_TEST)
892     del OBJET_DE_TEST
893     print
894
895     print "======> Utilisation des méthodes d'accès de type dictionnaire"
896     OBJET_DE_TEST = OneScalar("My int", unit="cm", basetype=int)
897     for i in range(5):
898         OBJET_DE_TEST.store( 7+i )
899     print "Taille \"len\"        :", len(OBJET_DE_TEST)
900     print "Les pas de stockage :", OBJET_DE_TEST.keys()
901     print "Les valeurs         :", OBJET_DE_TEST.values()
902     print "Les paires          :", OBJET_DE_TEST.items()
903     del OBJET_DE_TEST
904     print
905
906     print "======> Persistence composite"
907     OBJET_DE_TEST = CompositePersistence("My CompositePersistence")
908     print "Objets stockables :", OBJET_DE_TEST.get_stored_objects()
909     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
910     print "--> Stockage d'une valeur de Background"
911     OBJET_DE_TEST.store("Background",numpy.zeros(5))
912     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
913     print "--> Ajout d'un objet nouveau par defaut, de type vecteur numpy par pas"
914     OBJET_DE_TEST.add_object("ValeursVectorielles")
915     OBJET_DE_TEST.store("ValeursVectorielles",numpy.zeros(5))
916     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
917     print "--> Ajout d'un objet nouveau de type liste par pas"
918     OBJET_DE_TEST.add_object("ValeursList", persistenceType=OneList )
919     OBJET_DE_TEST.store("ValeursList",range(5))
920     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
921     print "--> Ajout d'un objet nouveau, de type vecteur string par pas"
922     OBJET_DE_TEST.add_object("ValeursStr", persistenceType=Persistence, basetype=str )
923     OBJET_DE_TEST.store("ValeursStr","c020")
924     OBJET_DE_TEST.store("ValeursStr","c021")
925     print "Les valeurs       :", OBJET_DE_TEST.get_object("ValeursStr").valueserie()
926     print "Acces comme dict  :", OBJET_DE_TEST["ValeursStr"].stepserie()
927     print "Acces comme dict  :", OBJET_DE_TEST["ValeursStr"].valueserie()
928     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
929     print "--> Suppression d'un objet"
930     OBJET_DE_TEST.del_object("ValeursVectorielles")
931     print "Objets actifs     :", OBJET_DE_TEST.get_stored_objects( hideVoidObjects = True )
932     print "--> Enregistrement de l'objet complet de Persistence composite"
933     OBJET_DE_TEST.save_composite("composite.pkl")
934     print
935
936     print "======> Affichage graphique d'objets stockés"
937     OBJET_DE_TEST = Persistence("My object", unit="", basetype=numpy.array)
938     D = OBJET_DE_TEST
939     vect1 = [1, 2, 1, 2, 1]
940     vect2 = [-3, -3, 0, -3, -3]
941     vect3 = [-1, 1, -5, 1, -1]
942     vect4 = 100*[0.29, 0.97, 0.73, 0.01, 0.20]
943     print "Stockage de 3 vecteurs de longueur identique"
944     D.store(vect1)
945     D.store(vect2)
946     D.store(vect3)
947     print "Affichage graphique de l'ensemble du stockage sur une même image"
948     D.stepplot(
949         title = "Tous les vecteurs",
950         filename="vecteurs.ps",
951         xlabel = "Axe X",
952         ylabel = "Axe Y",
953         pause = False )
954     print "Stockage d'un quatrième vecteur de longueur différente"
955     D.store(vect4)
956     print "Affichage graphique séparé du dernier stockage"
957     D.plot(
958         item  = 3,
959         title = "Vecteurs",
960         filename = "vecteur",
961         xlabel = "Axe X",
962         ylabel = "Axe Y",
963         pause = False )
964     print "Les images ont été stockées en fichiers Postscript"
965     print "Taille \"shape\" du dernier objet stocké",OBJET_DE_TEST.shape()
966     print "Taille \"len\" du dernier objet stocké",len(OBJET_DE_TEST)
967     del OBJET_DE_TEST
968     print
969
970     print "======> Affichage graphique dynamique d'objets"
971     OBJET_DE_TEST = Persistence("My object", unit="", basetype=float)
972     D = OBJET_DE_TEST
973     D.plot(
974         dynamic = True,
975         title   = "Valeur suivie",
976         xlabel  = "Pas",
977         ylabel  = "Valeur",
978         pause   = False,
979         )
980     for i in range(1,11):
981         D.store( i*i )
982     print "Taille \"shape\" du dernier objet stocké",OBJET_DE_TEST.shape()
983     print "Taille \"len\" du dernier objet stocké",len(OBJET_DE_TEST)
984     print "Nombre d'objets stockés",OBJET_DE_TEST.stepnumber()
985     del OBJET_DE_TEST
986     print
987
988     print "======> Affectation simple d'observateurs dynamiques"
989     def obs(var=None,info=None):
990         print "  ---> Mise en oeuvre de l'observer"
991         print "       var  =",var.valueserie(-1)
992         print "       info =",info
993     OBJET_DE_TEST = Persistence("My object", unit="", basetype=list)
994     D = OBJET_DE_TEST
995     D.setDataObserver( HookFunction = obs )
996     for i in range(5):
997         # print
998         print "Action de 1 observer sur la variable observée, étape :",i
999         D.store( [i, i, i] )
1000     del OBJET_DE_TEST
1001     print
1002
1003     print "======> Affectation multiple d'observateurs dynamiques"
1004     def obs(var=None,info=None):
1005         print "  ---> Mise en oeuvre de l'observer"
1006         print "       var  =",var.valueserie(-1)
1007         print "       info =",info
1008     OBJET_DE_TEST = Persistence("My object", unit="", basetype=list)
1009     D = OBJET_DE_TEST
1010     D.setDataObserver(
1011         HookFunction   = obs,
1012         Scheduler      = [2, 4],
1013         HookParameters = "Second observer",
1014         )
1015     D.setDataObserver(
1016         HookFunction   = obs,
1017         Scheduler      = xrange(1,3),
1018         HookParameters = "Troisième observer",
1019         )
1020     for i in range(5):
1021         # print
1022         print "Action de 2 observers sur la variable observée, étape :",i
1023         D.store( [i, i, i] )
1024     del OBJET_DE_TEST
1025     print