X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FdaComposant%2FdaCore%2FAssimilationStudy.py;h=910e5fd4b7825e67170712f85c5c0221ecc5307a;hb=2201c84e2047882b9bd918b3e62de45711d514d5;hp=f8c7920945adc7de54dcd8da280aba38440effd0;hpb=5ae2f269277f702b0ab031f22477ebabdd6f74d2;p=modules%2Fadao.git diff --git a/src/daComposant/daCore/AssimilationStudy.py b/src/daComposant/daCore/AssimilationStudy.py index f8c7920..910e5fd 100644 --- a/src/daComposant/daCore/AssimilationStudy.py +++ b/src/daComposant/daCore/AssimilationStudy.py @@ -1,6 +1,6 @@ -#-*-coding:iso-8859-1-*- +# -*- coding: utf-8 -*- # -# Copyright (C) 2008-2013 EDF R&D +# Copyright (C) 2008-2021 EDF R&D # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,1064 +20,22 @@ # # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D -__doc__ = """ - Classe principale pour la préparation, la réalisation et la restitution de - calculs d'assimilation de données. - - Ce module est destiné à être appelé par AssimilationStudy pour constituer - les objets élémentaires de l'étude. +""" + Normalized interface for ADAO scripting (full version API) """ __author__ = "Jean-Philippe ARGAUD" +__all__ = ["AssimilationStudy"] -import os, sys -import numpy -import Logging ; Logging.Logging() # A importer en premier -import scipy.optimize -import Persistence -from BasicObjects import Operator -from PlatformInfo import uniq +from daCore.Aidsm import Aidsm as _Aidsm # ============================================================================== -class AssimilationStudy: +class AssimilationStudy(_Aidsm): """ - Cette classe sert d'interface pour l'utilisation de l'assimilation de - données. Elle contient les méthodes ou accesseurs nécessaires à la - construction d'un calcul d'assimilation. + Generic ADAO TUI builder """ - def __init__(self, name=""): - """ - Prévoit de conserver l'ensemble des variables nécssaires à un algorithme - élémentaire. Ces variables sont ensuite disponibles pour implémenter un - algorithme élémentaire particulier. - - Background............: vecteur Xb - Observation...........: vecteur Y (potentiellement temporel) - d'observations - State.................: vecteur d'état dont une partie est le vecteur de - contrôle. Cette information n'est utile que si l'on veut faire des - calculs sur l'état complet, mais elle n'est pas indispensable pour - l'assimilation. - Control...............: vecteur X contenant toutes les variables de - contrôle, i.e. les paramètres ou l'état dont on veut estimer la - valeur pour obtenir les observations - ObservationOperator...: opérateur d'observation H - - Les observations présentent une erreur dont la matrice de covariance est - R. L'ébauche du vecteur de contrôle présente une erreur dont la matrice - de covariance est B. - """ - self.__name = str(name) - self.__Xb = None - self.__Y = None - self.__U = None - self.__B = None - self.__R = None - self.__Q = None - self.__HO = {} - self.__EM = {} - self.__CM = {} - # - self.__X = Persistence.OneVector() - self.__Parameters = {} - self.__StoredDiagnostics = {} - self.__StoredInputs = {} - # - self.__B_scalar = None - self.__R_scalar = None - self.__Q_scalar = None - self.__Parameters["B_scalar"] = None - self.__Parameters["R_scalar"] = None - self.__Parameters["Q_scalar"] = None - # - # Variables temporaires - self.__algorithm = {} - self.__algorithmFile = None - self.__algorithmName = None - self.__diagnosticFile = None - # - # Récupère le chemin du répertoire parent et l'ajoute au path - # (Cela complète l'action de la classe PathManagement dans PlatformInfo, - # qui est activée dans Persistence) - self.__parent = os.path.abspath(os.path.join(os.path.dirname(__file__),"..")) - sys.path.insert(0, self.__parent) - sys.path = uniq( sys.path ) # Conserve en unique exemplaire chaque chemin - - # --------------------------------------------------------- - def setBackground(self, - asVector = None, - asPersistentVector = None, - Scheduler = None, - toBeStored = False, - ): - """ - Permet de définir l'estimation a priori : - - asVector : entrée des données, comme un vecteur compatible avec le - constructeur de numpy.matrix - - asPersistentVector : entrée des données, comme un vecteur de type - persistent contruit avec la classe ad-hoc "Persistence" - - Scheduler est le contrôle temporel des données - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if asVector is not None: - if type( asVector ) is type( numpy.matrix([]) ): - self.__Xb = numpy.matrix( asVector.A1, numpy.float ).T - else: - self.__Xb = numpy.matrix( asVector, numpy.float ).T - elif asPersistentVector is not None: - if type(asPersistentVector) in [type([]),type(()),type(numpy.array([])),type(numpy.matrix([]))]: - self.__Xb = Persistence.OneVector("Background", basetype=numpy.matrix) - for member in asPersistentVector: - self.__Xb.store( numpy.matrix( numpy.asmatrix(member).A1, numpy.float ).T ) - else: - self.__Xb = asPersistentVector - else: - raise ValueError("Error: improperly defined background, it requires at minima either a vector, a list/tuple of vectors or a persistent object") - if toBeStored: - self.__StoredInputs["Background"] = self.__Xb - return 0 - - def setBackgroundError(self, - asCovariance = None, - asEyeByScalar = None, - asEyeByVector = None, - toBeStored = False, - ): - """ - Permet de définir la covariance des erreurs d'ébauche : - - asCovariance : entrée des données, comme une matrice compatible avec - le constructeur de numpy.matrix - - asEyeByScalar : entrée des données comme un seul scalaire de variance, - multiplicatif d'une matrice de corrélation identité, aucune matrice - n'étant donc explicitement à donner - - asEyeByVector : entrée des données comme un seul vecteur de variance, - à mettre sur la diagonale d'une matrice de corrélation, aucune matrice - n'étant donc explicitement à donner - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if asEyeByScalar is not None: - self.__B_scalar = float(asEyeByScalar) - self.__B = None - elif asEyeByVector is not None: - self.__B_scalar = None - self.__B = numpy.matrix( numpy.diagflat( asEyeByVector ), numpy.float ) - elif asCovariance is not None: - self.__B_scalar = None - self.__B = numpy.matrix( asCovariance, numpy.float ) - else: - raise ValueError("Background error covariance matrix has to be specified either as a matrix, a vector for its diagonal or a scalar multiplying an identity matrix") - # - self.__Parameters["B_scalar"] = self.__B_scalar - if toBeStored: - self.__StoredInputs["BackgroundError"] = self.__B - return 0 - - # ----------------------------------------------------------- - def setObservation(self, - asVector = None, - asPersistentVector = None, - Scheduler = None, - toBeStored = False, - ): - """ - Permet de définir les observations : - - asVector : entrée des données, comme un vecteur compatible avec le - constructeur de numpy.matrix - - asPersistentVector : entrée des données, comme un vecteur de type - persistent contruit avec la classe ad-hoc "Persistence" - - Scheduler est le contrôle temporel des données disponibles - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if asVector is not None: - if type( asVector ) is type( numpy.matrix([]) ): - self.__Y = numpy.matrix( asVector.A1, numpy.float ).T - else: - self.__Y = numpy.matrix( asVector, numpy.float ).T - elif asPersistentVector is not None: - if type(asPersistentVector) in [type([]),type(()),type(numpy.array([])),type(numpy.matrix([]))]: - self.__Y = Persistence.OneVector("Observation", basetype=numpy.matrix) - for member in asPersistentVector: - self.__Y.store( numpy.matrix( numpy.asmatrix(member).A1, numpy.float ).T ) - else: - self.__Y = asPersistentVector - else: - raise ValueError("Error: improperly defined observations, it requires at minima either a vector, a list/tuple of vectors or a persistent object") - if toBeStored: - self.__StoredInputs["Observation"] = self.__Y - return 0 - - def setObservationError(self, - asCovariance = None, - asEyeByScalar = None, - asEyeByVector = None, - toBeStored = False, - ): - """ - Permet de définir la covariance des erreurs d'observations : - - asCovariance : entrée des données, comme une matrice compatible avec - le constructeur de numpy.matrix - - asEyeByScalar : entrée des données comme un seul scalaire de variance, - multiplicatif d'une matrice de corrélation identité, aucune matrice - n'étant donc explicitement à donner - - asEyeByVector : entrée des données comme un seul vecteur de variance, - à mettre sur la diagonale d'une matrice de corrélation, aucune matrice - n'étant donc explicitement à donner - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if asEyeByScalar is not None: - self.__R_scalar = float(asEyeByScalar) - self.__R = None - elif asEyeByVector is not None: - self.__R_scalar = None - self.__R = numpy.matrix( numpy.diagflat( asEyeByVector ), numpy.float ) - elif asCovariance is not None: - self.__R_scalar = None - self.__R = numpy.matrix( asCovariance, numpy.float ) - else: - raise ValueError("Observation error covariance matrix has to be specified either as a matrix, a vector for its diagonal or a scalar multiplying an identity matrix") - # - self.__Parameters["R_scalar"] = self.__R_scalar - if toBeStored: - self.__StoredInputs["ObservationError"] = self.__R - return 0 - - def setObservationOperator(self, - asFunction = {"Direct":None, "Tangent":None, "Adjoint":None, - "useApproximatedDerivatives":False, - "withCenteredDF" :False, - "withIncrement" :0.01, - "withdX" :None, - }, - asMatrix = None, - appliedToX = None, - toBeStored = False, - ): - """ - Permet de définir un opérateur d'observation H. L'ordre de priorité des - définitions et leur sens sont les suivants : - - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None - alors on définit l'opérateur à l'aide de fonctions. Si la fonction - "Direct" n'est pas définie, on prend la fonction "Tangent". - Si "useApproximatedDerivatives" est vrai, on utilise une approximation - des opérateurs tangents et adjoints. On utilise par défaut des - différences finies non centrées ou centrées (si "withCenteredDF" est - vrai) avec un incrément multiplicatif "withIncrement" de 1% autour - du point courant ou sur le point fixe "withdX". - - si les fonctions ne sont pas disponibles et si asMatrix n'est pas - None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de - la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La - matrice fournie doit être sous une forme compatible avec le - constructeur de numpy.matrix. - - si l'argument "appliedToX" n'est pas None, alors on définit, pour des - X divers, l'opérateur par sa valeur appliquée à cet X particulier, - sous la forme d'un dictionnaire appliedToX[NAME] avec NAME un nom. - L'opérateur doit néanmoins déjà avoir été défini comme d'habitude. - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if (type(asFunction) is type({})) and \ - asFunction.has_key("useApproximatedDerivatives") and bool(asFunction["useApproximatedDerivatives"]) and \ - asFunction.has_key("Direct") and (asFunction["Direct"] is not None): - if not asFunction.has_key("withCenteredDF"): asFunction["withCenteredDF"] = False - if not asFunction.has_key("withIncrement"): asFunction["withIncrement"] = 0.01 - if not asFunction.has_key("withdX"): asFunction["withdX"] = None - from daNumerics.ApproximatedDerivatives import FDApproximation - FDA = FDApproximation( - Function = asFunction["Direct"], - centeredDF = asFunction["withCenteredDF"], - increment = asFunction["withIncrement"], - dX = asFunction["withdX"] ) - self.__HO["Direct"] = Operator( fromMethod = FDA.DirectOperator ) - self.__HO["Tangent"] = Operator( fromMethod = FDA.TangentOperator ) - self.__HO["Adjoint"] = Operator( fromMethod = FDA.AdjointOperator ) - elif (type(asFunction) is type({})) and \ - asFunction.has_key("Tangent") and asFunction.has_key("Adjoint") and \ - (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None): - if not asFunction.has_key("Direct") or (asFunction["Direct"] is None): - self.__HO["Direct"] = Operator( fromMethod = asFunction["Tangent"] ) - else: - self.__HO["Direct"] = Operator( fromMethod = asFunction["Direct"] ) - self.__HO["Tangent"] = Operator( fromMethod = asFunction["Tangent"] ) - self.__HO["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] ) - elif asMatrix is not None: - matrice = numpy.matrix( asMatrix, numpy.float ) - self.__HO["Direct"] = Operator( fromMatrix = matrice ) - self.__HO["Tangent"] = Operator( fromMatrix = matrice ) - self.__HO["Adjoint"] = Operator( fromMatrix = matrice.T ) - del matrice - else: - raise ValueError("Improperly defined observation operator, it requires at minima either a matrix, a Direct for approximate derivatives or a Tangent/Adjoint pair.") - # - if appliedToX is not None: - self.__HO["AppliedToX"] = {} - if type(appliedToX) is not dict: - raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.") - for key in appliedToX.keys(): - if type( appliedToX[key] ) is type( numpy.matrix([]) ): - # Pour le cas où l'on a une vraie matrice - self.__HO["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T - elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1: - # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions - self.__HO["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T - else: - self.__HO["AppliedToX"][key] = numpy.matrix( appliedToX[key], numpy.float ).T - else: - self.__HO["AppliedToX"] = None - # - if toBeStored: - self.__StoredInputs["ObservationOperator"] = self.__HO - return 0 - - # ----------------------------------------------------------- - def setEvolutionModel(self, - asFunction = {"Direct":None, "Tangent":None, "Adjoint":None, - "useApproximatedDerivatives":False, - "withCenteredDF" :False, - "withIncrement" :0.01, - "withdX" :None, - }, - asMatrix = None, - Scheduler = None, - toBeStored = False, - ): - """ - Permet de définir un opérateur d'évolution M. L'ordre de priorité des - définitions et leur sens sont les suivants : - - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None - alors on définit l'opérateur à l'aide de fonctions. Si la fonction - "Direct" n'est pas définie, on prend la fonction "Tangent". - Si "useApproximatedDerivatives" est vrai, on utilise une approximation - des opérateurs tangents et adjoints. On utilise par défaut des - différences finies non centrées ou centrées (si "withCenteredDF" est - vrai) avec un incrément multiplicatif "withIncrement" de 1% autour - du point courant ou sur le point fixe "withdX". - - si les fonctions ne sont pas disponibles et si asMatrix n'est pas - None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de - la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La - matrice fournie doit être sous une forme compatible avec le - constructeur de numpy.matrix. - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if (type(asFunction) is type({})) and \ - asFunction.has_key("useApproximatedDerivatives") and bool(asFunction["useApproximatedDerivatives"]) and \ - asFunction.has_key("Direct") and (asFunction["Direct"] is not None): - if not asFunction.has_key("withCenteredDF"): asFunction["withCenteredDF"] = False - if not asFunction.has_key("withIncrement"): asFunction["withIncrement"] = 0.01 - if not asFunction.has_key("withdX"): asFunction["withdX"] = None - from daNumerics.ApproximatedDerivatives import FDApproximation - FDA = FDApproximation( - Function = asFunction["Direct"], - centeredDF = asFunction["withCenteredDF"], - increment = asFunction["withIncrement"], - dX = asFunction["withdX"] ) - self.__EM["Direct"] = Operator( fromMethod = FDA.DirectOperator ) - self.__EM["Tangent"] = Operator( fromMethod = FDA.TangentOperator ) - self.__EM["Adjoint"] = Operator( fromMethod = FDA.AdjointOperator ) - elif (type(asFunction) is type({})) and \ - asFunction.has_key("Tangent") and asFunction.has_key("Adjoint") and \ - (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None): - if not asFunction.has_key("Direct") or (asFunction["Direct"] is None): - self.__EM["Direct"] = Operator( fromMethod = asFunction["Tangent"] ) - else: - self.__EM["Direct"] = Operator( fromMethod = asFunction["Direct"] ) - self.__EM["Tangent"] = Operator( fromMethod = asFunction["Tangent"] ) - self.__EM["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] ) - elif asMatrix is not None: - matrice = numpy.matrix( asMatrix, numpy.float ) - self.__EM["Direct"] = Operator( fromMatrix = matrice ) - self.__EM["Tangent"] = Operator( fromMatrix = matrice ) - self.__EM["Adjoint"] = Operator( fromMatrix = matrice.T ) - del matrice - else: - raise ValueError("Improperly defined evolution model, it requires at minima either a matrix, a Direct for approximate derivatives or a Tangent/Adjoint pair.") - # - if toBeStored: - self.__StoredInputs["EvolutionModel"] = self.__EM - return 0 - - def setEvolutionError(self, - asCovariance = None, - asEyeByScalar = None, - asEyeByVector = None, - toBeStored = False, - ): - """ - Permet de définir la covariance des erreurs de modèle : - - asCovariance : entrée des données, comme une matrice compatible avec - le constructeur de numpy.matrix - - asEyeByScalar : entrée des données comme un seul scalaire de variance, - multiplicatif d'une matrice de corrélation identité, aucune matrice - n'étant donc explicitement à donner - - asEyeByVector : entrée des données comme un seul vecteur de variance, - à mettre sur la diagonale d'une matrice de corrélation, aucune matrice - n'étant donc explicitement à donner - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if asEyeByScalar is not None: - self.__Q_scalar = float(asEyeByScalar) - self.__Q = None - elif asEyeByVector is not None: - self.__Q_scalar = None - self.__Q = numpy.matrix( numpy.diagflat( asEyeByVector ), numpy.float ) - elif asCovariance is not None: - self.__Q_scalar = None - self.__Q = numpy.matrix( asCovariance, numpy.float ) - else: - raise ValueError("Evolution error covariance matrix has to be specified either as a matrix, a vector for its diagonal or a scalar multiplying an identity matrix") - # - self.__Parameters["Q_scalar"] = self.__Q_scalar - if toBeStored: - self.__StoredInputs["EvolutionError"] = self.__Q - return 0 - - # ----------------------------------------------------------- - def setControlModel(self, - asFunction = {"Direct":None, "Tangent":None, "Adjoint":None, - "useApproximatedDerivatives":False, - "withCenteredDF" :False, - "withIncrement" :0.01, - "withdX" :None, - }, - asMatrix = None, - Scheduler = None, - toBeStored = False, - ): - """ - Permet de définir un opérateur de controle C. L'ordre de priorité des - définitions et leur sens sont les suivants : - - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None - alors on définit l'opérateur à l'aide de fonctions. Si la fonction - "Direct" n'est pas définie, on prend la fonction "Tangent". - Si "useApproximatedDerivatives" est vrai, on utilise une approximation - des opérateurs tangents et adjoints. On utilise par défaut des - différences finies non centrées ou centrées (si "withCenteredDF" est - vrai) avec un incrément multiplicatif "withIncrement" de 1% autour - du point courant ou sur le point fixe "withdX". - - si les fonctions ne sont pas disponibles et si asMatrix n'est pas - None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de - la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La - matrice fournie doit être sous une forme compatible avec le - constructeur de numpy.matrix. - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if (type(asFunction) is type({})) and \ - asFunction.has_key("useApproximatedDerivatives") and bool(asFunction["useApproximatedDerivatives"]) and \ - asFunction.has_key("Direct") and (asFunction["Direct"] is not None): - if not asFunction.has_key("withCenteredDF"): asFunction["withCenteredDF"] = False - if not asFunction.has_key("withIncrement"): asFunction["withIncrement"] = 0.01 - if not asFunction.has_key("withdX"): asFunction["withdX"] = None - from daNumerics.ApproximatedDerivatives import FDApproximation - FDA = FDApproximation( - Function = asFunction["Direct"], - centeredDF = asFunction["withCenteredDF"], - increment = asFunction["withIncrement"], - dX = asFunction["withdX"] ) - self.__CM["Direct"] = Operator( fromMethod = FDA.DirectOperator ) - self.__CM["Tangent"] = Operator( fromMethod = FDA.TangentOperator ) - self.__CM["Adjoint"] = Operator( fromMethod = FDA.AdjointOperator ) - elif (type(asFunction) is type({})) and \ - asFunction.has_key("Tangent") and asFunction.has_key("Adjoint") and \ - (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None): - if not asFunction.has_key("Direct") or (asFunction["Direct"] is None): - self.__CM["Direct"] = Operator( fromMethod = asFunction["Tangent"] ) - else: - self.__CM["Direct"] = Operator( fromMethod = asFunction["Direct"] ) - self.__CM["Tangent"] = Operator( fromMethod = asFunction["Tangent"] ) - self.__CM["Adjoint"] = Operator( fromMethod = asFunction["Adjoint"] ) - elif asMatrix is not None: - matrice = numpy.matrix( asMatrix, numpy.float ) - self.__CM["Direct"] = Operator( fromMatrix = matrice ) - self.__CM["Tangent"] = Operator( fromMatrix = matrice ) - self.__CM["Adjoint"] = Operator( fromMatrix = matrice.T ) - del matrice - else: - raise ValueError("Improperly defined input control model, it requires at minima either a matrix, a Direct for approximate derivatives or a Tangent/Adjoint pair.") - # - if toBeStored: - self.__StoredInputs["ControlModel"] = self.__CM - return 0 - - def setControlInput(self, - asVector = None, - asPersistentVector = None, - Scheduler = None, - toBeStored = False, - ): - """ - Permet de définir le controle en entree : - - asVector : entrée des données, comme un vecteur compatible avec le - constructeur de numpy.matrix - - asPersistentVector : entrée des données, comme un vecteur de type - persistent contruit avec la classe ad-hoc "Persistence" - - Scheduler est le contrôle temporel des données disponibles - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if asVector is not None: - if isinstance(asVector,numpy.matrix): - self.__U = numpy.matrix( asVector.A1, numpy.float ).T - else: - self.__U = numpy.matrix( asVector, numpy.float ).T - elif asPersistentVector is not None: - if type(asPersistentVector) in [type([]),type(()),type(numpy.array([])),type(numpy.matrix([]))]: - self.__U = Persistence.OneVector("ControlInput", basetype=numpy.matrix) - for member in asPersistentVector: - self.__U.store( numpy.matrix( numpy.asmatrix(member).A1, numpy.float ).T ) - else: - self.__U = asPersistentVector - else: - raise ValueError("Error: improperly defined control input, it requires at minima either a vector, a list/tuple of vectors or a persistent object") - if toBeStored: - self.__StoredInputs["ControlInput"] = self.__U - return 0 - - # ----------------------------------------------------------- - def setControls (self, - asVector = None, - toBeStored = False, - ): - """ - Permet de définir la valeur initiale du vecteur X contenant toutes les - variables de contrôle, i.e. les paramètres ou l'état dont on veut - estimer la valeur pour obtenir les observations. C'est utile pour un - algorithme itératif/incrémental. - - asVector : entrée des données, comme un vecteur compatible avec le - constructeur de numpy.matrix. - - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour - être rendue disponible au même titre que les variables de calcul - """ - if asVector is not None: - self.__X.store( asVector ) - if toBeStored: - self.__StoredInputs["Controls"] = self.__X - return 0 - - # ----------------------------------------------------------- - def setAlgorithm(self, choice = None ): - """ - Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude - d'assimilation. L'argument est un champ caractère se rapportant au nom - d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération - d'assimilation sur les arguments fixes. - """ - if choice is None: - raise ValueError("Error: algorithm choice has to be given") - if self.__algorithmName is not None: - raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName) - daDirectory = "daAlgorithms" - # - # Recherche explicitement le fichier complet - # ------------------------------------------ - module_path = None - for directory in sys.path: - if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')): - module_path = os.path.abspath(os.path.join(directory, daDirectory)) - if module_path is None: - raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path)) - # - # Importe le fichier complet comme un module - # ------------------------------------------ - try: - sys_path_tmp = sys.path ; sys.path.insert(0,module_path) - self.__algorithmFile = __import__(str(choice), globals(), locals(), []) - self.__algorithmName = str(choice) - sys.path = sys_path_tmp ; del sys_path_tmp - except ImportError, e: - raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e)) - # - # Instancie un objet du type élémentaire du fichier - # ------------------------------------------------- - self.__algorithm = self.__algorithmFile.ElementaryAlgorithm() - self.__StoredInputs["AlgorithmName"] = self.__algorithmName - return 0 - - def setAlgorithmParameters(self, asDico=None): - """ - Permet de définir les paramètres de l'algorithme, sous la forme d'un - dictionnaire. - """ - if asDico is not None: - self.__Parameters.update( dict( asDico ) ) - # - self.__StoredInputs["AlgorithmParameters"] = self.__Parameters - return 0 - - def getAlgorithmParameters(self, noDetails=True): - """ - Renvoie la liste des paramètres requis selon l'algorithme - """ - return self.__algorithm.getRequiredParameters(noDetails) - - # ----------------------------------------------------------- - def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ): - """ - Permet de sélectionner un diagnostic a effectuer. - """ - if choice is None: - raise ValueError("Error: diagnostic choice has to be given") - daDirectory = "daDiagnostics" - # - # Recherche explicitement le fichier complet - # ------------------------------------------ - module_path = None - for directory in sys.path: - if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')): - module_path = os.path.abspath(os.path.join(directory, daDirectory)) - if module_path is None: - raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n The search path is %s"%(choice, daDirectory, sys.path)) - # - # Importe le fichier complet comme un module - # ------------------------------------------ - try: - sys_path_tmp = sys.path ; sys.path.insert(0,module_path) - self.__diagnosticFile = __import__(str(choice), globals(), locals(), []) - sys.path = sys_path_tmp ; del sys_path_tmp - except ImportError, e: - raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e)) - # - # Instancie un objet du type élémentaire du fichier - # ------------------------------------------------- - if self.__StoredInputs.has_key(name): - raise ValueError("A default input with the same name \"%s\" already exists."%str(name)) - elif self.__StoredDiagnostics.has_key(name): - raise ValueError("A diagnostic with the same name \"%s\" already exists."%str(name)) - else: - self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic( - name = name, - unit = unit, - basetype = basetype, - parameters = parameters ) - return 0 - - # ----------------------------------------------------------- - def shape_validate(self): - """ - Validation de la correspondance correcte des tailles des variables et - des matrices s'il y en a. - """ - if self.__Xb is None: __Xb_shape = (0,) - elif hasattr(self.__Xb,"shape"): - if type(self.__Xb.shape) is tuple: __Xb_shape = self.__Xb.shape - else: __Xb_shape = self.__Xb.shape() - else: raise TypeError("Xb has no attribute of shape: problem !") - # - if self.__Y is None: __Y_shape = (0,) - elif hasattr(self.__Y,"shape"): - if type(self.__Y.shape) is tuple: __Y_shape = self.__Y.shape - else: __Y_shape = self.__Y.shape() - else: raise TypeError("Y has no attribute of shape: problem !") - # - if self.__U is None: __U_shape = (0,) - elif hasattr(self.__U,"shape"): - if type(self.__U.shape) is tuple: __U_shape = self.__U.shape - else: __U_shape = self.__U.shape() - else: raise TypeError("U has no attribute of shape: problem !") - # - if self.__B is None and self.__B_scalar is None: - __B_shape = (0,0) - elif self.__B is None and self.__B_scalar is not None: - __B_shape = (max(__Xb_shape),max(__Xb_shape)) - elif hasattr(self.__B,"shape"): - if type(self.__B.shape) is tuple: __B_shape = self.__B.shape - else: __B_shape = self.__B.shape() - else: raise TypeError("B has no attribute of shape: problem !") - # - if self.__R is None and self.__R_scalar is None: - __R_shape = (0,0) - elif self.__R is None and self.__R_scalar is not None: - __R_shape = (max(__Y_shape),max(__Y_shape)) - elif hasattr(self.__R,"shape"): - if type(self.__R.shape) is tuple: __R_shape = self.__R.shape - else: __R_shape = self.__R.shape() - else: raise TypeError("R has no attribute of shape: problem !") - # - if self.__Q is None: __Q_shape = (0,0) - elif hasattr(self.__Q,"shape"): - if type(self.__Q.shape) is tuple: __Q_shape = self.__Q.shape - else: __Q_shape = self.__Q.shape() - else: raise TypeError("Q has no attribute of shape: problem !") - # - if len(self.__HO) == 0: __HO_shape = (0,0) - elif type(self.__HO) is type({}): __HO_shape = (0,0) - elif hasattr(self.__HO["Direct"],"shape"): - if type(self.__HO["Direct"].shape) is tuple: __HO_shape = self.__HO["Direct"].shape - else: __HO_shape = self.__HO["Direct"].shape() - else: raise TypeError("H has no attribute of shape: problem !") - # - if len(self.__EM) == 0: __EM_shape = (0,0) - elif type(self.__EM) is type({}): __EM_shape = (0,0) - elif hasattr(self.__EM["Direct"],"shape"): - if type(self.__EM["Direct"].shape) is tuple: __EM_shape = self.__EM["Direct"].shape - else: __EM_shape = self.__EM["Direct"].shape() - else: raise TypeError("EM has no attribute of shape: problem !") - # - if len(self.__CM) == 0: __CM_shape = (0,0) - elif type(self.__CM) is type({}): __CM_shape = (0,0) - elif hasattr(self.__CM["Direct"],"shape"): - if type(self.__CM["Direct"].shape) is tuple: __CM_shape = self.__CM["Direct"].shape - else: __CM_shape = self.__CM["Direct"].shape() - else: raise TypeError("CM has no attribute of shape: problem !") - # - # Vérification des conditions - # --------------------------- - if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ): - raise ValueError("Shape characteristic of Xb is incorrect: \"%s\""%(__Xb_shape,)) - if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ): - raise ValueError("Shape characteristic of Y is incorrect: \"%s\""%(__Y_shape,)) - # - if not( min(__B_shape) == max(__B_shape) ): - raise ValueError("Shape characteristic of B is incorrect: \"%s\""%(__B_shape,)) - if not( min(__R_shape) == max(__R_shape) ): - raise ValueError("Shape characteristic of R is incorrect: \"%s\""%(__R_shape,)) - if not( min(__Q_shape) == max(__Q_shape) ): - raise ValueError("Shape characteristic of Q is incorrect: \"%s\""%(__Q_shape,)) - if not( min(__EM_shape) == max(__EM_shape) ): - raise ValueError("Shape characteristic of EM is incorrect: \"%s\""%(__EM_shape,)) - # - if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and not( __HO_shape[1] == max(__Xb_shape) ): - raise ValueError("Shape characteristic of H \"%s\" and X \"%s\" are incompatible"%(__HO_shape,__Xb_shape)) - if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and not( __HO_shape[0] == max(__Y_shape) ): - raise ValueError("Shape characteristic of H \"%s\" and Y \"%s\" are incompatible"%(__HO_shape,__Y_shape)) - if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and len(self.__B) > 0 and not( __HO_shape[1] == __B_shape[0] ): - raise ValueError("Shape characteristic of H \"%s\" and B \"%s\" are incompatible"%(__HO_shape,__B_shape)) - if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and len(self.__R) > 0 and not( __HO_shape[0] == __R_shape[1] ): - raise ValueError("Shape characteristic of H \"%s\" and R \"%s\" are incompatible"%(__HO_shape,__R_shape)) - # - if self.__B is not None and len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ): - if self.__StoredInputs["AlgorithmName"] in ["EnsembleBlue",]: - asPersistentVector = self.__Xb.reshape((-1,min(__B_shape))) - self.__Xb = Persistence.OneVector("Background", basetype=numpy.matrix) - for member in asPersistentVector: - self.__Xb.store( numpy.matrix( numpy.ravel(member), numpy.float ).T ) - __Xb_shape = min(__B_shape) - else: - raise ValueError("Shape characteristic of B \"%s\" and Xb \"%s\" are incompatible"%(__B_shape,__Xb_shape)) - # - if self.__R is not None and len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ): - raise ValueError("Shape characteristic of R \"%s\" and Y \"%s\" are incompatible"%(__R_shape,__Y_shape)) - # - if self.__EM is not None and len(self.__EM) > 0 and not(type(self.__EM) is type({})) and not( __EM_shape[1] == max(__Xb_shape) ): - raise ValueError("Shape characteristic of EM \"%s\" and X \"%s\" are incompatible"%(__EM_shape,__Xb_shape)) - # - if self.__CM is not None and len(self.__CM) > 0 and not(type(self.__CM) is type({})) and not( __CM_shape[1] == max(__U_shape) ): - raise ValueError("Shape characteristic of CM \"%s\" and U \"%s\" are incompatible"%(__CM_shape,__U_shape)) - # - return 1 - - # ----------------------------------------------------------- - def analyze(self): - """ - Permet de lancer le calcul d'assimilation. - - Le nom de la méthode à activer est toujours "run". Les paramètres en - arguments de la méthode sont fixés. En sortie, on obtient les résultats - dans la variable de type dictionnaire "StoredVariables", qui contient en - particulier des objets de Persistence pour les analyses, OMA... - """ - self.shape_validate() - # - self.__algorithm.run( - Xb = self.__Xb, - Y = self.__Y, - U = self.__U, - HO = self.__HO, - EM = self.__EM, - CM = self.__CM, - R = self.__R, - B = self.__B, - Q = self.__Q, - Parameters = self.__Parameters, - ) - return 0 - - # ----------------------------------------------------------- - def get(self, key=None): - """ - Renvoie les résultats disponibles après l'exécution de la méthode - d'assimilation, ou les diagnostics disponibles. Attention, quand un - diagnostic porte le même nom qu'une variable stockée, c'est la variable - stockée qui est renvoyée, et le diagnostic est inatteignable. - """ - if key is not None: - if self.__algorithm.has_key(key): - return self.__algorithm.get( key ) - elif self.__StoredInputs.has_key(key): - return self.__StoredInputs[key] - elif self.__StoredDiagnostics.has_key(key): - return self.__StoredDiagnostics[key] - else: - raise ValueError("The requested key \"%s\" does not exists as an input, a diagnostic or a stored variable."%key) - else: - allvariables = self.__algorithm.get() - allvariables.update( self.__StoredDiagnostics ) - allvariables.update( self.__StoredInputs ) - return allvariables - - def get_available_variables(self): - """ - Renvoie les variables potentiellement utilisables pour l'étude, - initialement stockées comme données d'entrées ou dans les algorithmes, - identifiés par les chaînes de caractères. L'algorithme doit avoir été - préalablement choisi sinon la méthode renvoie "None". - """ - if len( self.__algorithm.keys()) == 0 and len( self.__StoredInputs.keys() ) == 0: - return None - else: - variables = [] - if len( self.__algorithm.keys()) > 0: - variables.extend( self.__algorithm.get().keys() ) - if len( self.__StoredInputs.keys() ) > 0: - variables.extend( self.__StoredInputs.keys() ) - variables.sort() - return variables - - def get_available_algorithms(self): - """ - Renvoie la liste des algorithmes potentiellement utilisables, identifiés - par les chaînes de caractères. - """ - files = [] - for directory in sys.path: - if os.path.isdir(os.path.join(directory,"daAlgorithms")): - for fname in os.listdir(os.path.join(directory,"daAlgorithms")): - root, ext = os.path.splitext(fname) - if ext == '.py' and root != '__init__': - files.append(root) - files.sort() - return files - - def get_available_diagnostics(self): - """ - Renvoie la liste des diagnostics potentiellement utilisables, identifiés - par les chaînes de caractères. - """ - files = [] - for directory in sys.path: - if os.path.isdir(os.path.join(directory,"daDiagnostics")): - for fname in os.listdir(os.path.join(directory,"daDiagnostics")): - root, ext = os.path.splitext(fname) - if ext == '.py' and root != '__init__': - files.append(root) - files.sort() - return files - - # ----------------------------------------------------------- - def get_algorithms_main_path(self): - """ - Renvoie le chemin pour le répertoire principal contenant les algorithmes - dans un sous-répertoire "daAlgorithms" - """ - return self.__parent - - def add_algorithms_path(self, asPath=None): - """ - Ajoute au chemin de recherche des algorithmes un répertoire dans lequel - se trouve un sous-répertoire "daAlgorithms" - - Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est - pas indispensable de le rajouter ici. - """ - if not os.path.isdir(asPath): - raise ValueError("The given "+asPath+" argument must exist as a directory") - if not os.path.isdir(os.path.join(asPath,"daAlgorithms")): - raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"") - if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")): - raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"") - sys.path.insert(0, os.path.abspath(asPath)) - sys.path = uniq( sys.path ) # Conserve en unique exemplaire chaque chemin - return 1 - - def get_diagnostics_main_path(self): - """ - Renvoie le chemin pour le répertoire principal contenant les diagnostics - dans un sous-répertoire "daDiagnostics" - """ - return self.__parent - - def add_diagnostics_path(self, asPath=None): - """ - Ajoute au chemin de recherche des algorithmes un répertoire dans lequel - se trouve un sous-répertoire "daDiagnostics" - - Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est - pas indispensable de le rajouter ici. - """ - if not os.path.isdir(asPath): - raise ValueError("The given "+asPath+" argument must exist as a directory") - if not os.path.isdir(os.path.join(asPath,"daDiagnostics")): - raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"") - if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")): - raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"") - sys.path.insert(0, os.path.abspath(asPath)) - sys.path = uniq( sys.path ) # Conserve en unique exemplaire chaque chemin - return 1 - - # ----------------------------------------------------------- - def setDataObserver(self, - VariableName = None, - HookFunction = None, - HookParameters = None, - Scheduler = None, - ): - """ - Permet d'associer un observer à une ou des variables nommées gérées en - interne, activable selon des règles définies dans le Scheduler. A chaque - pas demandé dans le Scheduler, il effectue la fonction HookFunction avec - les arguments (variable persistante VariableName, paramètres HookParameters). - """ - # - if type( self.__algorithm ) is dict: - raise ValueError("No observer can be build before choosing an algorithm.") - # - # Vérification du nom de variable et typage - # ----------------------------------------- - if type( VariableName ) is str: - VariableNames = [VariableName,] - elif type( VariableName ) is list: - VariableNames = map( str, VariableName ) - else: - raise ValueError("The observer requires a name or a list of names of variables.") - # - # Association interne de l'observer à la variable - # ----------------------------------------------- - for n in VariableNames: - if not self.__algorithm.has_key( n ): - raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n) - else: - self.__algorithm.StoredVariables[ n ].setDataObserver( - Scheduler = Scheduler, - HookFunction = HookFunction, - HookParameters = HookParameters, - ) - - def removeDataObserver(self, - VariableName = None, - HookFunction = None, - ): - """ - Permet de retirer un observer à une ou des variable nommée. - """ - # - if type( self.__algorithm ) is dict: - raise ValueError("No observer can be removed before choosing an algorithm.") - # - # Vérification du nom de variable et typage - # ----------------------------------------- - if type( VariableName ) is str: - VariableNames = [VariableName,] - elif type( VariableName ) is list: - VariableNames = map( str, VariableName ) - else: - raise ValueError("The observer requires a name or a list of names of variables.") - # - # Association interne de l'observer à la variable - # ----------------------------------------------- - for n in VariableNames: - if not self.__algorithm.has_key( n ): - raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%n) - else: - self.__algorithm.StoredVariables[ n ].removeDataObserver( - HookFunction = HookFunction, - ) - - # ----------------------------------------------------------- - def setDebug(self, level=10): - """ - Utiliser par exemple "import logging ; level = logging.DEBUG" avant cet - appel pour changer le niveau de verbosité, avec : - NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50 - """ - import logging - log = logging.getLogger() - log.setLevel( level ) - - def unsetDebug(self): - """ - Remet le logger au niveau par défaut - """ - import logging - log = logging.getLogger() - log.setLevel( logging.WARNING ) - - def prepare_to_pickle(self): - self.__algorithmFile = None - self.__diagnosticFile = None - self.__HO = {} - self.__EM = {} - self.__CM = {} + def __init__(self, name = ""): + _Aidsm.__init__(self, name) # ============================================================================== if __name__ == "__main__": - print '\n AUTODIAGNOSTIC \n' - - ADD = AssimilationStudy("Ma premiere etude BLUE") - - ADD.setBackground (asVector = [0, 1, 2]) - ADD.setBackgroundError (asCovariance = "1 0 0;0 1 0;0 0 1") - ADD.setObservation (asVector = [0.5, 1.5, 2.5]) - ADD.setObservationError (asCovariance = "1 0 0;0 1 0;0 0 1") - ADD.setObservationOperator(asMatrix = "1 0 0;0 1 0;0 0 1") - - ADD.setAlgorithm(choice="Blue") - - ADD.analyze() - - print "Nombre d'analyses :", ADD.get("Analysis").stepnumber() - print "Ebauche :", [0, 1, 2] - print "Observation :", [0.5, 1.5, 2.5] - print "Demi-somme :", list((numpy.array([0, 1, 2])+numpy.array([0.5, 1.5, 2.5]))/2) - print " qui doit être identique à :" - print "Analyse résultante :", ADD.get("Analysis")[0] - print - - print "Algorithmes disponibles.......................:", ADD.get_available_algorithms() - # print " Chemin des algorithmes.....................:", ADD.get_algorithms_main_path() - print "Diagnostics types disponibles.................:", ADD.get_available_diagnostics() - # print " Chemin des diagnostics.....................:", ADD.get_diagnostics_main_path() - print "Variables disponibles.........................:", ADD.get_available_variables() - print - - print "Paramètres requis par algorithme :" - for algo in ADD.get_available_algorithms(): - tmpADD = AssimilationStudy("Un algorithme") - tmpADD.setAlgorithm(choice=algo) - print " %25s : %s"%(algo,tmpADD.getAlgorithmParameters()) - del tmpADD - print - - ADD.setDiagnostic("RMS", "Ma RMS") - - liste = ADD.get().keys() - liste.sort() - print "Variables et diagnostics nommés disponibles...:", liste - - print - print "Exemple de mise en place d'un observeur :" - def obs(var=None,info=None): - print " ---> Mise en oeuvre de l'observer" - print " var =",var[-1] - print " info =",info - ADD.setDataObserver( 'Analysis', HookFunction=obs, Scheduler = [2, 4], HookParameters = "Second observer") - # Attention, il faut décaler le stockage de 1 pour suivre le pas interne - # car le pas 0 correspond à l'analyse ci-dessus. - for i in range(1,6): - print - print "Action sur la variable observée, étape :",i - ADD.get('Analysis').store( [i, i, i] ) - print - - print "Mise en debug et hors debug" - print "Nombre d'analyses :", ADD.get("Analysis").stepnumber() - ADD.setDebug() - ADD.analyze() - ADD.unsetDebug() - print "Nombre d'analyses :", ADD.get("Analysis").stepnumber() - ADD.analyze() - print "Nombre d'analyses :", ADD.get("Analysis").stepnumber() - print + print('\n AUTODIAGNOSTIC\n')