1 #-*-coding:iso-8859-1-*-
3 # Copyright (C) 2008-2009 EDF R&D
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.
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.
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
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 Définit des outils de persistence et d'enregistrement de séries de valeurs
23 pour analyse ultérieure ou utilisation de calcul.
25 __author__ = "Jean-Philippe ARGAUD - Mars 2008"
29 from PlatformInfo import PathManagement ; PathManagement()
31 # ==============================================================================
34 Classe générale de persistence définissant les accesseurs nécessaires
37 def __init__(self, name="", unit="", basetype=str):
41 basetype : type de base de l'objet stocké à chaque pas
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
52 self.__name = str(name)
53 self.__unit = str(unit)
56 self.__basetype = basetype
61 def basetype(self, basetype=None):
63 Renvoie ou met en place le type de base des objets stockés
66 return self.__basetype
68 self.__basetype = basetype
70 def store(self, value=None, step=None):
72 Stocke une valeur à un pas. Une instanciation est faite avec le type de
73 base pour stocker l'objet. Si le pas n'est pas fournit, on utilise
74 l'étape de stockage comme valeur de pas.
76 if value is None: raise ValueError("Value argument required")
79 self.__steps.append(step)
81 self.__steps.append(self.__step)
83 self.__values.append(self.__basetype(value))
87 Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
88 objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
89 complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
90 longueur. Par défaut, renvoie 1.
92 if len(self.__values) > 0:
93 if self.__basetype in [numpy.matrix, numpy.array]:
94 return self.__values[-1].shape
95 elif self.__basetype in [int, float]:
97 elif self.__basetype in [list, dict]:
98 return (len(self.__values[-1]),)
102 raise ValueError("Object has no shape before its first storage")
106 Renvoie le nombre d'éléments dans un séquence ou la plus grande
107 dimension d'une matrice
109 return max( self.shape() )
111 # ---------------------------------------------------------
112 def stepserie(self, item=None, step=None):
114 Renvoie par défaut toute la liste des pas de temps. Si l'argument "step"
115 existe dans la liste des pas de stockage effectués, renvoie ce pas
116 "step". Si l'argument "item" est correct, renvoie le pas stockée au
119 if step is not None and step in self.__steps:
121 elif item is not None and item < len(self.__steps):
122 return self.__steps[item]
126 def valueserie(self, item=None, step=None):
128 Renvoie par défaut toute la liste des valeurs/objets. Si l'argument
129 "step" existe dans la liste des pas de stockage effectués, renvoie la
130 valeur stockée à ce pas "step". Si l'argument "item" est correct,
131 renvoie la valeur stockée au numéro "item".
133 if step is not None and step in self.__steps:
134 index = self.__steps.index(step)
135 return self.__values[index]
136 elif item is not None and item < len(self.__values):
137 return self.__values[item]
141 def stepnumber(self):
143 Renvoie le nombre de pas de stockage.
145 return len(self.__steps)
147 # ---------------------------------------------------------
150 Renvoie la valeur moyenne des données à chaque pas. Il faut que le type
151 de base soit compatible avec les types élémentaires numpy.
154 return [numpy.matrix(item).mean() for item in self.__values]
156 raise TypeError("Base type is incompatible with numpy")
158 def std(self, ddof=0):
160 Renvoie l'écart-type des données à chaque pas. Il faut que le type de
161 base soit compatible avec les types élémentaires numpy.
163 ddof : c'est le nombre de degrés de liberté pour le calcul de
164 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
167 if numpy.version.version >= '1.1.0':
168 return [numpy.matrix(item).std(ddof=ddof) for item in self.__values]
170 return [numpy.matrix(item).std() for item in self.__values]
172 raise TypeError("Base type is incompatible with numpy")
176 Renvoie la somme des données à chaque pas. Il faut que le type de
177 base soit compatible avec les types élémentaires numpy.
180 return [numpy.matrix(item).sum() for item in self.__values]
182 raise TypeError("Base type is incompatible with numpy")
186 Renvoie le minimum des données à chaque pas. Il faut que le type de
187 base soit compatible avec les types élémentaires numpy.
190 return [numpy.matrix(item).min() for item in self.__values]
192 raise TypeError("Base type is incompatible with numpy")
196 Renvoie le maximum des données à chaque pas. Il faut que le type de
197 base soit compatible avec les types élémentaires numpy.
200 return [numpy.matrix(item).max() for item in self.__values]
202 raise TypeError("Base type is incompatible with numpy")
204 def plot(self, item=None, step=None,
210 geometry = "600x400",
216 Renvoie un affichage de la valeur à chaque pas, si elle est compatible
217 avec un affichage Gnuplot (donc essentiellement un vecteur). Si
218 l'argument "step" existe dans la liste des pas de stockage effectués,
219 renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
220 "item" est correct, renvoie l'affichage de la valeur stockée au numéro
221 "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
222 affichage successif de tous les pas.
225 - step : valeur du pas à afficher
226 - item : index de la valeur à afficher
227 - steps : liste unique des pas de l'axe des X, ou None si c'est
228 la numérotation par défaut
229 - title : base du titre général, qui sera automatiquement
230 complétée par la mention du pas
231 - xlabel : label de l'axe des X
232 - ylabel : label de l'axe des Y
233 - ltitle : titre associé au vecteur tracé
234 - geometry : taille en pixels de la fenêtre et position du coin haut
235 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
236 - filename : base de nom de fichier Postscript pour une sauvegarde,
237 qui est automatiquement complétée par le numéro du
238 fichier calculé par incrément simple de compteur
239 - persist : booléen indiquant que la fenêtre affichée sera
240 conservée lors du passage au dessin suivant
241 Par défaut, persist = False
242 - pause : booléen indiquant une pause après chaque tracé, et
244 Par défaut, pause = True
248 # Vérification de la disponibilité du module Gnuplot
251 self.__gnuplot = Gnuplot
253 raise ImportError("The Gnuplot module is required to plot the object.")
255 # Vérification et compléments sur les paramètres d'entrée
257 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
259 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
262 self.__g = self.__gnuplot.Gnuplot() # persist=1
263 self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
264 self.__g('set style data lines')
266 self.__g('set autoscale')
267 self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
268 self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
270 # Tracé du ou des vecteurs demandés
272 if step is not None and step in self.__steps:
273 indexes.append(self.__steps.index(step))
274 elif item is not None and item < len(self.__values):
277 indexes = indexes + range(len(self.__values))
280 for index in indexes:
281 self.__g('set title "'+str(title).encode('ascii','replace')+' (pas '+str(index)+')"')
282 if ( type(steps) is type([]) ) or ( type(steps) is type(numpy.array([])) ):
285 Steps = range(len(self.__values[index]))
287 self.__g.plot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
291 stepfilename = "%s_%03i.ps"%(filename,i)
292 if os.path.isfile(stepfilename):
293 raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
294 self.__g.hardcopy(filename=stepfilename, color=1)
296 raw_input('Please press return to continue...\n')
298 # ---------------------------------------------------------
301 Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
302 longueur des pas. Il faut que le type de base soit compatible avec
303 les types élémentaires numpy.
306 return numpy.matrix(self.__values).mean()
308 raise TypeError("Base type is incompatible with numpy")
310 def stepstd(self, ddof=0):
312 Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
313 longueur des pas. Il faut que le type de base soit compatible avec
314 les types élémentaires numpy.
316 ddof : c'est le nombre de degrés de liberté pour le calcul de
317 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
320 if numpy.version.version >= '1.1.0':
321 return numpy.matrix(self.__values).std(ddof=ddof)
323 return numpy.matrix(self.__values).std()
325 raise TypeError("Base type is incompatible with numpy")
329 Renvoie la somme de toutes les valeurs sans tenir compte de la
330 longueur des pas. Il faut que le type de base soit compatible avec
331 les types élémentaires numpy.
334 return numpy.matrix(self.__values).sum()
336 raise TypeError("Base type is incompatible with numpy")
340 Renvoie le minimum de toutes les valeurs sans tenir compte de la
341 longueur des pas. Il faut que le type de base soit compatible avec
342 les types élémentaires numpy.
345 return numpy.matrix(self.__values).min()
347 raise TypeError("Base type is incompatible with numpy")
351 Renvoie le maximum de toutes les valeurs sans tenir compte de la
352 longueur des pas. Il faut que le type de base soit compatible avec
353 les types élémentaires numpy.
356 return numpy.matrix(self.__values).max()
358 raise TypeError("Base type is incompatible with numpy")
362 Renvoie la somme cumulée de 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.
367 return numpy.matrix(self.__values).cumsum(axis=0)
369 raise TypeError("Base type is incompatible with numpy")
371 # On pourrait aussi utiliser les autres attributs d'une "matrix", comme
380 geometry = "600x400",
386 Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
387 elles sont compatibles avec un affichage Gnuplot (donc essentiellement
388 un vecteur). Si l'argument "step" existe dans la liste des pas de
389 stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
390 "step". Si l'argument "item" est correct, renvoie l'affichage de la
391 valeur stockée au numéro "item".
394 - steps : liste unique des pas de l'axe des X, ou None si c'est
395 la numérotation par défaut
396 - title : base du titre général, qui sera automatiquement
397 complétée par la mention du pas
398 - xlabel : label de l'axe des X
399 - ylabel : label de l'axe des Y
400 - ltitle : titre associé au vecteur tracé
401 - geometry : taille en pixels de la fenêtre et position du coin haut
402 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
403 - filename : nom de fichier Postscript pour une sauvegarde,
404 - persist : booléen indiquant que la fenêtre affichée sera
405 conservée lors du passage au dessin suivant
406 Par défaut, persist = False
407 - pause : booléen indiquant une pause après chaque tracé, et
409 Par défaut, pause = True
413 # Vérification de la disponibilité du module Gnuplot
416 self.__gnuplot = Gnuplot
418 raise ImportError("The Gnuplot module is required to plot the object.")
420 # Vérification et compléments sur les paramètres d'entrée
422 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
424 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
427 if ( type(steps) is type([]) ) or ( type(steps) is type(numpy.array([])) ):
430 Steps = range(len(self.__values[0]))
431 self.__g = self.__gnuplot.Gnuplot() # persist=1
432 self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
433 self.__g('set style data lines')
435 self.__g('set autoscale')
436 self.__g('set title "'+str(title).encode('ascii','replace') +'"')
437 self.__g('set xlabel "'+str(xlabel).encode('ascii','replace')+'"')
438 self.__g('set ylabel "'+str(ylabel).encode('ascii','replace')+'"')
440 # Tracé du ou des vecteurs demandés
441 indexes = range(len(self.__values))
442 self.__g.plot( self.__gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
443 for index in indexes:
444 self.__g.replot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
447 self.__g.hardcopy(filename=filename, color=1)
449 raw_input('Please press return to continue...\n')
451 # ==============================================================================
452 class OneScalar(Persistence):
454 Classe définissant le stockage d'une valeur unique réelle (float) par pas
456 Le type de base peut être changé par la méthode "basetype", mais il faut que
457 le nouveau type de base soit compatible avec les types par éléments de
458 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
459 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
460 pour conserver une signification claire des noms.
462 def __init__(self, name="", unit="", basetype = float):
463 Persistence.__init__(self, name, unit, basetype)
465 class OneVector(Persistence):
467 Classe définissant le stockage d'une liste (list) de valeurs homogènes par
468 hypothèse par pas. Pour éviter les confusions, ne pas utiliser la classe
469 "OneVector" pour des données hétérogènes, mais bien "OneList".
471 def __init__(self, name="", unit="", basetype = list):
472 Persistence.__init__(self, name, unit, basetype)
474 class OneMatrix(Persistence):
476 Classe définissant le stockage d'une matrice de valeurs (numpy.matrix) par
479 def __init__(self, name="", unit="", basetype = numpy.matrix):
480 Persistence.__init__(self, name, unit, basetype)
482 class OneList(Persistence):
484 Classe définissant le stockage d'une liste de valeurs potentiellement
485 hétérogènes (list) par pas. Pour éviter les confusions, ne pas utiliser la
486 classe "OneVector" pour des données hétérogènes, mais bien "OneList".
488 def __init__(self, name="", unit="", basetype = list):
489 Persistence.__init__(self, name, unit, basetype)
491 # ==============================================================================
492 if __name__ == "__main__":
493 print '\n AUTODIAGNOSTIC \n'
495 print "======> Un flottant"
496 OBJET_DE_TEST = OneScalar("My float", unit="cm")
497 OBJET_DE_TEST.store( 5.)
498 OBJET_DE_TEST.store(-5.)
499 OBJET_DE_TEST.store( 1.)
500 print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
501 print "Les valeurs :", OBJET_DE_TEST.valueserie()
502 print "La 2ème valeur :", OBJET_DE_TEST.valueserie(1)
503 print "La dernière valeur :", OBJET_DE_TEST.valueserie(-1)
504 print "Valeurs par pas :"
505 print " La moyenne :", OBJET_DE_TEST.mean()
506 print " L'écart-type :", OBJET_DE_TEST.std()
507 print " La somme :", OBJET_DE_TEST.sum()
508 print " Le minimum :", OBJET_DE_TEST.min()
509 print " Le maximum :", OBJET_DE_TEST.max()
510 print "Valeurs globales :"
511 print " La moyenne :", OBJET_DE_TEST.stepmean()
512 print " L'écart-type :", OBJET_DE_TEST.stepstd()
513 print " La somme :", OBJET_DE_TEST.stepsum()
514 print " Le minimum :", OBJET_DE_TEST.stepmin()
515 print " Le maximum :", OBJET_DE_TEST.stepmax()
516 print " La somme cumulée :", OBJET_DE_TEST.cumsum()
517 print "Taille \"shape\" :", OBJET_DE_TEST.shape()
518 print "Taille \"len\" :", len(OBJET_DE_TEST)
522 print "======> Un entier"
523 OBJET_DE_TEST = OneScalar("My int", unit="cm", basetype=int)
524 OBJET_DE_TEST.store( 5 )
525 OBJET_DE_TEST.store(-5 )
526 OBJET_DE_TEST.store( 1.)
527 print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
528 print "Les valeurs :", OBJET_DE_TEST.valueserie()
529 print "La 2ème valeur :", OBJET_DE_TEST.valueserie(1)
530 print "La dernière valeur :", OBJET_DE_TEST.valueserie(-1)
531 print "Valeurs par pas :"
532 print " La moyenne :", OBJET_DE_TEST.mean()
533 print " L'écart-type :", OBJET_DE_TEST.std()
534 print " La somme :", OBJET_DE_TEST.sum()
535 print " Le minimum :", OBJET_DE_TEST.min()
536 print " Le maximum :", OBJET_DE_TEST.max()
537 print "Valeurs globales :"
538 print " La moyenne :", OBJET_DE_TEST.stepmean()
539 print " L'écart-type :", OBJET_DE_TEST.stepstd()
540 print " La somme :", OBJET_DE_TEST.stepsum()
541 print " Le minimum :", OBJET_DE_TEST.stepmin()
542 print " Le maximum :", OBJET_DE_TEST.stepmax()
543 print " La somme cumulée :", OBJET_DE_TEST.cumsum()
544 print "Taille \"shape\" :", OBJET_DE_TEST.shape()
545 print "Taille \"len\" :", len(OBJET_DE_TEST)
549 print "======> Un booléen"
550 OBJET_DE_TEST = OneScalar("My bool", unit="", basetype=bool)
551 OBJET_DE_TEST.store( True )
552 OBJET_DE_TEST.store( False )
553 OBJET_DE_TEST.store( True )
554 print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
555 print "Les valeurs :", OBJET_DE_TEST.valueserie()
556 print "La 2ème valeur :", OBJET_DE_TEST.valueserie(1)
557 print "La dernière valeur :", OBJET_DE_TEST.valueserie(-1)
558 print "Taille \"shape\" :", OBJET_DE_TEST.shape()
559 print "Taille \"len\" :", len(OBJET_DE_TEST)
563 print "======> Un vecteur de flottants"
564 OBJET_DE_TEST = OneVector("My float vector", unit="cm")
565 OBJET_DE_TEST.store( (5 , -5) )
566 OBJET_DE_TEST.store( (-5, 5 ) )
567 OBJET_DE_TEST.store( (1., 1.) )
568 print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
569 print "Les valeurs :", OBJET_DE_TEST.valueserie()
570 print "La 2ème valeur :", OBJET_DE_TEST.valueserie(1)
571 print "La dernière valeur :", OBJET_DE_TEST.valueserie(-1)
572 print "Valeurs par pas :"
573 print " La moyenne :", OBJET_DE_TEST.mean()
574 print " L'écart-type :", OBJET_DE_TEST.std()
575 print " La somme :", OBJET_DE_TEST.sum()
576 print " Le minimum :", OBJET_DE_TEST.min()
577 print " Le maximum :", OBJET_DE_TEST.max()
578 print "Valeurs globales :"
579 print " La moyenne :", OBJET_DE_TEST.stepmean()
580 print " L'écart-type :", OBJET_DE_TEST.stepstd()
581 print " La somme :", OBJET_DE_TEST.stepsum()
582 print " Le minimum :", OBJET_DE_TEST.stepmin()
583 print " Le maximum :", OBJET_DE_TEST.stepmax()
584 print " La somme cumulée :", OBJET_DE_TEST.cumsum()
585 print "Taille \"shape\" :", OBJET_DE_TEST.shape()
586 print "Taille \"len\" :", len(OBJET_DE_TEST)
590 print "======> Une liste hétérogène"
591 OBJET_DE_TEST = OneList("My list", unit="bool/cm")
592 OBJET_DE_TEST.store( (True , -5) )
593 OBJET_DE_TEST.store( (False, 5 ) )
594 OBJET_DE_TEST.store( (True , 1.) )
595 print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
596 print "Les valeurs :", OBJET_DE_TEST.valueserie()
597 print "La 2ème valeur :", OBJET_DE_TEST.valueserie(1)
598 print "La dernière valeur :", OBJET_DE_TEST.valueserie(-1)
599 print "Valeurs par pas : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
600 print " La moyenne :", OBJET_DE_TEST.mean()
601 print " L'écart-type :", OBJET_DE_TEST.std()
602 print " La somme :", OBJET_DE_TEST.sum()
603 print " Le minimum :", OBJET_DE_TEST.min()
604 print " Le maximum :", OBJET_DE_TEST.max()
605 print "Valeurs globales : attention, on peut les calculer car True=1, False=0, mais cela n'a pas de sens"
606 print " La moyenne :", OBJET_DE_TEST.stepmean()
607 print " L'écart-type :", OBJET_DE_TEST.stepstd()
608 print " La somme :", OBJET_DE_TEST.stepsum()
609 print " Le minimum :", OBJET_DE_TEST.stepmin()
610 print " Le maximum :", OBJET_DE_TEST.stepmax()
611 print " La somme cumulée :", OBJET_DE_TEST.cumsum()
612 print "Taille \"shape\" :", OBJET_DE_TEST.shape()
613 print "Taille \"len\" :", len(OBJET_DE_TEST)
617 print "======> Utilisation directe de la classe Persistence"
618 OBJET_DE_TEST = Persistence("My object", unit="", basetype=int )
619 OBJET_DE_TEST.store( 1 )
620 OBJET_DE_TEST.store( 3 )
621 OBJET_DE_TEST.store( 7 )
622 print "Les pas de stockage :", OBJET_DE_TEST.stepserie()
623 print "Les valeurs :", OBJET_DE_TEST.valueserie()
624 print "La 2ème valeur :", OBJET_DE_TEST.valueserie(1)
625 print "La dernière valeur :", OBJET_DE_TEST.valueserie(-1)
626 print "Taille \"shape\" :", OBJET_DE_TEST.shape()
627 print "Taille \"len\" :", len(OBJET_DE_TEST)
631 print "======> Affichage d'objets stockés"
632 OBJET_DE_TEST = Persistence("My object", unit="", basetype=numpy.array)
634 vect1 = [1, 2, 1, 2, 1]
635 vect2 = [-3, -3, 0, -3, -3]
636 vect3 = [-1, 1, -5, 1, -1]
637 vect4 = 100*[0.29, 0.97, 0.73, 0.01, 0.20]
638 print "Stockage de 3 vecteurs de longueur identique"
642 print "Affichage de l'ensemble du stockage sur une même image"
644 title = "Tous les vecteurs",
645 filename="vecteurs.ps",
649 print "Stockage d'un quatrième vecteur de longueur différente"
651 print "Affichage séparé du dernier stockage"
655 filename = "vecteur",
659 print "Les images ont été stockées en fichiers Postscript"
660 print "Taille \"shape\" du dernier objet stocké",OBJET_DE_TEST.shape()
661 print "Taille \"len\" du dernier objet stocké",len(OBJET_DE_TEST)